SSTエンジニアブログ

SSTのエンジニアによるWebセキュリティの技術を中心としたエンジニアブログです。

Stored(蓄積型)-XSSの危険性

はじめに

 初めまして。2019年度入社のTです。
 SSTにエンジニアとして入社して1年が経ち、私も新卒の方を迎える立場になってしまいました。

目的

 今回はエンジニアブログ初執筆ということで、私が去年研修中に学んだ題材を元に、特に印象的だった脆弱性について解説していきたいと思います。
 Webアプリケーション開発者の方やそれを管理・運用する方に対して脆弱性の解説をする上で特に重点を置く項目の一つに「想定される被害」が挙げられます。

 しかし、Webアプリケーションのセキュリティに焦点を当てた詳細な技術書や記事でもない限り、脆弱性ごとの「想定される被害」の表現は抽象的になりがちです。例えば今回扱うXSSにありがちなのは

  • 「攻撃者の用意した任意のJavaScriptが被害者ユーザのブラウザ上で実行される可能性があります」
  • 「Webページが攻撃者の用意した悪意あるHTMLコンテンツにより改ざんされる可能性があります」

等です。技術者や開発に携わった方であれば上記の2点がいかに危険な状態か理解できると思われますが、あまり馴染みがない方は「??つまりどういうこと?何がいけないの?」「具体的にどんな被害がでるの??」といった疑問がわくでしょう。

 今回はそういった疑問に答えるため、「Stored(蓄積型)-XSS」という脆弱性の危険性について専門外の方にも伝わるように具体例を交えて解説していきます。

解説

予備知識① XSSについて

 XSS(クロスサイトスクリプティング)は、Webアプリケーション上でユーザからの入力に対する処理が不十分であることが原因で起こる脆弱性です。この脆弱性が存在すると、一例として以下の図のようにWebサイトにアクセスした正規のユーザ(被害者)が、攻撃者の意図した処理を実行させられてしまいます。

f:id:yu_material256:20200408183456p:plain
Reflected(反射型)-XSS

  1. まず攻撃者は罠サイトを作成し、SNSやスパムメール等を利用してそのサイトに誘導します。
  2. 次に被害者が誘導され罠サイトにアクセスします。
  3. 被害者が罠サイト上の悪意あるコンテンツにアクセスすることで、XSSの脆弱性がある標的サイトに対して、攻撃値をパラメータに含むリクエストが送信されます。
  4. その結果、攻撃値が被害者のブラウザ上で実行されてしまいます。

 一連の流れはXSSの中でも一般的な「Reflected(反射型)-XSS」と呼ばれるXSSの被害の一例です。危険性についてもう少し掘り下げてみます。

「攻撃者の用意した任意のJavaScriptが被害者ユーザのブラウザ上で実行される可能性があります」

 前項のこの説明を元にもう少し掘り下げて説明します。まず、JavaScriptとはブラウザ上で実行されるプログラミング言語の一種であり、今この記事をご覧になっている方のブラウザ上でも(恐らく)動いています。それで何ができるの?と言われれば「JavaScriptで出来ることは大体できます」というのが答えです。これでもまだ説明としては不十分ですね。悪い人の目線に立って話すのであれば、この「JavaScriptで出来ること」には、「このJavaScriptを実行した人の大事な情報を悪い人に送信する」ことも含まれます。

 もちろん、JavaScriptに罪があるわけではありません。現にこの記事のように我々にリッチなWebコンテンツを提供してくれています。問題は「悪い人(攻撃者)が用意した悪意のあるJavaScript」です。もしここで偉い人が「よし、明日からすべてのサイトでJavaScriptの使用を禁止しよう!」と決定を下したところで、根本的な解決には至りません。XSSによる脅威の一例は「悪い人(攻撃者)が用意した悪意のあるHTMLコンテンツ(JavaScriptを含む)」が、XSSの脆弱性のあるWebアプリケーションを通して善良なユーザのブラウザ上で(半強制的に)有効になってしまうことにあります。
※XSSによる被害は不正なJavaScriptの実行に限るものではありません。ここではあくまで一例を紹介しています。

