プライム・ストラテジー「KUSANAGI」開発チームの石川です。
KUSANAGIでも採用している nginx は非常に高性能なWebサーバです。
Webサーバとして長い間使われてきた Apache HTTPD Server と比較すると設定ファイルの記述が異なるため、設定に戸惑うケースもあります。
Apache HTTPD Serverの .htaccess ファイルに相当する処理を、 nginx の location ディレクティブで行えることを、 以前にコラム Nginx の location の書き方で注意すること で説明しました。
今回は前回の説明に加えて、 location
ディレクティブで間違えがちなアクセス制限を設定する際の落とし穴を説明します。
サイト全体にアクセス制限をかける際の落とし穴
Basic認証を通ると deny all したパスもアクセスできる場合がある
Nginx でサイト全体に Basic 認証を行う場合は server
に以下のような記述を追加します。
server {
# 省略
satisfy any;
allow 127.0.0.1;
deny all;
auth_basic "basic authentication";
auth_basic_user_file "/home/kusanagi/.htpasswd";
}
ここでのポイントは satisfy any を指定することで、特定のIP (上記の例では 127.0.0.1) に対しては Basic 認証を無効にしていることです。
さて、ここで誰からもアクセスされてはいけないファイルがあったとします。
例えば .
で始まるファイルは一般的にアクセスさせない設定にしています。
上記の例に location
を追加してみます。location ~* /\.
で正規表現により .
から始まるパスをアクセスさせないようにしてみましょう。
server {
# 省略
satisfy any;
allow 127.0.0.1;
deny all;
auth_basic "basic authentication";
auth_basic_user_file "/home/kusanagi/.htpasswd";
location ~* /\. {
deny all;
}
}
しかし、上記の設定のままでは .htaccess
にアクセスできるケースが出てしまいます。
なぜかというと、location
の外で宣言された4行目の satisfy any
が location
内でも有効になるからです。satisfy
のデフォルト値は all
ですが、 any
となっているので、 いずれかの条件 を満たすことでアクセスできてしまうのです。
この例で言えば、以下のいずれかの条件を満たすと .
から始まるファイルをアクセスできてしまいます。
- 127.0.0.1 からのアクセス
- Basic 認証を通過する
このように どのような場合であっても アクセスできないような location
を作りたい場合には、location
の外で宣言された satisfy
に影響しないように、明示的に satisfy all
を入れることが必要です。
server {
# 省略
satisfy any;
allow 127.0.0.1;
deny all;
auth_basic "basic authentication";
auth_basic_user_file "/home/kusanagi/.htpasswd";
location ~* /\. {
satisfy all; # ここで 4行目の satisfy any を上書き
deny all;
}
}
なお、これはIPアドレスでアクセス制限を行う場合でも同様です。
server {
# 省略
satisfy any;
allow 127.0.0.1;
allow 192.168.0.0/24;
allow 10.0.1.0/24;
deny all;
location ~* /\. {
satisfy all; # ここで 4行目の satisfy any を上書き
deny all;
}
}
特定の location
にアクセス制限をかける際の落とし穴
Basic認証をかけても PHP にはBasic認証がかからない場合がある
次に location
で特定のディレクトリ配下にのみ Basic 認証をかけた場合を考えてみましょう。
server {
# 省略
location /auth_user_only {
satisfy any;
allow 127.0.0.1;
deny all;
auth_basic "basic authentication";
auth_basic_user_file "/home/kusanagi/.htpasswd";
}
}
これだけの状態であれば /auth_user_only
に対して Basic 認証が効いています。
ところが、以下の場合だとどうでしょうか。
server {
# 省略
location /auth_user_only {
satisfy any;
allow 127.0.0.1;
deny all;
auth_basic "basic authentication";
auth_basic_user_file "/home/kusanagi/.htpasswd";
}
location ~ [^/]\.php(/|$) {
# 省略
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
# 省略
}
}
この場合 /auth_user_only/index.php
には Basic 認証なしでアクセスできてしまいます。
なぜなら location の適用の優先順位 で紹介したように、
modifierなしの location /auth_user_only
よりも 正規表現の location ~ [^/]\.php(/|$)
が優先されるからです。
よって、制限をかけたい location
は、この場合では正規表現で定義する必要があります。
また、その location
の配下で PHP を実行する必要がある場合は、PHPの location
をその中で再定義ことが必要です。
server {
# 省略
location ~ ^/auth_user_only { # 正規表現に変更
satisfy any;
allow 127.0.0.1;
deny all;
auth_basic "basic authentication";
auth_basic_user_file "/home/kusanagi/.htpasswd";
location ~ [^/]\.php(/|$) { # PHPのlocationを再定義
# 省略
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
# 省略
}
}
location ~ [^/]\.php(/|$) {
# 省略
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
# 省略
}
}
KUSANAGIでプロビジョンしたWordPress用のプロファイル設定を見ると、管理画面用のURLにアクセス制限をかけるために、管理画面用の PHP と普通の PHP で PHP の location
を2つ定義しているのが分かります。
Basic認証をかけても画像などにはBasic認証がかからない場合がある
また、他に KUSANAGI では画像やCSS/JavaScriptといったリソースファイルをアクセスログに出力させないための location
を定義しています。
server {
# 省略
location /auth_user_only {
satisfy any;
allow 127.0.0.1;
deny all;
auth_basic "basic authentication";
auth_basic_user_file "/home/kusanagi/.htpasswd";
}
location ~* \.(jpg|jpeg|gif|png|webp|css|js|swf|ico|pdf|svg|eot|ttf|woff|woff2)$ {
# 省略
access_log off;
}
}
上記の PHP の場合と同様に、例えば /auth_user_only/secret.jpg
に対しては Basic 認証なしでアクセスできてしまいます。
この場合も、画像を許可させないようにするには、 location /auth_user_only
内に再定義が必要です。
server {
# 省略
location ~ ^/auth_user_only { # 正規表現に変更
satisfy any;
allow 127.0.0.1;
deny all;
auth_basic "basic authentication";
auth_basic_user_file "/home/kusanagi/.htpasswd";
location ~* \.(jpg|jpeg|gif|png|webp|css|js|swf|ico|pdf|svg|eot|ttf|woff|woff2)$ {
# 省略
access_log off;
}
}
location ~* \.(jpg|jpeg|gif|png|webp|css|js|swf|ico|pdf|svg|eot|ttf|woff|woff2)$ {
# 省略
access_log off;
}
}
完全にBasic認証をかけたいならば…
location
には正規表現を適用させない一致 ^~
があります。
これを利用すると、この location
には他の正規表現が適用されないので、安全に Basic 認証をかけることができます。
server {
# 省略
location ^~ /auth_user_only { # 配下に正規表現を適用させないmodifier
satisfy any;
allow 127.0.0.1;
deny all;
auth_basic "basic authentication";
auth_basic_user_file "/home/kusanagi/.htpasswd";
}
}
ただ、この場合は配下のディレクトリで PHP を実行することもできないので注意してください。
この配下の画像やリソースのアクセスログも出力されるようになります。
まとめ
昨今は「公開する意図がないデータが設定ミスによって公開されていた」といったセキュリティ事案が多く見られるようになりました。
Nginxの location
は柔軟な一方で様々な location
の記述がからみ合っていることで、設定ミスが起こりやすくなっています。
実運用に入る前には、必ず意図した通りのアクセス許可・不許可が設定できているか、きちんと確認するようにしてください。
また、その際にどこの設定が最終的に効いているのかを判断する際に、この説明を活用してください。
参考
Module nginx_http_core_module, location directive
Module nginx_http_auth_basic_module, auth_basic directive
Module nginx_http_core_module, satisfy directive