アカウント名:
パスワード:
今回の 2ch の XSS については、2chのサーバが外部のサービス経由のデータをHTMLに組み入れる際に、Shift_JIS において文字として完結していない半端バイトと、HTMLタグの属性値のダブルクォート (") が結合して Shift_JIS の2バイト文字としてブラウザが扱うことにより、属性値の終了を意味するダブルクォート (") が消失してしまい、メールアドレスとして入力した不正な JavaScript コードが実行されてしまうというものです。
5日前にもコメント [srad.jp] しましたが、詳しくは、文字コードの脆弱性はこの3年間でどの程度対策されたか? [slideshare.net] を読んでいただけると、原因と対策が分かりやすいです。
マルチバイトXSSに関しては、プラットフォーム側での対策は進んでいますが、ユーザー(解説本の著者を含む)の理解が不十分です。大きな書店でWebプログラミング言語の解説本を読むと、並んでいる大半の書籍にXSS対策の解説ページにおいて、マルチバイトXSSの脆弱性のあるコードが掲載されている のが現状です。
例えば、半端な先行バイトによるXSSの対策がされている PHP の 5.3.1 より新しいバージョンであっても、単に htmlspecialchars 関数を使っただけでは、文字コード UFT-8 として処理されてしまいますから(default_charset の指定がある場合を除く)、Shift_JIS などの文字コードを使っている場合には脆弱性となるのです。
// 【PHP における脆弱性のあるコード】 … 大半の解説書籍においてこのような脆弱性のあるコードが掲載されていますecho '<a href="mailto:' . htmlspecialchars($_POST['email'], ENT_QUOTES) . '">メール</a>';]
正しくは、オプションの引数として、入力値の文字コードを指定する必要があります。また、その場合であっても、入力値に当該文字コードにおいて不正なデータが含まれていたり、文字として完成していない半端データが含まれていることは、あらゆる不具合や場合によっては脆弱性の原因となります。従って、ユーザーから入力されたデータ(もしくは外部サービスなどから渡されたデータ)は、全て文字エンコーディングの validation をするべきです。
以下は、望ましいコードです。
// 【適切なコード】 // 下記のように、不正な文字エンコードのチェックと、文字コードを指定した上でのエスケープ処理の両方をやるべきです。 // 文字として不正な部分のみを削除するような処理は、一般に望ましくないのでエラーにすべきです(削除した結果、問題のあるデータが生成されるケースもあることから、そのような脆弱性を生むようなコードの書き方はしない方が無難です)。// 従って、半端バイトなどのデータが少しでも含まれていたら、その部分だけ削除するのではなく、そのままエラーとして処理します。if ( !mb_check_encoding($_POST['email'], 'Shift_JIS') ) { die('文字エンコーディングが不正です。');} // 上記のエラーチェックの他に、エスケープ処理も行います。// PHP 5.5 より新しい場合 default_charset で文字コードを指定することもできますecho '<a href="mailto:' . htmlspecialchars($_POST['email'], ENT_QUOTES, 'Shift_JIS') . '">メール</a>';
上記のコードにおける、入力時点での不正なエンコーディングのチェック、出力時点でのエスケープ処理のいずれかをやっていれば、外部サービス・外部データベース・外部APIなどを使わない限り、マルチバイトXSSは完全に防げますが、ユーザーからの入力を受け付ける段階での文字エンコーディングの validation と、HTML 出力段階でのエスケープ処理の、両方をやっておくことをお勧めします。前者はやらないと、文字として完結していない不正なデータが、データベース内のデータの破損やエラー発生などの不具合の原因にもなりますし、後者に関しては、外部のサービスなりAPIなりデータベースを経由した際に不正・破損データが含まれた場合の対策ともなります。
// 従って、半端バイトなどのデータが少しでも含まれていたら、その部分だけ削除するのではなく、そのままエラーとして処理します。金絡むトコほどエラーにしないでなんとか取り込めという方向で仕様が出来てますねぇ。。あくまで実体験なので、二桁程度のシステム。
100円未満か・・
そういうバグが原因だとしたら、埋め込んだ文字列からスクリプトを介するとかしないと「投稿に任意の文字列を埋め込み」は不可能ですね。好き放題やられる状況だったという結論は変わりませんけど。
確かに、JavaScript コードで使える文字種が制限されますので(ダブルクォートや半角スペースが使えない)、「任意のJavaScriptコード」ではなく「一部文字種が制限されたJavaScriptコード」ですね。
2ちゃんねるは、メールアドレス欄に入れた文字列が、a要素のhref属性の中に入る仕組みになっています。
普通は、こんな感じになります。
<a href="mailto:sage">名無しさん@アレゲいっぱい</a>
マルチバイトXSSの脆弱性がある場合、1回メールアドレスに「【マルチバイト文字の1バイト目 (0x82)】」だけいれて書き込みをした後、 メールアドレス欄に「 onload=alert(document.title)」といれてもう一度書き込めば、JavaScript コードが実行されるはずです。
レス2<a href="mailto:【マルチバイト文字の1バイト目 (0x82)】">名無しさん</a> レス3<a href="mailto: onload=alert(document.title)">名無しさん</a>
これが、下記のように 斜体 の部分が属性値としてブラウザに認識されます。
レス2 <a href="mailto:【マルチバイト文字の1バイト目 (0x82) と ダブルクォートで1つの文字と認識】>名無しさん</a> レス3 <a href="mailto: onload=alert(document.title)">名無しさん
分かりにくいですが、最初のa要素のhref属性を閉じるダブルクォートが消失したために、下記4行がhref属性の内容と扱われている状態です。
mailto:【マルチバイト文字の1バイト目 (0x82) と ダブルクォートで1つの文字と認識】>名無しさん</a> レス3<a href=
また、その次の「mailto:」の部分は、属性値が省略された属性名として扱われて、存在しない属性名なのでエラーとしてブラウザはスルーされます。
そして、ユーザーが2回目にメールアドレスとして入力した「 onload=alert(document.title)」(最初に半角スペース有り)の、「onload」が属性名、「alert(document.title)」が属性値として扱われて、JavaScript が実行される訳です。属性値をダブルクォートで囲わない場合、半角スペースが次に入るまでは属性値と主要ブラウザはそのまま処理するからです。
全ての文字列の最後に半角スペースを入れておけば解決ですよ。※P言語は文字コードの探査が可能だから、定型文書くならそっちだろぅに・・・
この問題、あるMMORPGでもあったなチャットで相手を落とせる、もちろん自分もメッセージ表示されるから自分も落ちるw叫べば・・・みんないなくなる。
不正なデータを弾く条件を考えるより、正常に受け付けるデータの条件を考えた方が、条件もれがあっても大丈夫w
ああ、あったあった。放置露店一掃はソレか。
より多くのコメントがこの議論にあるかもしれませんが、JavaScriptが有効ではない環境を使用している場合、クラシックなコメントシステム(D1)に設定を変更する必要があります。
目玉の数さえ十分あれば、どんなバグも深刻ではない -- Eric Raymond
文字エンコーディングの validation をしないのが原因 (スコア:5, 参考になる)
今回の 2ch の XSS については、2chのサーバが外部のサービス経由のデータをHTMLに組み入れる際に、Shift_JIS において文字として完結していない半端バイトと、HTMLタグの属性値のダブルクォート (") が結合して Shift_JIS の2バイト文字としてブラウザが扱うことにより、属性値の終了を意味するダブルクォート (") が消失してしまい、メールアドレスとして入力した不正な JavaScript コードが実行されてしまうというものです。
5日前にもコメント [srad.jp] しましたが、詳しくは、文字コードの脆弱性はこの3年間でどの程度対策されたか? [slideshare.net] を読んでいただけると、原因と対策が分かりやすいです。
マルチバイトXSSに関しては、プラットフォーム側での対策は進んでいますが、ユーザー(解説本の著者を含む)の理解が不十分です。大きな書店でWebプログラミング言語の解説本を読むと、並んでいる大半の書籍にXSS対策の解説ページにおいて、マルチバイトXSSの脆弱性のあるコードが掲載されている のが現状です。
例えば、半端な先行バイトによるXSSの対策がされている PHP の 5.3.1 より新しいバージョンであっても、単に htmlspecialchars 関数を使っただけでは、文字コード UFT-8 として処理されてしまいますから(default_charset の指定がある場合を除く)、Shift_JIS などの文字コードを使っている場合には脆弱性となるのです。
正しくは、オプションの引数として、入力値の文字コードを指定する必要があります。また、その場合であっても、入力値に当該文字コードにおいて不正なデータが含まれていたり、文字として完成していない半端データが含まれていることは、あらゆる不具合や場合によっては脆弱性の原因となります。従って、ユーザーから入力されたデータ(もしくは外部サービスなどから渡されたデータ)は、全て文字エンコーディングの validation をするべきです。
以下は、望ましいコードです。
上記のコードにおける、入力時点での不正なエンコーディングのチェック、出力時点でのエスケープ処理のいずれかをやっていれば、外部サービス・外部データベース・外部APIなどを使わない限り、マルチバイトXSSは完全に防げますが、ユーザーからの入力を受け付ける段階での文字エンコーディングの validation と、HTML 出力段階でのエスケープ処理の、両方をやっておくことをお勧めします。前者はやらないと、文字として完結していない不正なデータが、データベース内のデータの破損やエラー発生などの不具合の原因にもなりますし、後者に関しては、外部のサービスなりAPIなりデータベースを経由した際に不正・破損データが含まれた場合の対策ともなります。
Re:文字エンコーディングの validation をしないのが原因 (スコア:5, 興味深い)
バックスラッシュ単体でも問題が起こることを確認しているので、エスケープ文字から来るもののようです。
CloudFlareのemail protectionのバグなので、影響を受けるサイトは多いかもしれません。
もうすこし詳しく書くと、
<a href="mailto:\"></a>[...]<a href="mailto:a onmouseover=[...]//"></a>
が
<a href="/cdn-cgi/l/email-protection#[...].39 [...]<a href="mailto:a onmouseover=[...]//"></a>
となってしまいます。
CloudFlare側には既に通告済みですが、今のところ連絡ありません。
残念ながら (スコア:0)
// 従って、半端バイトなどのデータが少しでも含まれていたら、その部分だけ削除するのではなく、そのままエラーとして処理します。
金絡むトコほどエラーにしないでなんとか取り込めという方向で仕様が出来てますねぇ。。
あくまで実体験なので、二桁程度のシステム。
Re: (スコア:0)
100円未満か・・
Re: (スコア:0)
そういうバグが原因だとしたら、埋め込んだ文字列からスクリプトを介するとかしないと「投稿に任意の文字列を埋め込み」は不可能ですね。
好き放題やられる状況だったという結論は変わりませんけど。
「一部文字種が制限されたJavaScriptコード」 (スコア:2)
確かに、JavaScript コードで使える文字種が制限されますので(ダブルクォートや半角スペースが使えない)、「任意のJavaScriptコード」ではなく「一部文字種が制限されたJavaScriptコード」ですね。
2ちゃんねるは、メールアドレス欄に入れた文字列が、a要素のhref属性の中に入る仕組みになっています。
普通は、こんな感じになります。
マルチバイトXSSの脆弱性がある場合、1回メールアドレスに「【マルチバイト文字の1バイト目 (0x82)】」だけいれて書き込みをした後、 メールアドレス欄に「 onload=alert(document.title)」といれてもう一度書き込めば、JavaScript コードが実行されるはずです。
これが、下記のように 斜体 の部分が属性値としてブラウザに認識されます。
分かりにくいですが、最初のa要素のhref属性を閉じるダブルクォートが消失したために、下記4行がhref属性の内容と扱われている状態です。
また、その次の「mailto:」の部分は、属性値が省略された属性名として扱われて、存在しない属性名なのでエラーとしてブラウザはスルーされます。
そして、ユーザーが2回目にメールアドレスとして入力した「 onload=alert(document.title)」(最初に半角スペース有り)の、「onload」が属性名、「alert(document.title)」が属性値として扱われて、JavaScript が実行される訳です。属性値をダブルクォートで囲わない場合、半角スペースが次に入るまでは属性値と主要ブラウザはそのまま処理するからです。
Re: (スコア:0)
全ての文字列の最後に半角スペースを入れておけば解決ですよ。
※P言語は文字コードの探査が可能だから、定型文書くならそっちだろぅに・・・
Re: (スコア:0)
この問題、あるMMORPGでもあったなチャットで相手を落とせる、もちろん自分もメッセージ表示されるから自分も落ちるw
叫べば・・・みんないなくなる。
不正なデータを弾く条件を考えるより、正常に受け付けるデータの条件を考えた方が、条件もれがあっても大丈夫w
Re: (スコア:0)
ああ、あったあった。
放置露店一掃はソレか。