WAF是Web Application Firewall的縮寫
其原理是將已知的攻擊手法,用匹配方式偵測並阻擋
所以可以預想得到
規則會列很多條
也不一定全面
只需要熟悉漏洞原理的人
照樣可以繞過
但有總比沒有好
至少可以過濾掉大部分的script kiddie
除了ModSecurity之外
還有一個專門for nginx的WAF叫做naxsi
這個有機會也可以試用看看
不過這次先用ModSecurity
要把nginx跟ModSecurity結合
必須要自己compile nginx和ModSecurity
以下用我寫的shell script一步一步做講解
Step 1. 安裝compile所需套件
yum install -y git wget gcc gcc-c++ pcre-devel zlib-devel openssl openssl-devel httpd-devel libxml2-devel xz-devel python-devel libcurl-devel libxslt-devel gd gd-devel gmp gmp-devel perl-Tk-devel perl-ExtUtils-Embed.noarch GeoIP GeoIP-devel gperftools gperftools-devel
yum groupinstall -y 'Development Tools'
Step 2. 下載nginx和ModSecurity的source code 並解壓縮
wget https://www.modsecurity.org/tarball/2.9.1/modsecurity-2.9.1.tar.gz
wget http://nginx.org/download/nginx-1.10.3.tar.gz
tar zxvf modsecurity-2.9.1.tar.gz
tar zxvf nginx-1.10.3.tar.gz
Step 3. 用standalone方式Compile ModSecurity
cd modsecurity-2.9.1
./configure --enable-standalone-module --disable-mlogc
make
make install
cd ..
Step 4. Compile nginx with ModSecurity
cd nginx-1.10.3
sed -i "s/Server: nginx/Server: Hello/g" src/http/ngx_http_header_filter_module.c
./configure --prefix=/usr/share/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/var/lib/nginx/tmp/client_body --http-proxy-temp-path=/var/lib/nginx/tmp/proxy --http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi --http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi --http-scgi-temp-path=/var/lib/nginx/tmp/scgi --pid-path=/run/nginx.pid --lock-path=/run/lock/subsys/nginx --user=nginx --group=nginx --with-file-aio --with-ipv6 --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_geoip_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --with-http_perl_module=dynamic --with-mail=dynamic --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream=dynamic --with-stream_ssl_module --with-google_perftools_module --with-debug --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic' --add-module=../modsecurity-2.9.1/nginx/modsecurity
make
make install
第二行的sed指令,主要在修改http header裡的Server signature
預設會顯示Server:nginx,我把它改成Server:Hello
第三行的configure一大串的選項
我其實是參考Centos7裡用yum安裝的nginx的選項
用nginx -V可以看得到
並在最後面加上
--add-module=../modsecurity-2.9.1/nginx/modsecurity
Step 5. 加入nginx user和創建相關資料夾
adduser --no-create-home --user-group -s /sbin/nologin nginx
mkdir /var/lib/nginx
mkdir /var/lib/nginx/tmp
chown -R nginx:nginx /var/lib/nginx
mkdir /etc/nginx/conf.d
mkdir /etc/nginx/sites-available
mkdir /etc/nginx/sites-enabled
Step 6. 手動加入Service
/bin/cat <<EOM >/usr/lib/systemd/system/nginx.service
[Unit]
Description=The nginx HTTP and reverse proxy server
After=network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
PIDFile=/run/nginx.pid
# Nginx will fail to start if /run/nginx.pid already exists but has the wrong
# SELinux context. This might happen when running `nginx -t` from the cmdline.
# https://bugzilla.redhat.com/show_bug.cgi?id=1268621
ExecStartPre=/usr/bin/rm -f /run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/bin/kill -s HUP \$MAINPID
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=process
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOM
systemctl daemon-reload
cd ..
這一步驟其實也是copy用yum安裝好的nginx.service
Step 7. 將ModSecurity設定整合進nginx
cp modsecurity-2.9.1/modsecurity.conf-recommended /etc/nginx/modsecurity.conf
cp modsecurity-2.9.1/unicode.mapping /etc/nginx
sed -ie 's/SecRuleEngine DetectionOnly/SecRuleEngine On/g' /etc/nginx/modsecurity.conf
sed -ie 's/SecPcreMatchLimit .*$/SecPcreMatchLimit 150000/g' /etc/nginx/modsecurity.conf
sed -ie 's/SecPcreMatchLimitRecursion .*$/SecPcreMatchLimitRecursion 150000/g' /etc/nginx/modsecurity.conf
sed -ie 's/SecAuditLogType Serial/SecAuditLogType Concurrent/g' /etc/nginx/modsecurity.conf
sed -ie "/^SecAuditLogType Concurrent$/aSecAuditLogStorageDir \/var\/log\/nginx" /etc/nginx/modsecurity.conf
Step 8. 安裝OWASP top 10 rules
git clone https://github.com/SpiderLabs/owasp-modsecurity-crs.git
mv owasp-modsecurity-crs /etc/nginx
cd /etc/nginx/owasp-modsecurity-crs
cp crs-setup.conf.example crs-setup.conf
sed -ie 's/SecDefaultAction "phase:1,log,auditlog,pass"/#SecDefaultAction "phase:1,log,auditlog,pass"/g' crs-setup.conf
sed -ie 's/SecDefaultAction "phase:2,log,auditlog,pass"/#SecDefaultAction "phase:2,log,auditlog,pass"/g' crs-setup.conf
sed -ie 's/#.*SecDefaultAction "phase:1,log,auditlog,deny,status:403"/SecDefaultAction "phase:1,log,auditlog,deny,status:403"/g' crs-setup.conf
sed -ie 's/# SecDefaultAction "phase:2,log,auditlog,deny,status:403"/SecDefaultAction "phase:2,log,auditlog,deny,status:403"/g' crs-setup.conf
cat <<EOT >> /etc/nginx/modsecurity.conf
Include owasp-modsecurity-crs/crs-setup.conf
Include owasp-modsecurity-crs/rules/REQUEST-901-INITIALIZATION.conf
Include owasp-modsecurity-crs/rules/REQUEST-903.9002-WORDPRESS-EXCLUSION-RULES.conf
Include owasp-modsecurity-crs/rules/REQUEST-905-COMMON-EXCEPTIONS.conf
Include owasp-modsecurity-crs/rules/REQUEST-912-DOS-PROTECTION.conf
Include owasp-modsecurity-crs/rules/REQUEST-913-SCANNER-DETECTION.conf
Include owasp-modsecurity-crs/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf
Include owasp-modsecurity-crs/rules/REQUEST-931-APPLICATION-ATTACK-RFI.conf
Include owasp-modsecurity-crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf
Include owasp-modsecurity-crs/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf
Include owasp-modsecurity-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf
Include owasp-modsecurity-crs/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf
Include owasp-modsecurity-crs/rules/REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION.conf
Include owasp-modsecurity-crs/rules/RESPONSE-951-DATA-LEAKAGES-SQL.conf
Include owasp-modsecurity-crs/rules/RESPONSE-953-DATA-LEAKAGES-PHP.conf
EOT
預設ModSecurity並不會阻擋惡意連線
只會記錄在Log
所以最後的sed指令就是在開啟阻擋並回應403
最後幾行就是把要阻擋哪些惡意攻擊的rule include進來
可自行刪減
Step 9. 設定nginx.conf
其實ModSecurity並不是設定在nginx.conf
所以僅供參考即可
mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.back
cat <<EOT>> /etc/nginx/nginx.conf
user nginx nginx;
worker_processes 1;
worker_rlimit_nofile 260000;
timer_resolution 100ms;
error_log /var/log/nginx/nginx_error.log;
events {
worker_connections 2048;
accept_mutex on;
accept_mutex_delay 100ms;
use epoll;
#multi_accept on;
}
http {
# don't send the nginx version number in error pages and Server header
server_tokens off;
# config to don't allow the browser to render the page inside an frame or iframe
# and avoid clickjacking http://en.wikipedia.org/wiki/Clickjacking
# if you need to allow [i]frames, you can use SAMEORIGIN or even set an uri with ALLOW-FROM uri
# https://developer.mozilla.org/en-US/docs/HTTP/X-Frame-Options
add_header X-Frame-Options SAMEORIGIN;
# when serving user-supplied content, include a X-Content-Type-Options: nosniff header along with the Content-Type: header,
# to disable content-type sniffing on some browsers.
# https://www.owasp.org/index.php/List_of_useful_HTTP_headers
# currently suppoorted in IE > 8 http://blogs.msdn.com/b/ie/archive/2008/09/02/ie8-security-part-vi-beta-2-update.aspx
# http://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx
# 'soon' on Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=471020
add_header X-Content-Type-Options nosniff;
# This header enables the Cross-site scripting (XSS) filter built into most recent web browsers.
# It's usually enabled by default anyway, so the role of this header is to re-enable the filter for
# this particular website if it was disabled by the user.
# https://www.owasp.org/index.php/List_of_useful_HTTP_headers
add_header X-XSS-Protection "1; mode=block";
# with Content Security Policy (CSP) enabled(and a browser that supports it(http://caniuse.com/#feat=contentsecuritypolicy),
# you can tell the browser that it can only download content from the domains you explicitly allow
# http://www.html5rocks.com/en/tutorials/security/content-security-policy/
# https://www.owasp.org/index.php/Content_Security_Policy
# I need to change our application code so we can increase security by disabling 'unsafe-inline' 'unsafe-eval'
# directives for css and js(if you have inline css or js, you will need to keep it too).
# more: http://www.html5rocks.com/en/tutorials/security/content-security-policy/#inline-code-considered-harmful
# add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://ssl.google-analytics.com https://connect.facebook.net; img-src 'self' https://ssl.google-analytics.com https://s-static.ak.facebook.com ; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://themes.googleusercontent.com; frame-src https://www.facebook.com https://s-static.ak.facebook.com; object-src 'none'";
#log_format main '\$remote_addr - \$remote_user [\$time_local] \$request '
# '"\$status" \$body_bytes_sent "\$http_referer" '
# '"\$http_user_agent" "\$http_x_forwarded_for" "\$gzip_ratio"'
# ' "\$connection" "\$connection_requests" "\$request_time"';
index index.php index.html index.htm;
include mime.types;
default_type application/octet-stream;
charset utf-8;
sendfile on;
#sendfile_max_chunk 1m;
tcp_nopush on;
tcp_nodelay on;
server_name_in_redirect off;
keepalive_timeout 10;
keepalive_requests 100;
lingering_time 20s;
lingering_timeout 5s;
keepalive_disable msie6;
gzip on;
gzip_vary on;
gzip_disable "MSIE [1-6]\.";
gzip_static on;
gzip_min_length 1400;
gzip_buffers 32 8k;
gzip_http_version 1.0;
gzip_comp_level 5;
gzip_proxied any;
gzip_types text/plain text/css text/xml application/javascript application/x-javascript application/xml application/xml+rss application/ecmascript application/json image/svg+xml;
client_body_buffer_size 256k;
client_body_in_file_only off;
client_body_timeout 60s;
client_header_buffer_size 64k;
## how long a connection has to complete sending
## it's headers for request to be processed
client_header_timeout 20s;
client_max_body_size 20m;
connection_pool_size 512;
#directio 4m;
ignore_invalid_headers on;
large_client_header_buffers 8 64k;
output_buffers 8 256k;
postpone_output 1460;
#proxy_temp_path /tmp/nginx_proxy/;
request_pool_size 32k;
reset_timedout_connection on;
send_timeout 60s;
types_hash_max_size 2048;
server_names_hash_bucket_size 64;
# for nginx proxy backends to prevent redirects to backend port
# port_in_redirect off;
open_file_cache max=10000 inactive=30s;
open_file_cache_valid 120s;
open_file_cache_min_uses 2;
open_file_cache_errors off;
open_log_file_cache max=4096 inactive=30s min_uses=2;
### Directive describes the zone, in which the session states are stored i.e. store in slimits. ###
### 1m can handle 32000 sessions with 32 bytes/session, set to 5m x 32000 session ###
limit_conn_zone \$binary_remote_addr zone=slimits:10m;
### Control maximum number of simultaneous connections for one session i.e. ###
### restricts the amount of connections from a single ip address ###
limit_conn slimits 10;
## Load virtual host conf files. ##
include /etc/nginx/sites-enabled/*;
## Load another configs from conf.d/ ##
include /etc/nginx/conf.d/*.conf;
}
EOT
Step 10. 設定default.conf
touch /etc/nginx/sites-available/default.conf
cat <<EOT>> /etc/nginx/sites-available/default.conf
server {
listen 80;
server_name YOUR_SERVER_NAME
root /var/www/html;
access_log /var/log/nginx/default.access.log;
error_log /var/log/nginx/default.error.log error;
location ~ /.well-known {
allow all;
break;
}
location / {
index index.php index.html index.htm;
ModSecurityEnabled on;
ModSecurityConfig /etc/nginx/modsecurity.conf;
}
}
EOT
ln -s /etc/nginx/sites-available/default.conf /etc/nginx/sites-enabled/default.conf
echo "hi" > /var/www/html/index.html
Line 21, 22 就是在開啟ModSecurity並指定config位置
Step 11. 啟動nginx並測試
systemctl start nginx.service
用瀏覽器打上伺服器ip開啟首頁
成功的話會看到hi
這時我們在網址後面自己加?id=1
例如:http://127.0.0.1/?id=1
正常還是會看到hi
但我們在後面再加上 AND 1=1
變成:http://127.0.0.1/?id=1 AND 1=1
就會看到403 Forbidden的訊息了
這就代表安裝成功!