はじめに
ちょっとした検証とか確認でWAFをさくっと作りたいと思ったので、その時のメモ。
owasp/modsecurity
のdockerイメージがあることがわかったものの、用意されている環境変数一覧の中に、特定の文字列を含むようなリクエストを遮断するような設定をする変数がなかったのがきっかけです。
やること
OWASPで用意しているModSecurityのdockerイメージを使って、"test"というワードをパラメータに含むGET/POSTリクエストを遮断するWAFを作成します。
ModSecurityのイメージはapache, nginx, IISの3種類用意されていますが、今回はnginxを使用します。
もっと本格的なWAFを作ろうと思うと、Core Rule Set(CRS)を含むような設定を入れるといいと思いますが、今回はそこまではやらないです。
環境
% sw_vers
ProductName: macOS
ProductVersion: 11.6
BuildVersion: 20G165
% docker --version
Docker version 20.10.12, build e91ed57
% docker-compose --version
docker-compose version 1.29.2, build 5becea4c
やったこと
最終的に用意したファイル、ディレクトリは下の通りです。
% tree .
.
├── docker-compose.yml
├── modsecurity
│ ├── custom-error.html
│ ├── default.conf
│ ├── index.html
│ ├── log
│ └── modsecurity.conf
└── nginx
├── default.conf
├── index.html
└── succeeded.html
docker-compose.yml
の中身は下に載せています。
ここで、waf
が今回のメインであるModSecurity、nginx
はwaf
の後段に配置するアプリケーションサーバを想定しています。
version: "3"
services:
waf:
image: owasp/modsecurity:nginx
ports:
- "8080:80"
restart: always
volumes:
- ./modsecurity/default.conf:/etc/nginx/conf.d/default.conf
- ./modsecurity/index.html:/var/www/html/index.html
- ./modsecurity/custom-error.html:/var/www/html/custom-error.html
- ./modsecurity/modsecurity.conf:/etc/modsecurity.d/modsecurity-override.conf
- ./modsecurity/log:/var/log/nginx
container_name: waf-nginx
networks:
- default
nginx:
image: nginx:1.20.2
ports:
- "80:80"
restart: always
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
- ./nginx/index.html:/var/www/html/index.html
- ./nginx/succeeded.html:/var/www/html/nginx/index.html
container_name: nginx
networks:
- default
networks:
default:
アプリサーバ(nginx)の設定
nginx
ディレクトリ以下にはnginx用の設定ファイル3つを配置しています。
waf
経由でリクエストが来たときには、「nginx-succeeded」という文字列を表示するようにしています。
起動時の疎通確認用にlocation /
の設定をしています。
POSTでも同じ結果を返すようにerror_page
の設定を入れています。
server {
listen 80;
listen [::]:80;
server_name localhost;
location /nginx/ {
root /var/www/html;
index index.html;
}
location / {
root /var/www/html;
index index.html;
}
error_page 405 =200 $uri;
}
nginx
nginx-succeeded
WAF(ModSecurity on nginx)の設定
nginx
の設定(default.conf)の内容を↓に載せています。
location
については、http://localhost/nginx
のリクエストのときに後段のnginx
にリクエストを渡すような設定にしています。
ModSecurityでリクエストが遮断されたときにはhttp status codeが403になるので、このとき「modsecurity-blocked!」という文字列が表示されるようにしています。
server {
listen 80;
listen [::]:80;
server_name localhost;
location / {
root /var/www/html;
index index.html;
}
location /nginx {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect http:// https://;
proxy_pass http://nginx/nginx/;
}
error_page 403 /custom-error.html;
location = /custom-error.html {
root /var/www/html;
internal;
}
}
ModSecurityの設定が↓になります。
重要なのが、以下の2点です。
-
SecRuleEngine On
... デフォルトがDetectionOnly
になっていて、検知だけして遮断しない設定なので、遮断するようにOn
を指定。 -
SecRule ARGS_NAMES "test"
... このSecRule
がdockerコンテナに渡す環境変数として用意されていないので、configファイルを書いてコンテナに渡すようにしました。この設定でGETやPOSTなどのリクエストのパラメータ名が"test"であれば、ログ出力して遮断する設定にしています。POSTだとRequest Bodyを見ないといけないので、"phase:2"を指定しているところが注意点。
SecRuleEngine On
SecRequestBodyAccess On
SecAuditEngine On
SecAuditLog /var/log/nginx/modsec_audit.log
SecAuditLogParts ABCFHZ
SecRule ARGS_NAMES "test" "id:3,phase:2,t:lowercase,deny,log"
modsecurity
modsecurity-blocked!
動作確認
docker-compose up -d
で起動したあと、Google ChromeのAdvanced REST Clientで動作確認しました。
まとめ
簡単なWAFを作ってみました。
SecRule
のところは色んなパラメータがあったり、正規表現が使えたりともっとできることは多いですが、ARGS_POST
がうまく動かなかったりしてつまりました。結果的にはARGS
やARGS_NAMES
で解決したのですが、今度また触る機会があれば、もうちょっと調べてみようと思います。
参考
- https://qiita.com/housu_jp/items/1c0204c8cf0ce32a7605
- https://atmarkit.itmedia.co.jp/ait/articles/1409/18/news010_4.html
- https://coreruleset.org/docs/rules/creating/
- https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual-(v2.x)
- https://hub.docker.com/r/owasp/modsecurity
- https://qiita.com/ryounagaoka/items/fd641e39a196b47db875