来年へ向けて年末年始でセキュリティ分野を学習し、年明けにウェブ・セキュリティ基礎試験(通称:徳丸基礎試験)を受験することにしました。そこで勉強した内容を備忘録としてまとめます。今回はXSSについてです。
XSSとは何なのか?
XSS(Cross Site Scripting)とは、攻撃者が悪意のあるプログラムを特定のWebページに仕込むことができてしまう脆弱性です。プログラムはJavaScriptで書かれることが一般的で、ユーザーのブラウザ上で動作します。これだけじゃよく分からないと思うので、具体例で説明します。続きをお読みください。
XSSはどこでどうやって発生するのか?
XSSはユーザーの入力が文字列ではなくHTMLタグやJavaScriptとして認識されることで発生します。なのでメッセージの投稿やコメント、検索欄などユーザーがデータを入力可能なあらゆる箇所が攻撃対象となりえます。たとえば攻撃者がX(旧Twitter)で以下のテキストを投稿したとします。
もしX(旧Twitter)がXSSの脆弱性を抱えている場合、文字列として入力した<script>alert('XSS');</script>
がプログラムだと認識されてしまい、このツイートを閲覧した全てのユーザーの画面でスクリプトが実行されるのです。(※もちろんXにはそんな脆弱性はありません)
XSSが起きてしまったらどうなるのか?
この画像の例ではアラートメッセージが表示されるだけですが、スクリプトを巧妙に作り込めばセッションやクッキーの情報を流出させたり、アカウントを乗っ取ってデタラメな投稿をすることも可能です。スクリプトはブラウザ上で動作するので、偽サイトへの誘導や悪質なソフトウェアのインストールなどなんでもできてしまいます。(※XがXSSの脆弱性を抱えていると仮定した話です。Xにはそんな脆弱性はありません)
XSSはどうやったら防げるのか?
XSSを防ぐには悪意のある入力をフィルタリングすることと、画面への出力をエスケープすることが極めて重要です。
①悪意のある入力をフィルタリングする
まずは第1段階として、悪意のある入力がそもそもDBに登録されてしまうことを防ぎます。
その際は特定の入力を禁止するのではなく、特定の入力だけを許可することが重要です。禁止する方式だとイタチごっこになってしまい防御が不完全になってしまうからです。たとえばscript
という文言を除去する方式だった場合、最初は有効かもしれませんが次に<scrscriptipt>
と入力されてしまうとフィルタ適用後に<script>
が残ってしまいます。一方で許可形式であれば事前に定義した特定のタグ以外を遮断することでフィルタが正常に機能します。
②画面へ出力される文字列をエスケープする
第2段階として、不適切な入力が①のフィルタリングをくぐり抜けた可能性に備えて、出力される文字列をエスケープすることも重要です。エスケープ処理によって、ブラウザが<script>alert('XSS');</script>
をプログラムではなく「<script>alert(‘XSS’);</script>」という文字列として認識してくれるからです。
※エスケープとはプログラミング言語にとって特別な意味を持つ文字や記号を、別の文字に置き換えることです。HTMLやJavaScriptにおける「<
」や「>
」が該当します。具体的には以下のように変換されます。
&
→ &"
→ "'
→ '<
→ <>
→ >
その結果、<script>alert('XSS');</script>
はブラウザ上では「<script>alert('XSS');</script>」と変換され、スクリプトは実行されずに無害な文字列として扱われます。
RailsにおけるXSS対策
RailsではXSS対策として標準でいくつかの機能が用意されています。
①自動エスケープ
RailsはデフォルトでERBテンプレートにおいてHTMLを自動的にエスケープします。<%= %>
を使用していればデフォルトでエスケープしてくれます。次の例では@message.content
に悪意のあるスクリプトが含まれている想定ですが、問題は起きません。
# コントローラー
class MessagesController < ApplicationController
def show
@message = Message.find(params[:id])
end
end
# ビュー (show.html.erb)
<h1>Message</h1>
<p>
<%= @message.content %>
</p>
②sanitizeメソッド
文字列に含まれるHTMLタグのうち一部だけを許可したい場合、Railsのsanitize
メソッドを使います。
たとえば以下のように記述することでRailsのデフォルトで許可されているHTMLタグ(<strong>
, <em>
, <a>
)以外は削除されます。その結果、XSS対策をしつつ文字列「XSS!」にはCSSが適用された状態で表示されます。
# XSS対策をしつつ'XSS!'にだけstyleを付与した
<%= sanitize('Hello, <strong style="color: #f00;">XSS!<script>alert(\'XSS\');</script></strong>') %>
なおsanitize
メソッドの第2引数を指定することで、任意のHTMLタグを許可することも可能です。
# h2タグの入力を許可する
<%= sanitize('<h2>Hello, XSS!</h2>', tags: %w(h2)) =>
一般的なユーザー入力には前述のように<%= =>
を使用するのが基本です。特定のHTMLタグを許可する必要がある場合(例えば、リッチテキストエディタからの入力を扱う場合)にはsanitize
メソッドを使用します。
まとめ
XSSの概要とRailsにおける対策方法について理解できました。
コメント