公開エンドポイントのレート制限キーは、接続元が信頼プロキシ範囲内のときだけ信頼ヘッダーを採用する
Go
OAuth
Security
知識
判断
運用
未認証で受け付ける公開エンドポイント(動的クライアント登録、ログイン等)にはレート制限が必須になる。このときキーに何を使うかで、偽装耐性と可用性のどちらかを落としやすい。
二つの失敗モード
- クライアント申告ヘッダーをそのまま信じる: framework のクライアント IP 取得関数は、信頼プロキシを明示設定しないと既定で全プロキシを信頼し、転送ヘッダーの最左=クライアントが自由に書ける値を返す実装がある。この場合、値を毎回変えるだけで制限を丸ごと回避でき、被害者の IP を名乗れば標的型のロックアウトもできる。任意文字列がキーになるためエントリ膨張も起こせる。
- 接続元アドレス(TCP ピア)をそのまま使う: 前段にプロキシや CDN がある構成では、これは常にプロキシのアドレスになる。結果、全ユーザーが1つのバケットを共有し、誰か1人の試行で全員が締め出される可用性の穴になる。偽装はできないが DoS として使える。
「偽装されないから接続元アドレスを使う」は前者を避けて後者に落ちるだけで、解決になっていない。
判断基準
- 信頼ヘッダー(前段が実クライアント値で上書きするヘッダー)は、接続元アドレスが信頼プロキシの範囲内にあるときだけ採用する。範囲外なら無視して接続元アドレスへ落とす。判定に使う接続元アドレスは転送ヘッダーの影響を受けない値であること。
- framework が「このヘッダーを信じる」系の設定を持っていても、接続元を検証せず無条件にヘッダーを返す実装なら使わない。信頼リストの判定より前に返す実装が実際にある。
- 信頼プロキシ設定は空でも明示的に適用する。既定が全プロキシ信頼の実装では、設定しないこと自体が「全部信じる」になる。
- 前段の構成に依存する値(ヘッダー名・プロキシの範囲)はコードに埋め込まず設定に出す。インフラを移したら設定を差し替えるだけにする。既定値は「何も信頼しない」にして、未設定環境で勝手に緩まないようにする。
実測してから決める
どのヘッダーが実際に届くか、どれが前段に上書きされるかは推測で決めない。外すと DoS 穴か偽装バイパスのどちらかになり、どちらの失敗も外からは見えない。
一時的な診断ログを入れて本番へ1リクエスト送り、接続元アドレス・転送ヘッダーの全要素・候補ヘッダーの実値を観測する。偽装した値を送ったときに前段が上書きするか素通しするかも同じ手順で確認する。得られるのは設定値であってコード構造ではないので、将来インフラを移しても同じ手順で測り直せる。
落とし穴
- ローカル開発では接続元アドレスが実クライアントになるため、この不具合は本番でだけ再現する。テストが通ることは、その環境で設定が効いていることを意味しない。
- 転送ヘッダーの「実クライアントは右から N 番目」という位置決め打ちは、前段のホップ数構成が変わると壊れる。
- 前段プロキシの公開 IP レンジを信頼リストに入れる方式は、レンジ更新の保守が永続的に残り、古いと黙って劣化する。
検証方法
- 信頼プロキシ範囲外からの偽装ヘッダーが無視され、接続元アドレスがキーになること(攻撃シナリオそのものの回帰テスト)。
- 信頼プロキシ範囲内なら信頼ヘッダーの値がキーになること。
- 設定が空なら転送ヘッダーを一切採用しないこと。
- 本番では、転送ヘッダーを毎回変えながら上限を超える回数を送り、拒否されること。偽装が有効なら要求ごとにバケットが分かれて拒否されない。