自己紹介
初めまして、株式会社シーエー・アドバンス技術統括本部の@miya_zatoです。
もともとアプリのデバッガーとして働いていましたが、セキュリティエンジニアを目指して異動をしました。
簡単な内容とはいえ、まともに記事を書くのも初めてですが、よろしくお願いいたします。
この記事について
今回は、xssの種類と、発生の仕方について書こうと思います。
これは、僕がセキュリティの研修中に理解がなかなかできずに躓いた事の1つです。
もし同じように理解が難しいと思った方のためになれたら嬉しいです。
XSSについて
クロスサイト・スクリプティング(cross site scripting)といいます。
本題から少しだけそれますが、「CSSでは?」という疑問に5分間くらい打ちひしがれた事があります
wikiでは、
略称はXSS。かつてはCSSという略称も使われていたが、Cascading Style Sheetsと紛らわしいのでこの略称はあまり使われなくなった
とあり、CSSと紛らわしいので区別された背景がありました。
昔の情報や書籍ではCSSと記されている可能性はあるので頭のすみっこで覚えて置くと良いかもしれません。
本題に戻ります。
xssとは、脆弱性のあるWebサイトに対して、悪意のある不正なスクリプトを挿入して、サイトに訪れたユーザーに実行させ、サイトではなくユーザーに対して被害がある攻撃です。
xssはユーザーから受け取った値をWebページに表示する処理に不備があると発生します。
例としては、
- ショッピングサイトで、「羅生門 の検索結果」など、ユーザーが検索したワードを表示
- 「こんにちは!○○さん!」のようなユーザーに挨拶を表示する所
- ショッピングサイトで、最新の商品を並べている表示から検索結果の商品に表示が書き換わる所
となります。
とあるショッピングサイトに商品を検索する機能があり、検索結果として「○○ の検索結果」と表示するページがある場合、ユーザーが入力した値を出力する「○○」の部分がエスケープされていなかった時に、タグ文字が有効な状態で画面に表示されてしまいます。
タグ文字とは、ブラウザが表示する時に使う文字列です。(<h1>あいうえお</h1>なら、「ああああ」を見出しとしてブラウザが表示する)
結果、「<script>alert("xss")</script>」で検索した時に、javascriptのタグが有効な状態でブラウザで表示されるので、画面にアラートが表示されてしまいます。
例えば、xssと表示されるだけであれば問題ないですが、「Cookieの情報を攻撃者にメールで送信する」のような処理であった場合、セッションIDを攻撃者に知られてしまい、セッションハイジャック(アカウントの乗っ取り、不正アクセス)に繋がります。
また、これから下記で紹介するのですが、「urlを踏んだらxssが発生する」のような発生をする時に、他人のブラウザでjavascriptを動作させてしまう事が可能になります。
※ソースにjavascriptからCookieの情報を読み込ませない記述が入っていればCookieの情報は抜かれないので、xssが成功したら上記の攻撃が必ず成立するわけではありません。
実はxssには、反射型、蓄積型、DOM-basedの3種類が存在し、最初に例として出した
- ショッピングサイトで、「羅生門 の検索結果」など、ユーザーが検索したワードを表示
- 「こんにちは!○○さん!」のようなユーザーの挨拶を表示する所
- ショッピングサイトで、最新の商品を並べている表示から検索結果の商品に表示が書き換わる所
上記3つは同じxssでも全て種類が違います。
ここが僕が躓いた所であり、xssの大切な所でもあります。
1個づつ解説していきます。
反射型xss
反射型xssとは、攻撃者が用意したurlにアクセスした時に発生するxssの事です。
ショッピングサイトで、「羅生門 の検索結果」など、ユーザーが検索したワードを表示
この例が反射型xssなのですが、
検索ワードをパラメータで持っていて
https://miya-zato-shopping/search?word=羅生門
↑のurlにアクセスする事で「羅生門 の検索結果のページ」にアクセスできたとします。
このwordのパラメータにスクリプトタグを含めた、
https://miya-zato-shopping/search?word=<script>alert(1)</script>
にアクセスしたユーザーの検索結果画面でxssが発生します。
これが反射型xssです。
蓄積型xss
蓄積型xssとは、webサイトが蓄積しているコンテンツの中に含ませるxssの事です。
「こんにちは!○○さん!」のようなユーザーに挨拶を表示する所
この、「○○さん!」のようなユーザー名の情報は、先ほどの反射型の例のようにパラメータで持っているのではなく、WebサイトのDBに保存されているデータです。
ユーザー名を、「<script>alert(1)</script>」で登録しておいて、他のユーザーがプロフィールを見に来た時にxssが発生します。
これが蓄積型xssです。
DOM-based xss
こいつが曲者です。
こいつは、ブラウザ上動作するJavascript上で問題があった時に発生します。
ショッピングサイトで、最新の商品を並べている表示から検索結果の商品に表示が書き換わる所
この場合だと、検索ワードを入力し、「検索」のボタンを押し、javascriptで最新の商品の表示から検索結果の商品に書き換えた時、商品名にタグ文字があり、エスケープ漏れがあった場合に、xssが発生します。
※書き換えるだと説明不足でした、正しくは「javascriptがDOMを生成する時」です。
また、反射型、蓄積型と違いこちらはクライアント側の処理で発生するので、サーバーを経由しない特徴もあります。
(例えが悪いのもありますが...)
今回の例だと蓄積型xssに似ています。
こいつは、ブラウザで動作するJavascript上で問題があった時に発生します。
こいつの問題はここです。
凄く恥ずかしいですが僕は研修中にこのDom-based xssが発生する機能を作りました、その凄く恥ずかしいソースを一部載せます。
$('#search').submit(function(){
event.preventDefault();
var $form = $(this);
$.ajax({
url: '//localhost/api/booksearch',
type: "GET",
data: $form.serialize()
}).done(function(data){
$('#result').empty();
for (var i=0; i<data.length; i++) {
$('#result').append('<tr>');
$('#result').append('<td>' + data[i].title + '</td>');
~以下略~
(※僕は書籍を管理するサイトを研修で作成しました)
何をしているかというと、既に一覧として表示されている本のタイトルを、検索で引っかかった本のタイトルに書き換えようとしています。
この、「書き換える処理」はjavascriptで行っており、ここでエスケープ漏れがあり、xssが発生しています。
$('#result').append('<td>' + data[i].title + '</td>');
↑この部分です、javascriptでエスケープ処理を入れる時は自分で関数を用意する必要があります。
<script>
function htmlEscape(str) {
if (!str) return;
return str.replace(/[<>&"'`]/g, function(match) {
const escape = {
'<': '<',
'>': '>',
'&': '&',
'"': '"',
"'": ''',
'`': '`'
};
return escape[match];
});
}
</script>
↑のように、タグ文字をエスケープする関数を作成し、
$('#result').append('<td>' + htmlEscape(data[i].title) + '</td>');
作成した関数を通してエスケープした文字を表示させるようにします。
このように、ブラウザ上で動作するjavascriptに問題がある事で発生するxssがdom-based xssです。
また、javascript上で発生するので反射型、蓄積型と違って、必ずサーバー側の処理を経由する必要がなく、サーバーにログが残らなかった場合に発見が難しい特徴があります。
まとめ
同じxssですが種類が分かれていて最初は混乱していましたが、
どのシチュエーションで発生するか
で考えると整理しやすくなりました。
反射型:受け取ったパラメータをブラウザに表示する時に、エスケープされていない
蓄積型:登録したデータをブラウザに表示する時に、エスケープがされていない
Dom-based:javascriptでブラウザの表示を書き換える時に、エスケープされていない
もっと細かく色々な状況があると思いますので、今後もxssの学習は続けて行こうと思います。
終わり
今回はxssについて記事を書きましたが、これから機会があれば他の脆弱性についても書いて行きたいと思います。
知識不足な所が多いと思うので、ご指摘があればコメントに願いします。