予備知識② セッション管理について

 皆さんはショッピングサイトでお買い物をしたことはあるでしょうか?
 一度メールアドレス(ユーザID)とパスワードを入力してサイトにログインすれば、しばらくの間再度それらの情報を入力しなくても商品の購入や購入履歴の閲覧などが可能になります。(一部の重要な処理を除く)しかし、Webサーバが通信に利用するHTTPというプロトコルはステートレス(状態を保持しない)であるため、そのままでは前述のショッピングサイトのようにログイン状態の維持はできず、ページを巡回するたびに認証情報を入力しなくてはいけなくなります。

 そこで利用されるのが「セッション管理」の仕組みです。セッション管理は状態が維持されないHTTP通信上で、ログインセッション等の維持のために利用されます。例えば、ユーザがログイン画面で自身の認証情報を入力すると、期限付きの「セッションID」と呼ばれる値が発行され、ユーザのブラウザに保存されます。セッションIDはよく病院等の「整理番号」に例えられます。 以下の図で説明します。

f:id:yu_material256:20200408215445p:plain
セッション管理

  1. まず最初に、Webサイトのユーザが「ユーザIDは○○○、パスワードは〇△×でログインします」とWebサーバに対してリクエストを送信します。
  2. するとWebサーバは「認証に成功しました。セッションIDは×××...です」という内容のレスポンスを返し、認証情報と共にセッションIDをサーバ上に保存します。
  3. Webサーバからユーザに送られたセッションIDは、(一般的には)ユーザのブラウザの「クッキー(Cookie)」という領域に保存され、次回以降そのサイトにアクセスする際に送信されます。
  4. 次にユーザが「自分の購入履歴が見たい」という内容のリクエストをWebサーバに送信したとします。この時に3.で述べたようにセッションIDも同時に送信されます。
  5. Webサーバは4.のリクエストを受け取り、同時に送信されたセッションIDの値を元に保存しておいたユーザの情報を探し、リクエストを送信した本人の購入履歴をレスポンスで返します。

 今回是非意識して頂きたいのは「セッションIDの重要性」です。セッションIDは期限付きとはいえ認証情報(ユーザID, パスワード)に代わり得る値です。よってセッションIDが漏えいすると、攻撃者により1~5の手順の内4~5の手順が踏まれてしまい、攻撃者によるなりすましが成立してしまいます。

本題 (Stored(蓄積型)-XSSについて)

 Stored(蓄積型)-XSS*1はXSSの一種です。攻撃者が登録した攻撃値がデータベース等に保持されることで、そのコンテンツにアクセスしたユーザのブラウザ上で攻撃値が実行されます。前述のReflected(反射型)-XSSと呼ばれるタイプのXSSに比べ、被害者を罠サイトに誘導する手間がかからない等の理由で被害が発生しやすいとされています。

f:id:yu_material256:20200408225123p:plain
Stored(蓄積型)-XSS

 今回は、私が去年の研修で作ったSNS「陰(いん)スタグラム」を利用して解説していきたいと思います。
 まず陰スタグラムについてですが、以下のような構成になっています。

種別 バージョン等
OS Ubutnu 18.04
言語 PHP 7.2.19
Webサーバ Apache 2.4.29
データベース MySQL 14.14


 陰スタグラムは、「キラキラ『じゃない』画像投稿系SNS」をテーマに作成されたWebアプリケーションです。機能として

  • 実在のSNSと同様に会員登録・ログイン機能
  • コメントと共に実際に写真を投稿・編集・削除する機能
  • ユーザのフォロー機能・タイムライン機能
  • 投稿への返信機能・「いいね!」「陰ね!」ボタン
  • キラキラな写真を投稿をしたユーザに対するアカウントBAN機能(管理者のみ)

等が挙げられます。

f:id:yu_material256:20200413212043p:plain
「陰スタグラム」

 実はこの陰スタにはXSSの脆弱性が存在します。既に投稿済みの内容の編集処理の際に送信される入力内容がエスケープされていないため、ここで入力されたHTMLタグはタイムライン(投稿一覧画面)上で有効になってしまいます。

 では実際にどういった被害が出るのか見ていきます。

 攻撃者はユーザの情報を待ち受けるサーバをあらかじめ用意しておきます。  

f:id:yu_material256:20200413214621p:plain
攻撃者の用意したサイト

 そして攻撃者は陰スタ上でアカウントを作成し、適当な投稿をした後にその投稿を攻撃値を含むものに編集します。すると、編集が適用されタイムライン(投稿一覧)上に表示されます。

