WordPress や EC-CUBE とかそういうの使ってると .htaccess
にこんな風に書いてある。
RewriteEngine On RewriteBase / RewriteRule ^index\.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L]
RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !^(.*)\.(gif|png|jpe?g|css|ico|js|svg)$ [NC] RewriteRule ^(.*)$ index.php [QSA,L]
RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !^(.*)\.(gif|png|jpe?g|css|ico|js|svg|map)$ [NC] RewriteRule ^(.*)$ index.php [QSA,L]
どっちも似たようなことを書いているけど、ざっくり説明すると「ファイルが存在しなければ全部 index.php に投げる」みたいな設定。(EC-CUBE の方は後方参照しないのに ^(.*)\.
とか ^(.*)$
してて無駄だなと思う)
WordPress なら /2021/03/diary
みたいなパスや、EC-CUBE なら /products/list
みたいなパスで、実際にファイルやディレクトリが存在していないにもかかわらずページが表示されるのは index.php
にリダイレクトされて index.php
が受け取った情報によってページの振り分けをしているから。
さて、「サイト全体にベーシック認証をかけて特定のページだけ認証を無効にしたい(除外したい)」という話だけど、どんなときにそういうことが必要になるかというと、例えば EC-CUBE を使っていて決済通知を受け取るような場合に何を使っているかにもよって変わるけど、「ベリトランス」だと /shopping/vt3g_shopping_recv
、「クロネコwebコレクト」だと /shopping/yamato_payment_recv
、「GMOペイメントゲートウェイ」だと /shopping/gmo_payment_recv
に決済通知システムからのリクエストが来る。ここにベーシック認証がかかっていると決済通知は認証を通れない。本番環境ではあまり無いだろうけど検証環境とかだとこういう構成が必要になってくることがある。
また、AWS などで Application LoadBalancer(以下 ALB)のヘルスチェックのために認証を外す必要がある。
以下、Apache 2.4 での話。
今回のような場合は下記のように書いたりするかもしれない。
RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !^(.*)\.(gif|png|jpe?g|css|ico|js|svg|map)$ [NC] RewriteRule ^(.*)$ index.php [QSA,L] ### ベーシック認証設定 AuthType Basic AuthUserFile /var/www/.htpasswd AuthName . Require valid-user ### リクエストが ``/path/to/noauth'' だったら環境変数 ``noauth'' を定義する SetEnvIf Request_URI ^/path/to/noauth$ noauth ### ローカルからのアクセスは許可 ## Apache 2.2 #Allow from localhost ## Apache 2.4 Require local ### 環境変数 ``noauth'' が定義されていたら許可 ## Apache 2.2 #Allow from env=noauth ## Apache 2.4 Require env noauth
上記の設定だと
localhost
からのアクセスは無制限- それ以外のパスには Basic 認証がかかっているが
/path/to/noauth
は無効
と思われるが、実際には /path/to/noauth
にアクセスしても Basic 認証が発生する。順を追ってみると下記のようになってるはず。
/path/to/noauth
にアクセスする- 環境変数
noauth
が定義される - 環境変数
noauth
がチェックされて許可 /path/to/noauth
は存在しないためRewriteRule
によってindex.php
にリダイレクトされるindex.php
にアクセスするindex.php
では環境変数noauth
は定義されない- 環境変数
noauth
がチェックされて拒否
環境変数 noauto
が定義されるのは /path/to/noauth
だけであって index.php
では発生しない。
「そうか!ならこうすれば良いんだな!」ってことで下記のようにしたとする。
SetEnvIf Request_URI ^/index.php$ noauth SetEnvIf Request_URI ^/path/to/noauth$ noauth
一見これでも目的が達成できているように見える。しかし(いまどきブラウザに打ち込む人は少ないと思うけど)/index.php
に直接アクセスすると認証をすり抜けるという罠がある。
とりあえず index.php
に noauth
を設定をするのは止めて Apache の LogLevel
を debug
にしてログを見てみよう。
authorization result of Require valid-user : denied (no authenticated user yet) authorization result of Require local : denied authorization result of Require env noauth : granted ❶noauth が定義されている authorization result of <RequireAny>: granted ❷許可 authorization result of Require valid-user : denied (no authenticated user yet) authorization result of Require local : denied authorization result of Require env noauth : denied ❸noauth が定義されていない authorization result of <RequireAny>: denied (no authenticated user yet) ❹拒否
認証が 2 回行われていることや、❷で一度は granted されているのに❹で denied されていることがわかる。(ちなみにここの Require
の順番は .htaccess
に書いた順番)
「index.php
に noauth
が引き継げればいいのに…」と思った人は index.php
を
<?php phpinfo();
にして「Apache Environment」のセクションを見てみよう。(Basic 認証はクリアしても無効にしても可)
環境変数の一覧に定義した覚えのない REDIRECT_noauth
という環境変数が見つかるはず。(_noauth
部分は定義した環境変数によって変わる)
これはリダイレクトした際に Apache が新たに定義する環境変数で、プレフィックスとして REDIRECT_
が付与される。他には REDIRECT_URL
や REDIRECT_STATUS
などがある。
つまり最初に定義した noauth
環境変数は index.php
にリダイレクトした際に消えたのではなく REDIRECT_noauth
に置き換えられたということ。なのでこれを許可してやればいいことになる。
SetEnvIf Request_URI ^/path/to/noauth$ noauth Require env noauth Require env REDIRECT_noauth
なお、Require env
の書式は Require env env-env [env-var] ...
となっているので下記のようにまとめて書いても良い。動作はドキュメントに Access is allowed only if one of the given environment variables is set.
とある通り、どれか 1 つの環境変数が含まれていれば granted となる。
Require env noauth REDIRECT_noauth
もう一度 /path/to/noauth
にアクセスすれば 1 回目は Require env noauth: granted
、2 回目は Require env REDIRECT_noauth: granted
となっているはず。
# /path/to/noauth の認証 authorization result of Require valid-user : denied (no authenticated user yet) authorization result of Require local : denied authorization result of Require env noauth: granted authorization result of: granted # index.php の認証 authorization result of Require valid-user : denied (no authenticated user yet) authorization result of Require local : denied authorization result of Require env noauth: denied authorization result of Require env REDIRECT_noauth: granted authorization result of : granted
直接 index.php
にアクセスした場合は REDIRECT_noauth
が存在しないので Basic 認証が発生する。
一応ネット上の情報も調べてみたが下記のように REDIRECT_STATUS
でやっている人もいた。これは index.php
にリダイレクトされたときに自動的に定義される REDIRECT_STATUS
をリダイレクト元にも設定してしまえ、というもの…だと思う。
SetEnvIf Request_URI ^/path/to/noauth$ REDIRECT_STATUS Require env REDIRECT_STATUS
一見お手軽そうだが、リダイレクト元で定義された REDIRECT_STATUS
(暗黙的に REDIRECT_STATUS=1
) は index.php
にリダイレクトされると REDIRECT_
プレフィックスが付与されて REDIRECDT_REDIRECT_STATUS=1
となり、index.php
では新たに REDIRECT_STATUS=200
が定義されるため少々微妙に思える。
/path/to/noauth | → | index.php | |
---|---|---|---|
REDIRECT_STATUS | 1 | → | 200 |
REDIRECT_REDIRECT_STATUS | 未定義 | → | 1 |
まとめとして EC-CUBE で ALB のヘルスチェックファイルや「クロネコwebコレクト」、「GMOペイメントゲートウェイ」、「Paidy」の決済通知先を Basic 認証の対象外にする例を下記に書いておく。(私はネットワークレベルで制限するから .htaccess で設定しないんだけどレンタルサーバなら有用かも…)
AuthType Basic AuthName . AuthUserFile /var/www/.htpasswd # デフォルトでは RequireAny なので # ローカルからのアクセスや valid-user、環境変数(noauth または REDIRECT_noauth)が定義されていれば許可 Require local Require valid-user Require env noauth REDIRECT_noauth # ロードバランサー ヘルチェックファイル用 SetEnvIf Request_URI ^/healthcheck.php$ noauth # ベリトランス 決済通知用 SetEnvIf Request_URI ^/shopping/vt3g_payment_recv$ noauth # クロネコwebコレクト 決済通知用 SetEnvIf Request_URI ^/shopping/yamato_payment_recv$ noauth # GMO-PG 決済通知用 SetEnvIf Request_URI ^/shopping/gmo_payment_recv$ noauth # Paidy 決済通知用 SetEnvIf Request_URI ^/paidy_webhook$ noauth
複雑な例として Apache の環境変数に任意の値を使ったお遊びを下記に書いておく。
# アクセスを許可する URL (noauth 環境変数にレベル付する) SetEnvIf Request_URI ^/path/to/1$ noauth=1 SetEnvIf Request_URI ^/path/to/5$ noauth=5 ### index.php 用 # REDIRECT_noauth が ``1'' 以上 Require expr %{env:REDIRECT_noauth} -ge 1 ### /path/to/1 用 # noauth 環境変数が ``1'' かつプライベート IP からのアクセス <RequireAll> Require expr %{env:noauth} -eq 1 Require ip 10 172.16.0.0/12 192.168 </RequireAll> ### /path/to/5 用 # noauth 環境変数が ``5'' かつローカルからのアクセス <RequireAll> Require expr %{env:noauth} -eq 5 Require local </RequireAll>
Require env
は書式が Require env env-var [env-ver] ... で環境変数が定義されているかどうかで値のチェックができないため Require env noauth=1
のように書きたい場合は Require expr
を使わなければならない。
ALB 使っていて mod_remoteip とか mod_extract_forwarded の設定をしてない人は IP で制限しようとすると面倒になるので注意した方がいいかも。