XSS対策における < > の使用方法と、幾つかの質問 - PHPプロ!Q&A掲示板

4253

  • 0P

XSS対策における < > の使用方法と、幾つかの質問

質問日時 / 2014年2月26日 15:23 (最終編集:2月27日 04:28)    回答数 / 10件

Questioner:  gogoyangii  このエントリーをはてなブックマークに追加 

キーワード / XSS    <    >   

XSS対策における &lt; &gt; の使用方法と、幾つかの質問
一文加筆しました。

以下の質問についてご回答いただきますようお願いいたします。
前説が長いのですが、宜しくお願いいたします。

********前説始まり*********

PHPスクリプトで掲示板を作るとき、外部から入力された文字列に以下のようなコード(コード1)があるとします。
  1. コード1
  2.  
  3. <a href="http://www.seven01.xxx/?message=%3cscript%3ealert(document.cookie);%3c/script%3e">これは生スクリプトです</a>

ご存じのように、<a>タグのリンクをURLデコードすると
  1. http://www.seven01.xxx/?message=<script>alert(document.cookie);</script>
のようになり、そのままでは掲示板に表示したとき<a>タグが有効で、<script>タグでかこまれた内容が実行されます。
結果は、セッションネームとIDが表示されます。

対策としては、サイト外部から受け取るクエリーには関数htmlspecialcharsを使用して、HTMLエンティティーに変換しタグとしての機能を無効にし、文字列として受け取り、掲示板に表示します。

この場合コード1は以下のコード2に変換されています。
↓↓↓
  1. コード2
  2.  
  3. &lt;a href="http://www.seven01.xxx/?message=&lt;script&gt;ealert(document.cookie);&lt;/script&gt;"&gt;これは生スクリプトです&lt;/a&gt;

これで、掲示板に表示しても、ただの文字列として扱われ、<a>タグなどは機能せず問題なく扱うことができます。

当たり前のことを書くなと怒られそうですがご容赦ください。

******前説ここまで******


本題です。

少し掲示板の機能を増やして、外部から入力された文字列にルールを決めて、ハイパーリンクを表示できるように設定しようとしました。
例えば、以下のような形(コード3)です。
(遷移先と遷移元のページは同一ページで行っています。
この場合、遷移元 seven01.xxx/index.php → 遷移先 seven01.xxx/index.phpとなります。)
↓↓↓
  1. コード3 
  2.  
  3. [string(掲示板表示用)[URL(リンク設定用)]]

