Nginxセキュリティ設定


## 概要

[Apacheセキュリティ設定](http://qiita.com/bezeklik/items/1c4145652661cf5b2271)と同等程度の設定を目指します。
Apacheとの対比のために、Nginx上設定が不要な項目も設定不要として記載します。
SSLの設定はここでは記載していません。


### 想定環境
NginxのMainlineの最新版を使います。
現時点(2016/1/27)の最新版は1.9.10なのでそれを使います。
Stableではなく、Mainlineを利用します。

StableとMainlineの違いは[nginx開発者コメント：nginx 1.8およびnginx 1.9リリースについて](https://oss.sios.com/yorozu-blog/nginx-comment20150703)には以下のようにあります。
>ステーブルラインとメインラインの重要な違いは、メインラインはインフラストラクチャ操作に影響を与える可能性があり、開発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](http://nginx.org/en/docs/http/ngx_http_core_module.html#server_tokens)）

Nginxのバージョン番号をエラーページとServer headerに含まれないようにします。


```nginx.conf
server_tokens off;
```


#### TRACEメソッドの無効化（XST対策）

NginxはデフォルトでTRACEメソッドは許容していないため設定不要です。

#### エンティティタグ（ETag）

Header自体を削りたい場合は、more_clear_headersの導入が必要になります。
ETagを生成しない設定は以下の通りです。

```conf.d/default.conf
etag off;
```

#### X-Powered-By

PHPサイトのレスポンスヘッダーにはPHPを利用していることを示す、X-Powered-By:が出力されることが多いので抑止します。
これは、Nginx側ではなくphp−fpm側(php.ini)で設定しましょう。

```php.ini
expose_php = Off
```

サードパーティ製モジュールの[more_clear_headers](https://github.com/openresty/headers-more-nginx-module#readme)を利用するとNginx側の設定のみで削除することが可能になります。
X-Runtimeについてもmore_clear_headersで削除可能です。

#### Options -Indexes ([autoindex](http://nginx.org/en/docs/http/ngx_http_autoindex_module.html#autoindex))
デフォルトでインデックスページ(ディレクトリー内のファイル一覧)は非表示になっているため明示的に指定は不要です。
どうしても見せたい場合は、明示的にonにする必要があります。


```conf.d/default.conf
autoindex off;
```

#### include

必要なconfigファイルのみを読み込む設定に変更します。

```nginx.conf
include /etc/nginx/conf.d/default.conf;
```


### セキュリティ設定

#### AllowOverride

そもそもNginxには無いため考慮不要です。

#### HTTP メソッドの制限
不要なHTTPメソッドを受け付けないようにします。
GET以外のHTTPメソッド以外の場合は403を返す設定をします。

```conf.d/default.conf
if ($request_method != GET ) {
    return 403;
}
```


#### X-Frame-Options（クリックジャッキング対策（[add_header](http://nginx.org/en/docs/http/ngx_http_headers_module.html)）)

[クリックジャッキング](https://ja.wikipedia.org/wiki/クリックジャッキング)対応として、自身と生成元が同じフレーム内に限りページを表示する設定をします。

```conf.d/default.conf
add_header X-Frame-Options SAMEORIGIN;
```

参考: [X-Frame-Options レスポンスヘッダ](https://developer.mozilla.org/ja/docs/HTTP/X-Frame-Options)

#### X-XSS-Protection（XSSフィルター機能）

クロスサイトスクリプティング（XSS）に対するフィルタ機能を強制的に有効にする。

```conf.d/default.conf
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](https://bugzilla.mozilla.org/show_bug.cgi?id=471020)というような報告もあがっています。

```conf.d/default.conf
add_header X-Content-Type-Options nosniff;
```


参考: [MIME タイプのセキュリティ リスクの軽減](https://msdn.microsoft.com/ja-jp/library/gg622941(v=vs.85).aspx)


#### Content-Security-Policy 

Content Security Policy (CSP) は、クロスサイトスクリプティング (XSS) やデータインジェクション攻撃を含む、よく知られた種類の攻撃を検出して軽減することができます。
例えば、WebFont及びGoogle Analyticsを許容する設定は以下のようになります。

```conf.d/default.conf
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 対策に利用できる技術](https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/705.html)

#### リクエストのサイズ制限
クライアントからのリクエストサイズが事前に見積もれるのであれば、リクエストボディ、リクエストヘッダの最大サイズの制限を設定します。

リクエストボディの設定として、1Kbに設定しています。
これ以上のサイズが送られてくると413(Request Entity Too Large)を返します。

```conf.d/default.conf
client_max_body_size 1k;
```

リクエストヘッダの設定として、1Kbに設定しています。
この定義を超えるヘッダーを送られた場合は414 (Request-URI Too Large) を返します。

```conf.d/default.conf
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
```


### セキュリティテスト

[ウェブサイトの脆弱性を確認する-Nikto](http://qiita.com/mken/items/2761c8d63a7b8188804f)を参考に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の内容は必要に応じてコメントアウトを外してください。

```nginx.conf
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;
}
```


```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;
    }


}
```