XSSとその対策

Sinatraのメモアプリの終了条件であるXSS対策について調べました。

結論

文字コード指定とエスケープ(サニタイジング)を行うとXSS対策できる

XSSとは

悪意あるクライアントサイドのコードをウェブサイトに挿入するセキュリティ攻撃です。

引用: Cross-site scripting (クロスサイトスクリプティング) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN

  • クロスサイトスプリプティング(Cross-site scripting)
  • Cookieやセッショントークンの不正取得、HTMLコンテンツの書き換えなどが行われてしまう

XSSの簡単な例

入力フォームに直接JavaScriptスクリプトを埋め込み意図しないアラートを表示させる

  1. 下記スクリプトを入力フォームに入力し送信
  2. アクセス時にXSS!! がアラートで表示される
<script>alert('XSS!!')</script>

XSSの原因

ユーザ入力が文字列ではなくHTMLタグやJavascriptとして認識されること

XSS対策

文字コード指定

Content-Typeフィールドで文字コードの指定を省略した場合、攻撃者が、この挙動を悪用して、故意に特定の文字コードをブラウザに選択させるような文字列を埋め込んだ上、その文字コードで解釈した場合にスクリプトのタグとなるような文字列を埋め込む可能性があります。

Content-Typeの出力時にcharsetを省略することなく、必ず指定することが有効です。ウェブアプリケーションがHTML出力時に想定している文字コードを、Content-Typeのcharsetに必ず指定してください。

引用: 安全なウェブサイトの作り方 - 1.5 クロスサイト・スクリプティング:IPA 独立行政法人 情報処理推進機構

Sinatraを使った今回の場合だと全ページ共通で表示に仕様しているlayout.erb 内のhead タグ内で文字コードUTF-8 で指定する

<!DOCTYPE html>
<html lang="ja">
<head>
  <mata charset="UTF-8"> <!-- ここで文字コードを指定 -->
    <title>Memo App</title>
    <link rel="stylesheet" href="/normarize.css">
    <link rel="stylesheet" href="/style.css">
</head>
<body>
</body>
</html>

エスケープ(サニタイジング)

Rubyの標準ライブラリであるcgi を使ってHTMLタグとJavaScriptを文字列に置換します

  1. cgiライブラリ読込
  2. フォーム等のユーザーが入力した文字列の表示部をCGI.escapeHTML メソッドでエスケープする
require 'cgi'
=> true

string = ['"', "'", '&', '<', '>']
=> ["\"", "'", "&", "<", ">"]
string.each {|i| puts CGI.escapeHTML(i)}
&quot;
&#39;
&amp;
&lt;
&gt;
=> ["\"", "'", "&", "<", ">"]

html = '<p>テキスト</p>'
CGI.escapeHTML(html)
=> "&lt;p&gt;テキスト&lt;/p&gt;"

script = '<script type="text/javascript">script</script>'
CGI.escapeHTML(script)
=> "&lt;script type=&quot;text/javascript&quot;&gt;script&lt;/script&gt;"

フォーム等の入力部ではなく、ユーザーが入力した文字列の出力部でエスケープすることがポイントです。

まとめ

これまでセキュリティはあまり意識して学習してこなかったが、ちょっとずつ対策を覚えていきたい。

参照

安全なウェブサイトの作り方 - 1.5 クロスサイト・スクリプティング:IPA 独立行政法人 情報処理推進機構

CGI.escapeHTML (Ruby 3.1 リファレンスマニュアル)

素の Ruby で HTML エスケープするなら cgi/escapeが最強 - Qiita