nginx.conf内でリクエストヘッダーとリファラをチェックして403を返す | Mac + Docker + Rails + Next.js その0048
はじめに
- Next.jsでフロントエンドを作成して、そのドメインを
web-bonsai.techとしています。 - RailsでAPIを作成して、そのドメインを
api.web-bonsai.techとしています。
APIはweb-bonsai.techからのリクエストだけ受け付けたいので、リクエストヘッダーとリファラをチェックするようにしたときのメモです。
nginx.confはGitHubのリポジトリにはコミットされないように、.gitignoreしています。
nginx.confを編集する
以下のようにしてみました。
server {
〜略〜
# invalidなリクエストかどうか判定するための変数を定義
set $invalid_request '';
# $invalid_refererのとき$invalid_requestを1にする
valid_referers server_names example.com;
if ($invalid_referer) {
set $invalid_request 1;
}
# $invalid_refererだったとしても、headers['X-API-PASSWORD']が一致したとき$invalid_request = ''に戻す
if ($http_x_api_password = 'abcde') {
set $invalid_request '';
}
if ($invalid_request) {
return 403;
}
〜略〜
}
nginx.confのコードについての説明
まず判定用の変数$invalid_requestを定義して、空文字列を入れておきます。
# invalidなリクエストかどうか判定するための変数を定義
set $invalid_request '';
リファラが無い場合や、リファラのサーバ名がexample.comでない場合は、以下のブロックで変数$invalid_requestが1になります。
# $invalid_refererのとき$invalid_requestを1にする
valid_referers server_names example.com;
if ($invalid_referer) {
set $invalid_request 1;
}
Next.jsでSSGビルドをするとき、getStaticProps()内でfetch()すると、リファラが無いので上記のブロックで変数$invalid_requestが1になってしまいますが、リクエストヘッダーに正しい X-API-PASSWORD が存在する場合は、変数$invalid_requestを空文字列に戻します。
ここでは 'abcde' と比較していますが、実際には文字数や文字種の多いパスワードと比較します。
# $invalid_refererだったとしても、headers['X-API-PASSWORD']が一致したとき$invalid_request = ''に戻す
if ($http_x_api_password = 'abcde') {
set $invalid_request '';
}
$invalid_requestが空文字列でない場合は403を返します。
if ($invalid_request) {
return 403;
}
Next.jsのgetStaticProps()の記述内容
以下の通り、headersを設定して送信すると、nginx.conf内では変数$http_x_api_passwordで参照できます。
export const getStaticProps = async () => {
const response = await fetch(`${process.env.API_BASE_PATH}/api/tasks`, {
headers: {
'X-API-PASSWORD': process.env.X_API_PASSWORD ?? '',
},
});
const props = await response.json();
return {
props,
};
};
環境変数の定義
frontend/.env.localに以下の通り定義します。
X_API_PASSWORD=abcde
frontendサービスでyarn buildする
いつも通り以下のコマンドでビルドします。
$ docker-compose run --rm frontend yarn build
おわりに
nginx.conf内で2つの変数の文字列を結合して判定したり、正規表現を使って判定するともっと適切な書き方がありそうな気がします。
また、web-bonsai.techのフォーム入力などからリクエストする場合には、httponlyでsecureなcookieなどを使って判定した方がよさそうだなと思っていますが、nginx.confに慣れていなくてつらいので今のところはここまでにしておきます。