コード3の形式で入力された場合は、string(掲示板表示用)に隣のURLをハイパーリンクとして設定することとします。
以下のコード4を例とします。
↓↓↓
  1. コード4
  2.  
  3. [http://www.seven01.xxx/?message=%3cscript%3ealert(document.cookie);%3c/script%3e[これは生スクリプトです]]

これをそのままリンクを設定して表示すると、コード1と同じ表示になりますので、掲示板に表示されるコードは以下の通りです。
  1. <a href="http://www.seven01.xxx/?message=%3cscript%3ealert(document.cookie);%3c/script%3e">これは生スクリプトです</a>

これでは問題ですので、事前にHTMLエンティティーに変換します。
この時、URLデコードも行い %3c→&lt; %3e→&gt;に変換します。
変換した後のコードはコード5になります。
  1. コード5
  2.  
  3. <a href="http://www.seven01.xxx/?message=&lt;script&gt;alert(document.cookie);&lt;/script&gt;">これは生スクリプトです</a>

問題ないように思うのですが、リンクにマウスのカーソルをあわせると以下のように表示され、クリックするとダイアログが表示されます。
  1. http://www.seven01.xxx/?message=<script>alert(document.cookie);</script>

前説のような、セキュリティー対策についての解説はネットやPHP及びセキュリティーの参考書には多いのですが、本題のようなケースは余り見受けません。


以下、質問とお願いです。

*******質問とお願い******

質問1
本題のような例が、なぜ発生するか詳しくご存じであれば、解説をお願いします。

質問2
対策方法のご教示ください。

質問3
本題のような例が発生するブラウザの種類およびバージョンの一覧がわかれば助かります。

お願い
このような質問が、この「PHPプロ」のテーマからは、質問内容の幅が広く、私が欲張っているように思うのですが、初心者の人間からするとサイト構築中に流してしまい、問題に気づかないままセキュリティーホールを作成してしまう内容であると思います。

皆様何卒ご検討のほど宜しくお願いいたします。

この質問への意見の募集は締め切られ、ポイントは既に配分されました。
意見を投稿することはできますが、ポイントを受け取ることはできません。



ツリー一覧

┗A01To_aru_User2つの問題を混同されているように思いました。 ・
 ┗A01-1gogoyangiiTo_aru_User 様 ご回答ありがとうございました。
  ┗A01-1-1To_aru_UserInternetExplorer 8.0 Firefox 23.0 GoogleChrome 3
   ┗A01-1-1-1gogoyangii再度のご回答ありがとうございます。 ブラウザのバ
    ┗A01-1-1-1-1To_aru_Userはい、もちろんです。 (ポート80は別のアプリケーシ
     ┣A01-1-1-1-1-1gogoyangiiTo_aru_User様 いろいろお手数をおかけします。
     ┣A01-1-1-1-1-2gogoyangii追記です。 先ほどの回答の、追記を拝見しました。
     ┗A01-1-1-1-1-3gogoyangiiTo_aru_User様 私が間違っていました。 すみませ
      ┣A01-1-1-1-1-3-1To_aru_User了解しました。特にお気になさらないでください。
      ┗A01-1-1-1-1-3-2To_aru_User了解しました。特にお気になさらないでください。

回答一覧

並び替え:

A01 満足
answererTo_aru_User [2月26日 22:14] (最終編集:2月26日 22:19)

2つの問題を混同されているように思いました。

・外部から受け取るクエリ(message)の処理での対処
・ユーザーが入力した内容のマークアップ処理での対処

前者でも後者でもhtmlspecialchars関数によるエスケープを行っていれば脆弱性は発生しません。後者の問題を説明している過程で、前者でセキュリティ対策が行われていないような言い回しに感じられたのですが…

脆弱性がありそうなIE8でも試しましたが、

  1. <a href="http://www.seven01.xxx/?message=&lt;script&gt;alert(document.cookie);&lt;/script&gt;">これは生スクリプトです</a>

にマウスポインタを合わせてもエンティティをデコードしたものがステータスバーに表示されるだけで、それをクリックしただけで(ページ遷移せずに)JavaScriptが実行されることはありませんでした。遷移先で発生してしまうのは遷移先のページの責任で、遷移元が負うものではありません。

ただし、属性値として展開する場合はscriptタグが無くとも

  1. <a href="javascript:alert('XSS')">XSS</a>

などとすることが出来てしまうので、頭が「http://」「https://」のいずれかから始まっていることを調べる必要はあります。

以下は(私の)参考記事です。CSRF攻撃に関する部分だけまだ書いていませんが…

Qiita - 【PHP入門講座】 XSS攻撃への対策
http://qiita.com/mpyw/items/565b3670dd0c7f9162fa

Qiita - [PHP] リクエストパラメータ・セッションに関するまとめ
http://qiita.com/mpyw/items/7852213f478e8c5a2802

この意見に回答する

ツリーへ TOPへ

A01-1
replyergogoyangii [2月27日 03:48] (最終編集:2月27日 04:40)

To_aru_User 様

ご回答ありがとうございました。

取り急ぎ、お礼を申し上げます。

また、その上でいただいたご意見に対して訂正はさせていただきたく存じます。

  1. 1.混乱していると指摘のあった箇所。
  2. >>・外部から受け取るクエリ(message)の処理での対処
  3. >>・ユーザーが入力した内容のマークアップ処理での対処

前節で、XSSの処理として多く取り上げられている例をあげ、その上で私が問題と思う点を本題で述べています。
混乱しているとは思わなかったのですが (^-^)、文章は再考させていただきます。


  1. 2.>>脆弱性がありそうなIE8でも試しましたが、

IE8でテストした結果、ダイアログが表示されました。
失礼とは思いますが、他のブラウザでは?
FierFox(私は27.0.1ですが)とか?
どちらにしても、ブラウザごとに挙動が変わるようであれば、問題と考えます。

  1. 3.
  2. >>にマウスポインタを合わせてもエンティティをデコードしたものがステータスバーに表示されるだけで、それをクリックしただけで(ページ遷移せずに)JavaScriptが実行されることはありませんでした。

確実にそう言い切れるかどうかその点は疑問に思っています。
2.の件と同様、ブラウザごとに挙動が違うのではないでしょうか。
ご検討をお願いします。


  1. 4.
  2. >>遷移先で発生してしまうのは遷移先のページの責任で、遷移元が負うものではありません。

この点は確かにそう思います。
一文加筆し、質問内容に反映いたします。
「遷移先と遷移元のページは同一ページで行っています。
この場合、遷移元 seven01.xxx/index.php → 遷移先 seven01.xxx/index.phpとなります。」

  1. 5.
  2. >>ただし、属性値として展開する場合はscriptタグが無くとも
  3.  >>   <a href="javascript:alert('XSS')">XSS</a>
  4. >>などとすることが出来てしまうので、頭が「http://」「https://」のいずれかから始まっていることを調べる必要はあります。

この点については、よく勉強させていただきます。
ありがとうございました。

gogoyangii拝

この意見に回答する

ツリーへ TOPへ

A01-1-1
replyerTo_aru_User [2月27日 13:48]

InternetExplorer 8.0
Firefox 23.0
GoogleChrome 33.0

以上すべてで試しましたが、提示された現象は再現できませんでした。証拠画像貼っておきます。

https://pbs.twimg.com/media/BhdHqoUCYAA-l2y.jpg:large

この意見に回答する

ツリーへ TOPへ

A01-1-1-1
replyergogoyangii [2月27日 15:06] (最終編集:2月27日 16:11)

再度のご回答ありがとうございます。

ブラウザのバージョンのチェックをしていただいたとのこと、お手数をおかけいたします。

一度確認をさせていただきます。

画像の一番下のアドレスバーに以下の記載があります。
C:\Dropbox\Repository\Scripts\xss.php

「C:\Dropbox\Repository\Scripts」は、「http://scripts.localhost.jp:81」のWEB領域ということでよろしいでしょうか?
多分そうだと思いますが、念のため確認させていただいいています。

私もテストしていますので、画像を後ほど貼り付けておきます。

取り急ぎ。

***************
追記 16:10

遅くなりました。

http://www.fastpic.jp/images.php?file=1769956860.gif

この意見に回答する

ツリーへ TOPへ

A01-1-1-1-1
replyerTo_aru_User [2月27日 15:42] (最終編集:2月27日 16:25)

はい、もちろんです。
(ポート80は別のアプリケーションに使ってるのでテスト用に81を使わせてます)

こちらこそお手数おかけします。


************************

xss.phpの「HTMLソース」ではなく、それを出力する要因となっている「PHPソース」を見せてもらえませんか?そちらに原因があるとしか思えないです。

気になったのが、ブラウザのアドレスバーのURLが「遷移した後」のものになっている点ですね。

この意見に回答する

ツリーへ TOPへ

A01-1-1-1-1-1
replyergogoyangii [2月27日 16:37]

To_aru_User様

いろいろお手数をおかけします。

画像は以下のURLに掲載いたしました。
↓↓↓
http://www.fastpic.jp/images.php?file=1769956860.gif

その上でなのですが、最初は環境の違いかと考えていました。

私はXP(未だにですが)を2台そろえて、片方をサーバー(XAMPP)に仕立て、もう一台のPCからサーバーにアクセスする手法です。
以前、COOKIEの保存状態がサーバー用のPCとクライアント側のPCで変化があったので、この手法を以後踏襲しています。

1.
それで、サーバーのPCから直接、ブラウザで閲覧したら挙動が変わるかと思いましたが、残念ながら結局ダイアログは表示されます。

2.
次にIE8のセキュリティーの設定の違いかと考え、セキュリティーの設定をいろいろと変更しましたが、これも、挙動が変わりません。

3.
そちらと、挙動が変わることがどうしても腑に落ちなくて、画像のアップロードの前に、そちらと同じソースに切り替えて、テストしたところ、ダイアログは表示されませんでした。
ご免なさい。

いやーん、って感じです。すみません。


4.
ですが、お届けした画像の通り、ダイアログボックスが表示されるケースは存在しました。
現在、症状の切り分けを再度行っています。

画像に表示されているように、自作の配列表示用の関数を作って、値の受け渡しのチェック等に使用しています。

どうもこれが、症状を発生させている問題のようです。

もう一度切り分けを行って、症状が発生するスクリプトを掲載します。

それで、ご検討いただければ幸いです。

gogoyangii拝

この意見に回答する

ツリーへ TOPへ

A01-1-1-1-1-2
replyergogoyangii [2月27日 16:41]

追記です。

先ほどの回答の、追記を拝見しました。

ソースの確認については、前述で申し上げたとおり、切り分け後のソースを再度掲載いたします。

お手数をおかけいたします。

宜しくお願いいたします。

取り急ぎ、ご返信まで。

この意見に回答する

ツリーへ TOPへ

A01-1-1-1-1-3
replyergogoyangii [2月27日 20:06] (最終編集:2月27日 20:16)

To_aru_User様

私が間違っていました。
すみません。失礼いたしました。

検証用スクリプト作りましたので、下に記します。
結局、$_SERVERを自作関数で表示するときに、スクリプトが作動していたようです。

大事なことは、一度エスケープ処理したリンクを、次に受け渡してリンクを作っても、次にページを表示する時にブラウザで再度デコードしてしまうことがあるという事のようでした。

ページを表示するごとに、必ずHTMLエンティティーを行わなければいけないようです。

スクリプトご覧になった頃合いで、この質問削除してもよろしいでしょうか。

宜しくお願いいたします。

  1. 検証用スクリプト
  2.  
  3. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  4. <html xmlns="http://www.w3.org/1999/xhtml">
  5. <head>
  6. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  7. <meta name="description" content="SITE::PHPプロ(2014.02.26)に質問として掲載した内容の再検証用.phpファイル" />
  8. <title>XSS検証用のスクリプト</title>
  9. </head>
  10. <body>
  11.  
  12. <!--
  13. SITE::PHPプロ(2014.02.26)に質問として掲載した内容の再検証用.phpファイル(もしくはスクリプト)です。
  14. HTMLエンティティーの文字列をリンクに設定すると、なぜか、ダイアログが表示される状況の検証用です。
  15. ブラウザはIE8.0で起きます。
  16.  
  17. テストサーバーの設定
  18.  
  19. ***このスクリプトはsession関係ないけど念のため****
  20. ***最初はセッションでテストしていたから*****
  21.  
  22. session.auto_start  Off
  23. session.cache_expire  10
  24. session.cache_limiter  nocache
  25. session.cookie_domain  .seven01.xxx
  26. session.cookie_httponly  On
  27. session.cookie_lifetime  8640000
  28. session.use_cookies  On
  29. session.use_only_cookies  Off
  30. session.use_trans_sid  0
  31.  
  32. **********************
  33.  
  34.  
  35.  
  36. このファイルの basenameはxss.phpとしてください。
  37. そして、このファイルにジャンプするように、以下のURLリンクを書き換えてください。
  38.  
  39. <br/>          ↓↓↓
  40. <a href="http://seven01.xxx/xss.php?message=&lt;script&gt;alert(document.cookie);&lt;/script&gt;">生スクリプトです</a>
  41. <br/>
  42.  
  43. このコメントの直下に、問題のリンクを記載しています。
  44.                          ↓↓↓
  45. -->
  46.  
  47. <br/>
  48. <a href="http://seven01.xxx/xss.php?message=&lt;script&gt;alert(document.cookie);&lt;/script&gt;">生スクリプトです</a>
  49. <br/>
  50.  
  51.  
  52. <?PHP
  53.  
  54. ##### この部分はcookieを削除するための処理に使ったので気にしないでください。##############
  55. #setcookie('ID701','',time()-1800);
  56. #$_SESSION = array(); setcookie(session_name(), '', time()-42000, '/');session_destroy();
  57. #session_start();
  58. ##########################################################################################
  59.  
  60. setcookie('test',123);  //テストのためにcookieにtest=123をセットする。
  61.  
  62.  
  63.  
  64.  
  65. #### PV()は自作関数 #######
  66. # 直下に作成
  67.  
  68. # テスト用の配列を作成
  69.  
  70. #単純な配列
  71. $arr01 = array('A'=>0,'B'=>1);
  72.  
  73.  
  74. # $_SERVERから問題になる要素を抽出して、配列に格納する
  75. $arr02 = array(
  76.          'A'=>0
  77.         ,'B'=>1
  78.         ,'HTTP_REFERER'  => $_SERVER['HTTP_REFERER']
  79.         ,'QUERY_STRING'  => $_SERVER['QUERY_STRING']
  80.         ,'REQUEST_URI'  => $_SERVER['REQUEST_URI']
  81.         
  82.         )# arr02
  83.  
  84. # $_SERVERから問題になる要素を抽出して、配列に格納する前にHTMLエンティティー
  85. $arr03 = array(
  86.          'A'=>0
  87.         ,'B'=>1
  88.         ,'HTTP_REFERER'  => htmlspecialchars($_SERVER['HTTP_REFERER'],ENT_QUOTES) 
  89.         ,'QUERY_STRING'  => htmlspecialchars($_SERVER['QUERY_STRING'],ENT_QUOTES)
  90.         ,'REQUEST_URI'  => htmlspecialchars($_SERVER['REQUEST_URI'],ENT_QUOTES)
  91.         
  92.         )# arr02
  93.  
  94.  
  95. PV($arr01,'arr01');    //この部分では、ダイアログは非表示。
  96.  
  97. PV($arr02,'arr02');    //この部分では、ダイアログは表示。
  98.  
  99. PV($arr03,'arr03');    //この部分では、ダイアログは非表示。
  100.  
  101. #PV($_SERVER,'SERVER')
  102.  
  103.  
  104.  
  105. ?>
  106. <?PHP
  107. ########
  108. # PV($arr,$name = '')
  109. #
  110. # 配列の要素と値をブラウザに表示するための自作関数
  111. # $arr は 表示する配列 $nameは配列名
  112.  
  113.  
  114. function PV($arr,$name = ''){
  115.   
  116. print "<br/>$name<br/>\n-----------------------------------------------\n<br/>";
  117.   
  118.   if(is_array($arr)){  
  119.     
  120.     foreach($arr as $a => $b){
  121.         
  122.     
  123.       if(is_array($b)){$a = $name.'--'.$a;  array_map('PV',array($b),array($a));  }
  124.           else{  print"$a-->";var_dump($b)print "<br/>\n";}                
  125.                               
  126.                           } # foreach
  127.                       
  128.                       } # if 1
  129.   
  130.   else{  var_dump($arr);print "<br/>\n";    }
  131.   
  132. print "---------------------------------------\n<br/><br/>\n";
  133.   
  134.                 } # PV
  135.  
  136. # PV($arr,$name = '')ここまで
  137. ########  
  138. ?>
  139. </body>
  140. </html>

この意見に回答する

ツリーへ TOPへ

A01-1-1-1-1-3-1
replyerTo_aru_User [2月27日 23:51]

了解しました。特にお気になさらないでください。

この意見に回答する

ツリーへ TOPへ

A01-1-1-1-1-3-2
replyerTo_aru_User [2月27日 23:51]

了解しました。特にお気になさらないでください。

この意見に回答する

ツリーへ TOPへ

<<質問一覧へ



Pick Up Q&A

Q
掲示板サイトを作成するときの、コメントしたユーザーのID取得方法
 このエントリーをはてなブックマークに追加 
A
2chがどのようなハッシュ形式を使っているかは知りませんが、 概ねIPアドレスをcryptやmd5などでハッシュ値にして一部分を取り出しているものだと思います。 単純にハッシュ関数を使うだけだとIPがバレてしまう...

>>続きを読む

alice4work様 shimix様 ご回答ありがとうございます。掲示板サイトを作る上で重要なトピックです。IPアドレスの解読を防ぐためにソルトを付加する点がポイントですね。

▲解説者:岡本(アシアル株式会社 教育コーディネーター兼 システムエンジニア)