X-Frame-Options: クリックジャッキングからサイトを守る
これは何?
X-Frame-Optionsは、あなたのWebサイトが他のサイトの<iframe>タグ内に埋め込まれることを制御するHTTPレスポンスヘッダです。このヘッダを設定することで、悪意のあるサイトがあなたのページをiframe内に表示し、ユーザーを騙す「クリックジャッキング」攻撃を防ぐことができます。
2009年にマイクロソフトが提案し、現在ではすべての主要ブラウザでサポートされています。設定は非常にシンプルで、3つの値(DENY、SAMEORIGIN、ALLOW-FROM)のいずれかを指定するだけです。
なぜ重要なのか
X-Frame-Optionsを設定しないと、攻撃者はあなたのサイトを透明なiframe内に配置し、その上に偽のUIを重ねることができます。ユーザーは攻撃者のサイトを見ていると思いながら、実際にはあなたのサイト上のボタンをクリックしてしまいます。
例えば、銀行サイトの送金ボタンを透明なiframe内に配置し、「無料プレゼントをクリック」という偽のボタンを重ねることで、ユーザーに意図しない送金操作をさせることが可能になります。ソーシャルメディアサイトでは、ユーザーに知らないうちに「いいね」ボタンを押させたり、フォローさせたりする攻撃が報告されています。
攻撃の仕組み
クリックジャッキング攻撃は以下の手順で実行されます:
- iframe埋め込み: 攻撃者は自分のサイトに被害サイトをiframeで埋め込みます
- 透明化: CSSで
opacity: 0.0001を設定し、iframeをほぼ完全に透明にします - UI偽装: iframe上に攻撃者が用意した魅力的なボタンやリンクを配置します
- 位置調整: z-indexとpositionを使い、偽のUIと実際のボタンを正確に重ねます
- 誘導: ユーザーが偽のUIをクリックすると、実際には透明なiframe内のボタンをクリックしてしまいます
<!-- 攻撃者のサイト例 -->
<style>
#victim-iframe {
position: absolute;
width: 500px;
height: 300px;
opacity: 0.0001; /* ほぼ透明 */
z-index: 2;
}
#decoy-button {
position: absolute;
top: 150px;
left: 200px;
z-index: 1;
}
</style>
<iframe id="victim-iframe" src="https://bank.example.com/transfer"></iframe>
<button id="decoy-button">無料でiPhoneをゲット!</button>
実際の被害事例
Facebookの「いいね」ジャッキング(2010年): 複数のサイトでFacebookの「いいね」ボタンを透明なiframe内に配置し、ユーザーに意図しない「いいね」を押させる攻撃が横行しました。これにより、スパムページが急速に拡散されました。
Adobe Flash設定の変更(2008年): Adobeが運営するFlashの設定ページを悪用し、ユーザーのWebカメラとマイクへのアクセス許可を無断で取得する攻撃が報告されました。この事例がクリックジャッキングという用語が広まるきっかけとなりました。
Twitter「Don't Click」ワーム(2009年): Twitterのフォローボタンを透明にし、ユーザーに意図しないアカウントをフォローさせるワームが発生。数時間で数万のアカウントに感染しました。
Nyambushでの検出内容
Nyambushのセキュリティヘッダスキャンでは、以下の項目をチェックしています:
- X-Frame-Optionsヘッダの有無: ヘッダが設定されているか
- 設定値の妥当性: DENY、SAMEORIGIN、ALLOW-FROMのいずれかが正しく設定されているか
- CSP frame-ancestorsとの併用: より新しいContent-Security-Policyのframe-ancestorsディレクティブも設定されているか推奨
- 矛盾のチェック: 複数の設定が矛盾していないか(例: X-Frame-Options: DENY と CSP: frame-ancestors 'self')
スキャン結果では、設定されていない場合は「危険」、適切に設定されている場合は「安全」と表示され、具体的な推奨設定も提示します。
対策方法
Apache (.htaccess または httpd.conf)
# すべてのframeを拒否(最も安全)
Header always set X-Frame-Options "DENY"
# 同一オリジンのみ許可(自サイト内でのiframe利用がある場合)
Header always set X-Frame-Options "SAMEORIGIN"
Nginx
# すべてのframeを拒否
add_header X-Frame-Options "DENY" always;
# 同一オリジンのみ許可
add_header X-Frame-Options "SAMEORIGIN" always;
Next.js (next.config.js)
module.exports = {
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'X-Frame-Options',
value: 'DENY',
},
],
},
];
},
};
Express.js
const helmet = require('helmet');
app.use(helmet.frameguard({ action: 'deny' }));
// または
app.use(helmet.frameguard({ action: 'sameorigin' }));
WordPress (functions.php)
function add_security_headers() {
header('X-Frame-Options: SAMEORIGIN');
}
add_action('send_headers', 'add_security_headers');
CSP frame-ancestorsとの併用(推奨)
X-Frame-Optionsは古い仕様のため、より新しいContent-Security-Policyも併用することを推奨します:
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'
CSPのframe-ancestorsディレクティブは、X-Frame-Optionsよりも柔軟な制御が可能で、複数のドメインを許可することもできます。
まとめ
X-Frame-Optionsは、わずか1行の設定でクリックジャッキング攻撃から効果的にサイトを守ることができる重要なセキュリティヘッダです。設定漏れは攻撃者に悪用の余地を与えてしまいます。
推奨設定:
- iframeを使わないサイト:
X-Frame-Options: DENY - 自サイト内でiframeを使用:
X-Frame-Options: SAMEORIGIN - さらに強化: CSP
frame-ancestorsも併用
Nyambushのスキャンで設定状況を確認し、未設定の場合はすぐに対応しましょう。数分の作業で、サイトのセキュリティを大幅に向上させることができます。