HTMLとPHPだけを使ってXSS(クロスサイトスクリプティング)を防ぐにはどうすればよいですか?
このトピックに関して他にも多数の投稿がありますが、XSSを実際に防止する方法を明確かつ簡潔に記載した記事は見つかりませんでした。
基本的に、ユーザー入力から来たものをブラウザに出力したいときはいつでも、関数 htmlspecialchars()
を使う必要があります。
この関数を使う正しい方法は次のようなものです。
echo htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
Google Code Universityには、Webセキュリティに関する非常に教育的なビデオもあります。
私のお気に入りのOWASP参照はクロスサイトスクリプティング説明XSS攻撃の方法は多数ありますが、いくつかの規則に従うことでそれらの大部分を防御することができます。
これはPHPセキュリティチートシートです
優先順に:
{{ var|e('html_attr') }}
htmlentities($var, ENT_QUOTES | ENT_HTML5, $charset)
を使用し、ドキュメントの残りの部分で$charset
と同じ文字セットが使用されていることを確認してください。ほとんどの場合、'UTF-8'
が目的の文字セットです。また、 入力ではなく、出力でエスケープする であることを確認してください。
オフラインになっているSO Documentationベータ版からの総合参照としてこれをクロス投稿しています。
クロスサイトスクリプティングは、Webクライアントによるリモートコードの意図しない実行です。 Webアプリケーションは、ユーザーから入力を受け取り、それをWebページに直接出力すると、自分自身をXSSに公開する可能性があります。入力にHTMLまたはJavaScriptが含まれる場合、このコンテンツがWebクライアントによってレンダリングされるときにリモートコードが実行される可能性があります。
たとえば、サードパーティサイドにJavaScriptファイルが含まれているとします。
// http://example.com/runme.js
document.write("I'm running");
また、PHPアプリケーションは、渡された文字列を直接出力します。
<?php
echo '<div>' . $_GET['input'] . '</div>';
チェックされていないGETパラメータに<script src="http://example.com/runme.js"></script>
が含まれている場合、PHPスクリプトの出力は次のようになります。
<div><script src="http://example.com/runme.js"></script></div>
サードパーティのJavaScriptが実行され、ユーザーはWebページに "I'm running"と表示されます。
原則として、クライアントからの入力を決して信頼しないでください。 GET、POST、およびCookieの値はすべてなんでもかまいませんので、検証する必要があります。これらの値を出力するときは、それらをエスケープして、予期しない方法で評価されないようにしてください。
最も単純なアプリケーションでもデータは移動する可能性があり、すべてのソースを追跡するのは困難になります。したがって、alwaysエスケープ出力を使用することをお勧めします。
PHPはコンテキストに応じて出力をエスケープするためのいくつかの方法を提供します。
PHPフィルタ関数 phpスクリプトへの入力データを サニタイズ または 検証済み in さまざまな方法 にすることができます。クライアント入力を保存または出力するときに役立ちます。
htmlspecialchars
は、 "HTML特殊文字"をHTMLエンコーディングに変換します。つまり、notは標準のHTMLとして処理されます。この方法で前の例を修正するには
<?php
echo '<div>' . htmlspecialchars($_GET['input']) . '</div>';
// or
echo '<div>' . filter_input(INPUT_GET, 'input', FILTER_SANITIZE_SPECIAL_CHARS) . '</div>';
出力します:
<div><script src="http://example.com/runme.js"></script></div>
<div>
タグ内のすべてはnotではなくJavaScriptタグとして解釈されますが、代わりに単純なテキストノードとして解釈されます。ユーザーは安全に表示されます。
<script src="http://example.com/runme.js"></script>
動的に生成されたURLを出力するとき、PHPは有効なURLを安全に出力するためのurlencode
関数を提供します。したがって、たとえば、ユーザーが別のGETパラメータの一部になるデータを入力できるとします。
<?php
$input = urlencode($_GET['input']);
// or
$input = filter_input(INPUT_GET, 'input', FILTER_SANITIZE_URL);
echo '<a href="http://example.com/page?input="' . $input . '">Link</a>';
悪意のある入力はエンコードされたURLパラメータに変換されます。
HTMLや他の種類のコード入力を送信したいことがあります。あなたは許可された単語のリスト(ホワイトリスト)と許可されていない(ブラックリスト)を維持する必要があります。
標準のリストは OWASP AntiSamy Webサイト からダウンロードできます。各リストは特定の種類の相互作用(ebay api、tinyMCEなど)に適しています。そしてそれはオープンソースです。
HTMLをフィルタリングし、一般的な状況でXSS攻撃を防止し、少なくとも非常に簡単に使用できるAntiSamyリストを実行するためのライブラリがあります。例えば、あなたは HTML Purifier を持っています
多くのフレームワークがさまざまな方法でXSSを処理するのを助けます。自分でロールバックするとき、またはXSSの懸念があるときは、 filter_input_array (PHP 5> = 5.2.0、PHP 7で利用可能)を利用できます。他のコントローラがデータとやり取りする前にすべての呼び出しがそこを通過するため、通常はこのスニペットを私のSessionControllerに追加します。このようにして、すべてのユーザー入力が1か所で消毒されます。 これがプロジェクトの開始時またはデータベースが汚染される前に行われている場合は、出力時に問題はないはずです。
/* Prevent XSS input */
$_GET = filter_input_array(INPUT_GET, FILTER_SANITIZE_STRING);
$_POST = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING);
/* I prefer not to use $_REQUEST...but for those who do: */
$_REQUEST = (array)$_POST + (array)$_GET + (array)$_REQUEST;
上記はALLのHTMLとスクリプトタグを削除します。ホワイトリストに基づいて安全なタグを許可するソリューションが必要な場合は、 HTML Purifier をご覧ください。
データベースが既に汚染されている場合、または出力時にXSSを処理したい場合は、 OWASPecho
用のカスタムラッパー関数を作成し、それを使用してユーザー指定の値を出力することをお勧めします。
//xss mitigation functions
function xssafe($data,$encoding='UTF-8')
{
return htmlspecialchars($data,ENT_QUOTES | ENT_HTML401,$encoding);
}
function xecho($data)
{
echo xssafe($data);
}
<?php
function xss_clean($data)
{
// Fix &entity\n;
$data = str_replace(array('&','<','>'), array('&amp;','&lt;','&gt;'), $data);
$data = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $data);
$data = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $data);
$data = html_entity_decode($data, ENT_COMPAT, 'UTF-8');
// Remove any attribute starting with "on" or xmlns
$data = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $data);
// Remove javascript: and vbscript: protocols
$data = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $data);
$data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $data);
$data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $data);
// Only works in IE: <span style="width: expression(alert('Ping!'));"></span>
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $data);
// Remove namespaced elements (we do not need them)
$data = preg_replace('#</*\w+:\w[^>]*+>#i', '', $data);
do
{
// Remove really unwanted tags
$old_data = $data;
$data = preg_replace('#</*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i', '', $data);
}
while ($old_data !== $data);
// we are done...
return $data;
}
header(...)
経由でXSS関連のHTTPレスポンスヘッダを設定することもできます。
X-XSS-Protection "1;モード=ブロック"
確かに、ブラウザのXSS保護モードは有効になっています。
コンテンツセキュリティポリシー "default-src 'self'; ..."
ブラウザサイドのコンテンツセキュリティを有効にします。コンテンツセキュリティポリシー(CSP)の詳細についてはこちらを参照してください。 http://content-security-policy.com/ 特にインラインスクリプトと外部スクリプトソースをブロックするようにCSPを設定することはXSSに対して役に立ちます。
あなたのWebアプリケーションのセキュリティに関する有用なHTTPレスポンスヘッダの一般的な束については、OWASPを見てください: https://www.owasp.org/index.php/List_of_useful_HTTP_headers
htmlspecialchars
にPHP
を使用してください。 HTMLでは、次のことを避けてください。
element.innerHTML = “…”; element.outerHTML = “…”; document.write(…); document.writeln(…);
ここでvar
はユーザーによって制御されます。
また、eval(var)
を避けて試してみてください。それらを使用する必要がある場合は、JSエスケープしてください。HTMLエスケープしてください。基本的にはこれで十分なはずです。