f:id:yu_material256:20200413212130p:plain
攻撃者のブラウザ上で、自身の投稿内容を攻撃値を含むものに編集する

 編集の際に送信・登録した攻撃値に関しては悪用防止のために修正しています(赤い部分)が、その攻撃値が指示する内容は「この投稿(攻撃値)をブラウザで読み込んだユーザのセッションIDの値を、攻撃者の用意したサーバに送信する」という内容です。これによりログインした他ユーザがタイムライン(投稿一覧)画面を読み込むとそのユーザのブラウザ上で攻撃値が実行され、前述の攻撃値が指示する処理を強制されます。

 今回はわかりやすくするために、管理者権限を持つユーザでログインしてタイムライン(投稿一覧)画面にアクセスしてみます。

f:id:yu_material256:20200413212205p:plain
被害者(adminユーザ)が陰スタにログインすることで、被害者のブラウザ上で攻撃値が実行される

 すると攻撃者の用意したサーバに対して、管理者のセッションIDを含む内容が送信されているのが確認できます。

f:id:yu_material256:20200413214645p:plain
攻撃者のサイトに、管理者のセッションidが送信されていることが分かる

 この情報を元に、攻撃者がリクエストを改ざんして陰スタにアクセスすると、管理者権限を持つユーザのアカウントの認証情報を知らないにもかかわらず、管理者としてアクセスできてしまうことが確認できます。

f:id:yu_material256:20200413212327p:plain
攻撃者のブラウザ上から、admin(管理者)ユーザとしてログインできていることが分かる

 admin(管理者権限を持つユーザ)のみが管理できる管理画面が表示され、ユーザのメールアドレス等の情報が流出してしまっていることが確認できます。他にも、管理画面の操作もできてしまうため、任意のアカウントをBAN(停止)や強制退会させたりとやりたい放題です。

f:id:yu_material256:20200410002837p:plain
攻撃者のブラウザ上から管理画面にアクセスできてしまっている

 他にも、タイムライン画面は全てのユーザがログイン成功後に強制的にアクセスさせられてしまうため、adminユーザに限らず他のユーザがいつも通りログインするとそのユーザのブラウザ上でも同様に攻撃値が実行され、セッションIDの値が攻撃者のサイトに送信されてしまいます。よって、このサイトを利用する多くのユーザがアカウント乗っ取り等の被害にあう可能性があります。

 XSSの脆弱性が悪用された場合の危険性の一例について、ご理解頂けたでしょうか?

※ これらはあくまでXSSの脆弱性が存在する場合の被害の一例であり、XSSの脆弱性があるWebアプリケーション全てにおいてこのような攻撃が実行される訳ではありません

対策

 では前述のような攻撃をさせないためにはどういった対策を施せばいいのかについてですが、対策についてはそのWebアプリケーションを構成する言語やフレームワークにより様々です。今回の場合、陰スタはPHP製のため、以下のような修正が有効です。

根本的な対策

  • 根本的な対策として、入力内容を表示する際にHTMLタグを構成する文字・記号を無効化することが挙げられます。
  • htmlspecialchar()関数を用いて入力内容をエスケープ(サニタイズ)する処理が有効です。
<?php
//~省略~

//POSTパラメータ「param」を変数$paramに格納する。
$param = $_POST["param"];

//変数$paramをエスケープした値を変数$escapedParamに格納する。
//以降の処理では$escapedParamを利用する。
$escapedParam = htmlspecialchars($param, ENT_QUOTES, 'UTF-8');

//~省略~
?>
  • こうすることでダブルクォート等がエスケープされ、悪意あるHTMLコンテンツの生成やscriptタグ等によるJavaScriptの実行を防ぎます。
<s>value</s>
//↓エスケープ後
&lt;s&gt;value&lt/s&gt;

副次的な対策

  • CookieにHttpOnly属性を付与すると、JavaScript等からCookieに含まれるセッションIDの値を参照できなくなります。
  • PHPの場合はphp.iniでsession.cookie_httponlyをOnにすることで有効になります。
session.cookie_httponly = On

まとめ

 今回は「Stored(蓄積型)-XSSの危険性」というテーマで執筆させて頂きました。XSSの中でも特に危険度が高い一例についてでしたが、皆様のご理解の一助となれば幸いです。

     

*1:持続型XSS、Persistent-XSSとも呼ばれます。