Webサーバへのアタックが多くなってきたのでWAF(naxsi)を導入してみた。

自宅Webサーバのログを確認しているとインジェクション攻撃など、サーバへのアタックが多くなっているようです。そこでNGINX用のWAF(naxsi)を導入することにしました。当初、ModSecurityも検討しましたが、今回はWebサーバの規模も小さくシンプルで自分で一つ一つルール設定できそうなnaxsiを選択することにしました。

前回のブログから1年以上経ってしまいました。この1年でネットワークやコンテナの構成も更新もしましたので、今は以下のような構成となっています。 ネットワーク、サーバ構成

ネットワークの構成は、フロントにTL-SG105Eを置いてその配下にIPv6(IPoE)接続は、WSR-3200AX4S、IVP4固定アドレス用(PPPoE)にEdgerouterXを配置。色々構成変えて試しましたが、今は、この構成で落ち着きました。またネットワークトラフィック監視用にRaspberry Piを配置しました。ネットワーク機器のログはrsyslogコンテナに集約し、prometheus,victoria,loki経由grafanaでモニタリングしています。この辺の備忘録はそのうち整理したいと思います。そう思って半年以上放置状態でそろそろ設定した内容忘れそうです。

naxsiのロードモジュール作成

nginxバージョンに合わせてnaxsiのロードモジュール作成したいと思います。当初は、モジュールのみコンパイルして適用してみましたが、実行時にcore dumpを吐いていたので、原因追求はしないで、nginx本体もコンパイルすることにしました。 16行以降の.configureのパラメータは、稼働中のnginx環境からパラメータを取り出して利用しました。47行以下が本番用のImageファイルになります。 コンパイルした結果をalpine+nginxに上書きしています。

Dockerfile

環境は、alpine+nginx+naxsiをDocker(Podman)で、nginx-1.20.2、openssl-3.0、naxsi1.3をコンパイルします。

Dockerfile
FROM alpine AS builder

LABEL version="1.0" "akiboi.net"="akiboI"
RUN apk add --update --no-cache git libtool autoconf automake make g++ pcre-dev zlib-dev linux-headers nginx
COPY build/ /etc/nginx/
WORKDIR /src
RUN wget http://nginx.org/download/nginx-1.20.2.tar.gz
RUN tar zxvf nginx-1.20.2.tar.gz
RUN git clone -b openssl-3.0 https://github.com/openssl/openssl openssl-3.0
RUN git clone https://github.com/nbs-system/naxsi.git
RUN cp /src/naxsi/naxsi_config/naxsi_core.rules /etc/nginx && \
  touch /var/log/naxsi.log && \
  chown nginx /var/log/naxsi.log
WORKDIR /src/nginx-1.20.2
RUN ./configure \
  --prefix=/usr/share/nginx \
  --conf-path=/etc/nginx/nginx.conf \
  --http-log-path=/var/log/nginx/access.log \
  --error-log-path=/var/log/nginx/error.log \
  --lock-path=/var/lock/nginx.lock \
  --pid-path=/run/nginx.pid \
  --modules-path=/usr/lib/nginx/modules \
  --http-client-body-temp-path=/var/lib/nginx/body \
  --http-fastcgi-temp-path=/var/lib/nginx/fastcgi \
  --http-proxy-temp-path=/var/lib/nginx/proxy \
  --http-scgi-temp-path=/var/lib/nginx/scgi \
  --http-uwsgi-temp-path=/var/lib/nginx/uwsgi \
  --with-compat \
  --with-debug \
  --with-pcre-jit \
  --with-http_ssl_module \
  --with-http_stub_status_module \
  --with-http_realip_module \
  --with-http_auth_request_module \
  --with-http_v2_module \
  --with-http_dav_module \
  --with-http_slice_module \
  --with-threads \
  --with-http_addition_module \
  --with-http_gunzip_module \
  --with-http_gzip_static_module \
  --with-http_sub_module \
  --with-openssl=../openssl-3.0 \
  --add-dynamic-module=../naxsi/naxsi_src
RUN make && make install

FROM alpine
LABEL version="1.0" "akiboi.net"="akiboI"
RUN apk add --update --no-cache nginx
COPY build/ /etc/nginx/
COPY --from=builder /src/nginx-1.20.2/objs/nginx /usr/sbin
COPY --from=builder /src/nginx-1.20.2/objs/ngx_http_naxsi_module.so /usr/lib/nginx/modules
COPY --from=builder /etc/nginx/naxsi_core.rules /etc/nginx
RUN touch /var/log/naxsi.log && \
  chown nginx /var/log/naxsi.log
CMD ["/usr/sbin/nginx", "-g", "daemon off;"]

その他環境ファイル

nginxのその他設定は、build配下に必要な環境ファイルを作成しdeployしています。Dockerfileの5行目、COPY Build/ /etc/nginx/ 各環境に合わせてnginxを設定ましす。なおnaxsiを利用するためdefault.confを以下のようにしました。 7行目にbackendのWebサーバを設定します。最初は、10行目のLearningModeを設定してエラーリストの内容をみながらホワイトリストを作成していきます。 11行目のファイルに許可したいホワイトリストを追加していくことになります。 エラーリストは、コンテナ内の/var/log/naxsi.logに出力します。今回は別のサーバにsyslogを転送し、このログを監視するようにしています。 ログの監視は、nginx-waf->rsyslog->promtail->prometheus,loke,victoria->grafanaとなっています。

