マジック クォートがオンになり、レジスタ グローバルがオフになるように PHP を構成しました。
ユーザー入力から派生したものを出力する場合は、常に htmlentities() を呼び出すように最善を尽くしています。
私は時々データベースを検索して、添付されたxssで使用される一般的なものを探します...
<script
他に何をすべきか、やろうとしていることが常に行われるようにするにはどうすればよいでしょうか。
入力をエスケープすることは、XSS 防止を成功させるためにできる最善の方法ではありません。出力もエスケープする必要があります。Smarty テンプレート エンジンを使用する場合は|escape:'htmlall'
、すべての機密文字を HTML エンティティに変換するために修飾子を使用できます (|e
上記のエイリアスである独自の修飾子を使用します)。
入出力セキュリティに対する私のアプローチは次のとおりです。
私は、入力時に何もエスケープすべきではなく、出力時にのみエスケープするべきだと考えています。(ほとんどの場合) そのデータがどこに行くのかを知っていると仮定することはできません。たとえば、送信する電子メールに後で表示されるデータを取得するフォームがある場合、別のエスケープが必要です (そうしないと、悪意のあるユーザーが電子メールのヘッダーを書き換える可能性があります)。
つまり、データがアプリケーションから「離れる」最後の瞬間にのみエスケープできます。
要するに:
Esp #3 は、入力レイヤーでデータをエスケープする場合 (または再度エスケープ解除する必要がある場合など) に発生します。
PS: 私は、magic_quotes を使用しないというアドバイスを支持します。それらは純粋な悪です!
XSS を実行するには多くの方法があり ( http://ha.ckers.org/xss.htmlを参照)、把握するのは非常に困難です。
私は個人的にこれを現在使用しているフレームワーク (コード イグナイターなど) に委任します。完璧ではありませんが、私の手作りのルーチンよりも多くをキャッチする可能性があります.
これは素晴らしい質問です。
まず、安全に保管するため (データベースに入れる場合など) を除いて、入力時にテキストをエスケープしないでください。その理由は、さまざまな方法や場所で文脈に応じて提示できるように、入力されたものを保持したいからです。ここで変更を加えると、後のプレゼンテーションが損なわれる可能性があります。
データを提示するときは、そこにあるべきではないものを除外します。たとえば、javascript が存在する理由がない場合は、それを検索して削除します。これを行う簡単な方法は、strip_tags関数を使用して、許可する html タグのみを表示することです。
次に、持っているものを htmlentities または htmlspecialchars に渡して、そこにあるものを ASCII 文字に変更します。これは、コンテキストと何を伝えたいかに基づいて行ってください。
また、マジック クォートをオフにすることをお勧めします。これは PHP 6 から削除されており、使用するのは悪い習慣と見なされています。詳細はhttp://us3.php.net/magic_quotesを参照
詳細については、http://ha.ckers.org/xss.htmlをご覧ください。
これは完全な答えではありませんが、うまくいけば、始めるのに十分です。
リッチは次のように書いています。
ユーザー入力から派生したものを出力する場合は、常に htmlentities() を呼び出すように最善を尽くしています。
これについては、Joel のMaking Code Look Wrongに関するエッセイを参照してください。
テンプレートライブラリ。または、少なくとも、それがテンプレートライブラリが行うべきことです。XSSを防ぐには、すべての出力をエンコードする必要があります。これはメインアプリケーション/制御ロジックのタスクではなく、出力メソッドによってのみ処理される必要があります。
コード全体にhtmlentities()を振りかけると、全体的なデザインが間違っています。そして、あなたが示唆するように、あなたは1つか2つの場所を逃すかもしれません。そのため、唯一の解決策は、厳密なhtmlエンコーディング->出力変数がhtml/xmlストリームに書き込まれる場合です。
残念ながら、ほとんどのphpテンプレートライブラリは独自のテンプレート構文を追加するだけですが、出力のエンコード、ローカリゼーション、HTML検証、または重要なことには関係ありません。たぶん誰か他の人がphp用の適切なテンプレートライブラリを知っていますか?
私はそのためにPHPTALに依存しています。
Smarty やプレーンな PHP とは異なり、デフォルトですべての出力をエスケープします。これはセキュリティにとって大きなメリットです。忘れたhtmlspecialchars()
場合や|escape
どこかでサイトが脆弱になることはないからです。
XSS は HTML 固有の攻撃であるため、HTML 出力はそれを防ぐのに適した場所です。HTML を受け入れない別のメディアにデータを出力する必要がある可能性があるため、データベース内のデータを事前にフィルター処理しようとしないでください。ただし、独自のリスクがあります。
「魔法の引用符」は、XSSの最悪の欠陥のいくつかに対する緩和療法であり、設計上間違っている入力のすべてをエスケープすることで機能します。それを使用したい唯一のケースは、XSSに関して不注意に書かれていることが知られている既存のPHPアプリケーションを絶対に使用しなければならない場合です。(この場合、「魔法の引用符」でも深刻な問題が発生します。)独自のアプリケーションを開発するときは、「魔法の引用符」を無効にして、代わりにXSSセーフな方法に従う必要があります。
クロスサイトスクリプティングの脆弱性であるXSSは、アプリケーションが[X] HTML、CSS、ECMAscript、またはその他のブラウザーで解析された出力に、適切なエスケープや期待なしに外部ソース(ユーザー入力、他のWebサイトからフェッチされたものなど)からの文字列を含む場合に発生します。より小さい([X] HTML)、一重引用符または二重引用符(ECMAscript)などの特殊文字は表示されません。これに対する適切な解決策は、出力言語の規則に従って常に文字列をエスケープすることです。[X] HTMLのエンティティ、ECMAscriptのバックスラッシュなどを使用します。
信頼できないものを追跡するのは難しく、エスケープする必要があるため、HTMLなどの言語の「マークアップ付きテキスト」ではなく「テキスト文字列」であるすべてのものを常にエスケープすることをお勧めします。一部のプログラミング環境では、互換性のないいくつかの文字列タイプ(「文字列」(通常のテキスト)、「HTML文字列」(HTMLマークアップ)など)を導入することで、より簡単になります。そうすれば、「文字列」から「HTML文字列」への直接の暗黙の変換は不可能になり、文字列がHTMLマークアップになる唯一の方法は、エスケープ関数を通過させることです。
「グローバルの登録」は、無効にすることは間違いなく良い考えですが、XSSとはまったく異なる問題を扱います。
これらの回答はどれもすばらしいものですが、基本的に XSS の解決策は、文字列操作による HTML ドキュメントの生成を停止することです。
入力をフィルタリングすることは、どのアプリケーションでも常に良い考えです。
htmlentities() などを使用して出力をエスケープすることは、適切に使用されている限り機能するはずですが、これは mysql_real_escape_string($var) で文字列を連結して SQL クエリを作成するのと同等の HTML です - 機能するはずですが、作業を検証できるものはほとんどありません、いわば、パラメーター化されたクエリを使用するようなアプローチと比較して.
長期的な解決策は、おそらく DOM のような標準インターフェイスを使用して、アプリケーションがページを内部的に構築し、ライブラリ (libxml など) を使用して XHTML/HTML/etc へのシリアル化を処理することです。もちろん、それが普及して十分に高速になるにはほど遠いですが、それまでの間、文字列操作を介して HTML ドキュメントを作成する必要があり、それは本質的によりリスクが高くなります。
ほとんどのサイトでは、すべてのユーザー入力をエスケープするだけで十分です。Referer
また、別のサイトへのリンクからセッション ID が盗まれないように、セッション ID が URL に含まれていないことを確認してください。さらに、ユーザーがリンクを送信できるようにする場合は、javascript:
プロトコル リンクが許可されていないことを確認してください。これらは、ユーザーがリンクをクリックするとすぐにスクリプトを実行します。
この関数を使用すると、考えられる多くの xss 攻撃を取り除くのに役立つことがわかりました。
<?php
function h($string, $esc_type = 'htmlall')
{
switch ($esc_type) {
case 'css':
$string = str_replace(array('<', '>', '\\'), array('<', '>', '/'), $string);
// get rid of various versions of javascript
$string = preg_replace(
'/j\s*[\\\]*\s*a\s*[\\\]*\s*v\s*[\\\]*\s*a\s*[\\\]*\s*s\s*[\\\]*\s*c\s*[\\\]*\s*r\s*[\\\]*\s*i\s*[\\\]*\s*p\s*[\\\]*\s*t\s*[\\\]*\s*:/i',
'blocked', $string);
$string = preg_replace(
'/@\s*[\\\]*\s*i\s*[\\\]*\s*m\s*[\\\]*\s*p\s*[\\\]*\s*o\s*[\\\]*\s*r\s*[\\\]*\s*t/i',
'blocked', $string);
$string = preg_replace(
'/e\s*[\\\]*\s*x\s*[\\\]*\s*p\s*[\\\]*\s*r\s*[\\\]*\s*e\s*[\\\]*\s*s\s*[\\\]*\s*s\s*[\\\]*\s*i\s*[\\\]*\s*o\s*[\\\]*\s*n\s*[\\\]*\s*/i',
'blocked', $string);
$string = preg_replace('/b\s*[\\\]*\s*i\s*[\\\]*\s*n\s*[\\\]*\s*d\s*[\\\]*\s*i\s*[\\\]*\s*n\s*[\\\]*\s*g:/i', 'blocked', $string);
return $string;
case 'html':
//return htmlspecialchars($string, ENT_NOQUOTES);
return str_replace(array('<', '>'), array('<' , '>'), $string);
case 'htmlall':
return htmlentities($string, ENT_QUOTES);
case 'url':
return rawurlencode($string);
case 'query':
return urlencode($string);
case 'quotes':
// escape unescaped single quotes
return preg_replace("%(?<!\\\\)'%", "\\'", $string);
case 'hex':
// escape every character into hex
$s_return = '';
for ($x=0; $x < strlen($string); $x++) {
$s_return .= '%' . bin2hex($string[$x]);
}
return $s_return;
case 'hexentity':
$s_return = '';
for ($x=0; $x < strlen($string); $x++) {
$s_return .= '&#x' . bin2hex($string[$x]) . ';';
}
return $s_return;
case 'decentity':
$s_return = '';
for ($x=0; $x < strlen($string); $x++) {
$s_return .= '&#' . ord($string[$x]) . ';';
}
return $s_return;
case 'javascript':
// escape quotes and backslashes, newlines, etc.
return strtr($string, array('\\'=>'\\\\',"'"=>"\\'",'"'=>'\\"',"\r"=>'\\r',"\n"=>'\\n','</'=>'<\/'));
case 'mail':
// safe way to display e-mail address on a web page
return str_replace(array('@', '.'),array(' [AT] ', ' [DOT] '), $string);
case 'nonstd':
// escape non-standard chars, such as ms document quotes
$_res = '';
for($_i = 0, $_len = strlen($string); $_i < $_len; $_i++) {
$_ord = ord($string{$_i});
// non-standard char, escape it
if($_ord >= 126){
$_res .= '&#' . $_ord . ';';
} else {
$_res .= $string{$_i};
}
}
return $_res;
default:
return $string;
}
}
?>
XSS 攻撃が心配な場合は、出力文字列を HTML にエンコードすることが解決策です。すべての出力文字を HTML 形式にエンコードすることを忘れない場合、XSS 攻撃を成功させる方法はありません。
続きを読む: ユーザーデータのサニタイズ: 方法と場所
個人的には、magic_quotes を無効にします。PHP5+ ではデフォルトで無効になっており、すべてをエスケープするわけではなく、PHP6 から削除されるため、まったく存在しないかのようにコーディングすることをお勧めします。
次に、フィルタリングしているユーザー データのタイプに応じて、次に何をすべきかが決まります。たとえば、名前などの単なるテキストの場合は、strip_tags(trim(stripslashes()));
正規表現を使用して範囲を確認します。
特定の範囲の値が予想される場合は、有効な値の配列を作成し、それらの値のみを許可します ( in_array($userData, array(...))
)。
数値をチェックしている場合は、 is_numeric を使用して整数を強制するか、特定の型にキャストすることで、代わりに文字列を送信しようとする人を防ぐことができます。
PHP5.2+ を使用している場合は、filter()を調べて、電子メール アドレスを含むさまざまなデータ型をフィルタリングできる拡張機能を利用することを検討してください。ドキュメンテーションは特に優れているわけではありませんが、改善されています。
HTML を処理する必要がある場合は、PHP Input FilterやHTML Purifierなどを検討する必要があります。HTML Purifier は、HTML の適合性も検証します。Input Filter がまだ開発中であるかどうかはわかりません。どちらも、使用できる一連のタグと許可される属性を定義できます。
何を決定するにしても、ユーザー (自分自身を含む) から PHP スクリプトに入力されるものは決して信用しないことを常に覚えておいてください。
少なくとも、データベースに入るすべてのデータを検証する必要があります。また、データベースから出るすべてのデータも検証してみてください。
mysql_real_escape_string は SQL インジェクションを防ぐのに適していますが、XSS はよりトリッキーです。可能であれば、preg_match、stip_tags、または htmlentities を使用する必要があります。
HttpOnly を使用する任意のセッション Cookie (またはすべての Cookie) を作成します。その場合、ほとんどのブラウザは JavaScript から Cookie の値を隠します。ユーザーは引き続き Cookie を手動でコピーできますが、これによりスクリプトへの直接アクセスを防ぐことができます。StackOverflow には、ベータ版でこの問題がありました。
これは解決策ではありません。
PHP アプリケーションで XSS を防止するための現在の最良の方法は、HTML Purifier (http://htmlpurifier.org/) です。これのマイナーな欠点の 1 つは、かなり大きなライブラリであり、APC のような op コード キャッシュと一緒に使用するのが最適であることです。これは、信頼できないコンテンツが画面に出力されるあらゆる場所で使用できます。htmlentities、htmlspecialchars、filter_input、filter_var、strip_tags などよりもはるかに徹底しています。
既存のユーザー入力サニタイズライブラリを使用して、すべてのユーザー入力をクリーンアップします。あなたがそれに多くの努力を払わない限り、それを自分で実装することは決してうまくいきません。
最良の方法は、コードをバインドできるクラスを使用することであり、データを手動でエスケープすることを心配する必要はありません。
誤報を引き起こさないサイトで完全な sql インジェクション/xss インジェクション防止を実装することは困難です。CMS では、エンド ユーザーが使用し<script>
たり<object>
、別のサイトのアイテムにリンクしたりする場合があります。
すべてのユーザーに FireFox を NoScript でインストールすることをお勧めします ;-)