概要
Apacheセキュリティ設定と同等程度の設定を目指します。
Apacheとの対比のために、Nginx上設定が不要な項目も設定不要として記載します。
SSLの設定はここでは記載していません。
想定環境
NginxのMainlineの最新版を使います。
現時点(2016/1/27)の最新版は1.9.10なのでそれを使います。
Stableではなく、Mainlineを利用します。
StableとMainlineの違いはnginx開発者コメント:nginx 1.8およびnginx 1.9リリースについてには以下のようにあります。
ステーブルラインとメインラインの重要な違いは、メインラインはインフラストラクチャ操作に影響を与える可能性があり、開発APIの変更がある場合があるため、nginxのサードパーティ製モジュールや拡張機能に影響を及ぼす場合があります。それ以外は、安全にご利用いただけます。
###検証環境
- Amazon Linux AMI release 2015.09
- php-fpm 5.6.17
- nginx 1.9.10
デフォルト値検証の環境情報
GCCとOpenSSLのバージョンは削っていますが、インストール後は以下のような状態になっています。
$ nginx -V
nginx version: nginx/1.9.10
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-http_ssl_module --with-http_realip_module --with-http_addition_module --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_stub_status_module --with-http_auth_request_module --with-threads --with-stream --with-stream_ssl_module --with-http_slice_module --with-mail --with-mail_ssl_module --with-file-aio --with-ipv6 --with-http_v2_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic'
また、/usr/share/nginx/html/ 配下は以下のようになっています。
$ tree
├── 50x.html
├── index.html
└── test.php
ポリシー
- Yum によるパッケージ管理を利用(ビルドはしない)
- コストパフォーマンス優先
- 成立の可能性が低い脆弱性への対応で汎用性やシンプルさが失われるような設定は避ける
設定項目
情報の隠匿(隠蔽)
バージョン情報(ServerTokens)
Nginxのバージョン番号をエラーページとServer headerに含まれないようにします。
server_tokens off;
TRACEメソッドの無効化(XST対策)
NginxはデフォルトでTRACEメソッドは許容していないため設定不要です。
エンティティタグ(ETag)
Header自体を削りたい場合は、more_clear_headersの導入が必要になります。
ETagを生成しない設定は以下の通りです。
etag off;
X-Powered-By
PHPサイトのレスポンスヘッダーにはPHPを利用していることを示す、X-Powered-By:が出力されることが多いので抑止します。
これは、Nginx側ではなくphp−fpm側(php.ini)で設定しましょう。
expose_php = Off
サードパーティ製モジュールのmore_clear_headersを利用するとNginx側の設定のみで削除することが可能になります。
X-Runtimeについてもmore_clear_headersで削除可能です。
Options -Indexes (autoindex)
デフォルトでインデックスページ(ディレクトリー内のファイル一覧)は非表示になっているため明示的に指定は不要です。
どうしても見せたい場合は、明示的にonにする必要があります。
autoindex off;
include
必要なconfigファイルのみを読み込む設定に変更します。
include /etc/nginx/conf.d/default.conf;
セキュリティ設定
AllowOverride
そもそもNginxには無いため考慮不要です。
HTTP メソッドの制限
不要なHTTPメソッドを受け付けないようにします。
GET以外のHTTPメソッド以外の場合は403を返す設定をします。
if ($request_method != GET ) {
return 403;
}
X-Frame-Options(クリックジャッキング対策(add_header))
クリックジャッキング対応として、自身と生成元が同じフレーム内に限りページを表示する設定をします。
add_header X-Frame-Options SAMEORIGIN;
X-XSS-Protection(XSSフィルター機能)
クロスサイトスクリプティング(XSS)に対するフィルタ機能を強制的に有効にする。
add_header X-XSS-Protection "1; mode=block";
X-Content-Type-Options
X-Content-Type-Options レスポンスヘッダーの値に nosniff を設定して送信すると、Internet Explorer が MIME Sniffing 機能で content-type 宣言を回避するのを防止できるそうです。
ちなみに、FirefoxにはAdd X-Content-Type-Options: nosniff support to Firefoxというような報告もあがっています。
add_header X-Content-Type-Options nosniff;
Content-Security-Policy
Content Security Policy (CSP) は、クロスサイトスクリプティング (XSS) やデータインジェクション攻撃を含む、よく知られた種類の攻撃を検出して軽減することができます。
例えば、WebFont及びGoogle Analyticsを許容する設定は以下のようになります。
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://ssl.google-analytics.com; img-src 'self' https://ssl.google-analytics.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com 'self' https://themes.googleusercontent.com; frame-src 'none' object-src 'none'";
参考:IPA ISEC セキュア・プログラミング講座:Webアプリケーション編 第8章 マッシュアップ:クライアントサイドマッシュアップ: #4 対策に利用できる技術
リクエストのサイズ制限
クライアントからのリクエストサイズが事前に見積もれるのであれば、リクエストボディ、リクエストヘッダの最大サイズの制限を設定します。
リクエストボディの設定として、1Kbに設定しています。
これ以上のサイズが送られてくると413(Request Entity Too Large)を返します。
client_max_body_size 1k;
リクエストヘッダの設定として、1Kbに設定しています。
この定義を超えるヘッダーを送られた場合は414 (Request-URI Too Large) を返します。
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
セキュリティテスト
ウェブサイトの脆弱性を確認する-Niktoを参考にnikto.plを導入し、確認します。
$ perl ./nikto.pl -h 127.0.0.1
- Nikto v2.1.5
---------------------------------------------------------------------------
+ Target IP: 127.0.0.1
+ Target Hostname: localhost
+ Target Port: 80
+ Start Time: 2016-01-27 14:03:35 (GMT0)
---------------------------------------------------------------------------
+ Server: nginx
+ Server leaks inodes via ETags, header found with file /, fields: 0x5668432f 0x264
+ Uncommon header 'x-frame-options' found, with contents: SAMEORIGIN
+ Uncommon header 'x-xss-protection' found, with contents: 1; mode=block
+ Uncommon header 'x-content-type-options' found, with contents: nosniff
+ Uncommon header 'content-security-policy' found, with contents: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://ssl.google-analytics.com; img-src 'self' https://ssl.google-analytics.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com 'self' https://themes.googleusercontent.com; frame-src 'none' object-src 'none'
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ OSVDB-3233: /test.php: PHP is installed, and a test script which runs phpinfo() was found. This gives a lot of system information.
+ OSVDB-3092: /test.php: This might be interesting...
+ 6545 items checked: 0 error(s) and 7 item(s) reported on remote host
+ End Time: 2016-01-27 14:04:42 (GMT0) (7 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested
セキュリティテスト(その2)
nikto.plに指摘された項目のために、以下の対応を行います。
- X-Frame-Optionsしか見てないので、それ以外のadd_headerの設定は削る。
- test.phpの中でphp_infoを呼び出していたので、php_infoをコメントアウトする。
- test.phpの名前をlist.phpに変更する。
なお、X-Frame-Optionsは付与しても、付与しなくても怒られるので無視します。
上記の対応を実施した結果、以下のような結果になりました。
]$ perl ./nikto.pl -h 127.0.0.1
- ***** SSL support not available (see docs for SSL install) *****
- Nikto v2.1.5
---------------------------------------------------------------------------
+ Target IP: 127.0.0.1
+ Target Hostname: localhost
+ Target Port: 80
+ Start Time: 2016-01-27 15:04:37 (GMT0)
---------------------------------------------------------------------------
+ Server: nginx
+ Uncommon header 'x-frame-options' found, with contents: SAMEORIGIN
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ 6545 items checked: 0 error(s) and 1 item(s) reported on remote host
+ End Time: 2016-01-27 15:04:42 (GMT0) (5 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested
コピペ用設定
これらを加味した結果、以下のような設定になります。
add_headerの内容は必要に応じてコメントアウトを外してください。
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
server_tokens off;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/default.conf;
}
server {
listen 80;
server_name localhost;
client_max_body_size 1k;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
#charset koi8-r;
#access_log /var/log/nginx/log/host.access.log main;
add_header X-Frame-Options SAMEORIGIN;
#add_header X-Content-Type-Options nosniff;
#add_header X-XSS-Protection "1; mode=block";
#add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://ssl.google-analytics.com; img-src 'self' https://ssl.google-analytics.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com 'self' https://themes.googleusercontent.com; frame-src 'none' object-src 'none'";
etag off;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
if ($request_method != GET ) {
return 403;
}
}
location ~ \.php$ {
root html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html$fastcgi_script_name;
include fastcgi_params;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}