【実務・中級編】Permissions Policy (旧Feature-Policy)によるブラウザ機能の制限 – アプリケーションセキュリティ & 安全な開発防御ガイド

XSSのその先へ:Permissions Policyでブラウザを「要塞化」する実践ガイド

「XSS対策はサニタイズ(エスケープ)さえしていれば安心」――そう思っているなら、そろそろ考えを改めるべきだ。

現場でインシデント対応をしていると痛感するが、現代のWebアプリケーションにおいて、XSSは単なる「アラートボックスを表示させるいたずら」ではない。攻撃者は、ブラウザが持つ強力なAPI(カメラ、マイク、位置情報、支払機能など)を奪い取り、ユーザーのプライバシーを根こそぎ盗み出すことを狙っている。

もし、君たちが開発している管理画面や会員サイトに、たった一箇所でもクロスサイトスクリプティング(XSS)の脆弱性が残っていたらどうなるか。攻撃者は、ユーザーのブラウザを使ってカメラを勝手に起動し、盗撮を行うことも、マイクで会議を盗聴することも可能だ。

今日は、そんな「ブラウザAPIの悪用」を根絶するための最後の砦、Permissions Policyについて、実戦的な実装レベルで解説する。

—

1. なぜ「エスケープ」だけでは足りないのか?

XSSには反射型、格納型、DOM型の3種類があるが、どれであっても攻撃のゴールは「ブラウザ上で悪意あるスクリプトを実行すること」に変わりはない。

仮に、巧妙なDOM型XSSを突かれ、攻撃者のスクリプトが実行されたとする。その時、Permissions Policyが設定されていなければ、攻撃者は以下のようなAPIを自由に呼び出せる。

  • navigator.geolocation.getCurrentPosition()(現在地特定)
  • navigator.mediaDevices.getUserMedia()(カメラ・マイクの権限奪取)
  • paymentRequest(決済機能の不正操作)

これらはOSレベルでユーザーの許可を求めることもあるが、ソーシャルエンジニアリングを組み合わせられれば、ユーザーは気づかぬうちに「許可」を押させられる。これを防ぐには、「ブラウザ自体に、その機能を使う権利があるかを事前定義しておく」というOS的な発想が必要だ。

—

2. Permissions Policyの「守りの要」

Permissions Policy(旧Feature-Policy)は、HTTPレスポンスヘッダーとして送信することで、特定のオリジン(ドメイン)以外からの強力なブラウザ機能の利用を、ブラウザエンジンレベルで拒否する仕組みだ。

推奨される実装設定(Nginx)

設定はアプリケーションコードに書くよりも、Webサーバー(Nginx等)で一括制御するのが運用の鉄則だ。以下の設定を nginx.conf に追加してほしい。

HTTPレスポンスヘッダーにPermissions-Policyを追加
不要な機能を徹底的に「なし(none)」に制限する
add_header Permissions-Policy “geolocation=(), microphone=(), camera=(), payment=(), usb=(), display-capture=()”;

解説:

  • =() とすることで、そのサイト内(およびiframe内)のすべてのオリジンに対して機能を禁止している。
  • もし特定のドメイン(例えば決済用のサードパーティ製iframe)にのみ許可を与えたい場合は、camera=(self "https://trusted-payment.com") のように記述する。

—

3. アプリケーション側での制御(PHP / Python)

インフラ層だけでなく、アプリケーション側でも動的に設定したいケースがあるだろう。その場合は、フレームワークのミドルウェア層で付与するのがベストプラクティスだ。

PHP (Laravel等のミドルウェア例)

// ミドルウェアでヘッダーを付与する
public function handle($request, Closure $next)
{
$response = $next($request);

// カメラ、マイク、ジオロケーションをすべて遮断
$response->headers->set(‘Permissions-Policy’, ‘geolocation=(), camera=(), microphone=()’);

return $response;
}

Python (Flask / FastAPI)

FastAPIでの実装例
@app.middleware(“http”)
async def add_security_headers(request, call_next):
response = await call_next(request)
# セキュリティリスクを最小化する設定
response.headers[“Permissions-Policy”] = “geolocation=(), camera=(), microphone=(), usb=()”
return response

—

4. 現場のシニアエンジニアからの忠告

Permissions Policyを導入する際、初心者が陥りがちな罠が2つある。

1. 「とりあえず全部許可()しておこう」という怠慢

  • これでは意味がない。常に「最小権限の原則」に従い、空の括弧 () で全拒否し、必要なものだけを明示的に開放する姿勢を徹底すること。

2. サードパーティ製iframeの考慮漏れ

  • 外部の広告タグやチャットボットがカメラやマイクを要求するケースがある。導入前にブラウザのデベロッパーツール(Networkタブ)を開き、どの機能がブロックされているかコンソールエラーを確認する「儀式」を怠らないこと。

最後に:セキュリティは「多層」で考える

Permissions Policyは、XSSを「修正」するものではなく、XSSが成功した後の「被害を食い止める」ための盾だ。

堅牢な開発とは、「脆弱性を生まない努力(入力チェック・出力エスケープ)」と「万が一の際に被害を局所化する努力(CSPやPermissions Policy)」の両輪で成り立つ。

今日からこのヘッダーを導入すれば、君たちのアプリケーションは、攻撃者にとって「非常にコストの高い、割に合わない標的」へと姿を変えるはずだ。技術は人を守るためにある。さあ、今すぐサーバーの設定ファイルを開いてくれ。

コメント

タイトルとURLをコピーしました