/etc/nginx/default.conf
server {
    listen	443 ssl http2;
    listen [::]:443 ssl http2;

    location / {
        proxy_set_header Host $host;
        proxy_pass http://xxx.xxx.xxx:xxxx/;

        # NAXSIの設定
        #LearningMode;
        include /etc/nginx/naxsi_my.rules;
        SecRulesEnabled;
        DeniedUrl "/50x.html";

        CheckRule "$SQL >= 8" BLOCK;
        CheckRule "$RFI >= 8" BLOCK;
        CheckRule "$TRAVERSAL >= 4" BLOCK;
        CheckRule "$EVADE >= 4" BLOCK;
        CheckRule "$XSS >= 8" BLOCK;

        # ログの出力先
        error_log /var/log/naxsi.log;
        error_log syslog:server=xxx.xxx.xxx.xxx:xxx,facility=local1,tag=proxy_access,severity=info;

    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        return 418 "<html><head><title>Blocked By NAXSI</title></head><body><div style='text-align: center'><h1>Malicious Request</h1><hr><p>This Request Has Been Blocked By NAXSI.</p></div></body></html>"
        internal;
    }
}

ホワイトリストの作成

Webにアクセスしながらエラーリストを確認していきます。そのエラーリストに合わせてホワイトリストを作成します。小規模なWebサーバなので一つ一つエラーを確認しながらホワイトリストを作成していきました。 エラーリストの詳細は、こちらを参照してください。ルールにマッチしたidNをホワイトリスト化していきます。書き方はこちらのサンプルを見ながら設定しました。Webアクセス、エラーリスト確認、ホワイトリスト追加みたいに作業を進めました。一通り確認が終わったら、default.confのLearningModeをコメントアウトし本番適用です。

Tip 【alpine+nginxのリスタート】
ホワイトリスト作成は、トライアンドエラーでやること多いので、その都度nginxのホワイトリストを読み直す必要があります。ついつい、systemctl restart nginxとやりたいところですが、alpine+nginxしかいれてませんのでnginx -s reloadで環境設定を読み直し確認していきます。・・・ついつい忘れて毎回nginx -hで確認してしまう。

確認作業

Webにアクセスしてエラーが出ないことを確認します。試しにhttps://www.akiboi.net/param=<script>xxxx</script>と実行すると弾いてくれました。

/var/log/naxsi.log;
2022/06/24 13:03:59 [error] 3#3: *888 NAXSI_FMT: ip=xxx.xxx.xxx.xxx&server=www.akiboi.net&uri=/parm=<script>xxxx</script>&vers=1.3&total_processed=250&total_blocked=1&config=block&cscore0=$XSS&score0=16&zone0=URL&id0=1302&var_name0=, client: xxx.xxx.xxx.xxx, server: , request: "GET /parm=%3Cscript%3Exxxx%3C/script%3E HTTP/1.1", host: "www.akiboi.net", referrer: ""

これで一通りWAF(naxsi)の導入が完了しました。rsyslogのログのサンプルを乗せておきます。こちらのログは、naxsiのログをrsyslogに転送して、ここでipからGeoIPを利用して地域を付加しています。

/var/log/rsyslog/nginx_waf.log
2022-06-25T10:53:20.456163+09:00 web proxy_access: "NA" "North America" "US" "United States" "San Francisco" "37.730800" "-122.383800" 2022/06/25 01:53:20 [error] 95#95: *3548 NAXSI_FMT: ip=xxx.xxx.xxx.xxx&server=xxx.xxx.xxx.xxx&uri=/owa/auth/logon.aspx&vers=1.3&total_processed=158&total_blocked=3&config=block&cscore0=$RFI&score0=8&zone0=ARGS&id0=1101&var_name0=url, client: xxx.xxx.xxx.xxx, server: , request: "GET /owa/auth/logon.aspx?url=https%3a%2f%2f1%2fecp%2f HTTP/1.1", host: "xxx.xxx.xxx.xxx", referrer: ""
2022-06-25T13:14:29.313530+09:00 web proxy_access: "EU" "Europe" "RU" "Russia" "" "55.738600" "37.606800" 2022/06/25 04:14:29 [error] 95#95: *3592 NAXSI_FMT: ip=xxx.xxx.xxx.xxx&server=xxx.xxx.xxx.xxx&uri=/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php&vers=1.3&total_processed=165&total_blocked=4&config=block&cscore0=$SQL&score0=16&cscore1=$XSS&score1=16&zone0=BODY&id0=1001&var_name0=%3C%3F, client: xxx.xxx.xxx.xxx, server: , request: "POST /vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php HTTP/1.1", host: "xxx.xxx.xxx.xxx:443", referrer: ""
2022-06-25T15:37:22.920420+09:00 web proxy_access: "EU" "Europe" "RU" "Russia" "" "55.738600" "37.606800" 2022/06/25 06:37:22 [error] 95#95: *3669 NAXSI_FMT: ip=xxx.xxx.xxx.xxx&server=xxx.xxx.xxx.xxx&uri=/index.php&vers=1.3&total_processed=201&total_blocked=5&config=block&cscore0=$TRAVERSAL&score0=8&zone0=ARGS&id0=1205&var_name0=s, client: xxx.xxx.xxx.xxx, server: , request: "GET /index.php?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=md5&vars[1][]=HelloThinkPHP21 HTTP/1.1", host: "xxx.xxx.xxx.xxx:443", referrer: ""
:

OWAインジェクション、PHPUnitの脆弱性、ThinkPHPという中国ローカルのフレームワークの脆弱みたいな感じですかね。 Grafana+lokiでログ見ていると色々な脆弱性をついているなぁと感じました。



© 2021 akibo.I Blog v2.0.8