web_bonsaiの日記

web開発の学習日記です。誰に見せるためでもないただの日記です。

nginx.conf内でリクエストヘッダーとリファラをチェックして403を返す | Mac + Docker + Rails + Next.js その0048

はじめに

  • Next.jsでフロントエンドを作成して、そのドメインweb-bonsai.tech としています。
  • RailsAPIを作成して、そのドメイン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に慣れていなくてつらいので今のところはここまでにしておきます。