web-dev-qa-db-ja.com

XSS防止と.innerHTML

ユーザーがJSinnerHTML関数の引数としてデータを挿入できるようにすると、次のようになります。

element.innerHTML = “User provided variable”;

XSSを防ぐには、ユーザーが次のようなものを挿入できるため、HTMLエンコードしてから、JSエンコードする必要があることを理解しました。

<img src=a onerror='alert();'>

私が理解したように.innerHTMLメソッドは入力をページに挿入する前にデコードするため、HTMLまたはJSエンコーディングのみは役に立ちません。 HTML + JSエンコーディングでは、.innerHTMLはJSのみをデコードしますが、HTMLエンコーディングは残ります。

しかし、HTMLにダブルエンコードすることで同じことを達成することができました。

私の質問は次のとおりです。.innerHTMLメソッドを使用するときにHTMLで二重エンコードするのではなく、HTMLエンコードしてからJSエンコードする必要がある理由の例を誰かが提供できますか?

9
stanko

誰かが、.innerHTMLメソッドを使用するときにHTMLで二重エンコードするのではなく、HTMLエンコードしてからJSエンコードする必要がある理由の例を提供できますか?

承知しました。

「ユーザー提供データ」がサーバーによってJavaScriptに入力されていると仮定すると、そこに到達するにはJSエンコードする必要があります。

以下はサーバー側の擬似コードですが、フロントエンドのJavaScriptでは次のようになります。

_var userProdividedData = "<%=serverVariableSetByUser %>";
element.innerHTML = userProdividedData;
_

ASP.NETと同様に、_<%= %>_はエンコードせずにサーバー側の変数を出力します。ユーザーが「良好」で値fooを指定すると、次のJavaScriptがレンダリングされます。

_var userProdividedData = "foo";
element.innerHTML = userProdividedData;
_

これまでのところ問題はありません。

ここで、悪意のあるユーザーが値"; alert("xss attack!");//を提供するとします。これは次のようにレンダリングされます。

_var userProdividedData = ""; alert("xss attack!");//";
element.innerHTML = userProdividedData;
_

これにより、上記の最初の行でコードが実際に実行されるXSSエクスプロイトが発生します。

これを防ぐために、あなたが言うように、JSはエンコードします。 OWASP XSS防止に関するチートシートルール# は次のように述べています。

英数字を除き、256未満のすべての文字を\ xHH形式でエスケープして、データ値がスクリプトコンテキストまたは別の属性に切り替わらないようにします。

したがって、これを防ぐために、コードは次のようになります。

_var userProdividedData = "<%=JsEncode(serverVariableSetByUser) %>";
element.innerHTML = userProdividedData;
_

ここで、JsEncodeはOWASPの推奨に従ってエンコードします。

これにより、次のようにレンダリングされるため、上記の攻撃を防ぐことができます。

_var userProdividedData = "\x22\x3b\x20alert\x28\x22xss\x20attack\x21\x22\x29\x3b\x2f\x2f";
element.innerHTML = userProdividedData;
_

これで、JavaScript変数の割り当てがXSSに対して保護されました。

ただし、悪意のあるユーザーが値として<img src="xx" onerror="alert('xss attack')" />を指定した場合はどうなりますか?これは、上記のように同等の16進エンティティに変換されるだけなので、変数代入部分には問題ありません。

しかし、ライン

_element.innerHTML = userProdividedData;
_

ブラウザが内部HTMLをレンダリングするときに、alert('xss attack')が実行されます。これは DOMベースのXSS 攻撃になります。

これが、HTMLエンコードも必要になる理由です。これは、次のような関数を介して実行できます。

_function escapeHTML (unsafe_str) {
    return unsafe_str
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/\"/g, '&quot;')
      .replace(/\'/g, '&#39;')
      .replace(/\//g, '&#x2F;')
}
_

コードを作成する

_element.innerHTML = escapeHTML(userProdividedData);
_

または、JQueryの text() 関数を介して実行できます。

コメントの質問に関する更新

もう1つ質問があります。攻撃者が"; alert("xss attack!");//に入る可能性があるため、JSエンコードする必要があるとのことです。しかし、JSエンコーディングの代わりにHTMLエンコーディングを使用する場合、HTMLも_"_記号をエンコードし、次のようになるため、この攻撃を不可能にしません。var userProdividedData ="&quot;; alert(&quot;xss attack!&quot;);&#x2F;&#x2F;";

私はあなたの質問を次のことを意味すると考えています:JSエンコーディングの後にHTMLエンコーディングが続くのではなく、そもそもHTMLエンコーディングだけでなく、そのままにしておかないのはなぜですか?

<img src="xx" onerror="alert('xss attack')" />などの攻撃をすべてエンコードしてペイロードを挿入するために_\xHH_形式を使用してエンコードできるため、HTMLエンコードの文字を使用せずに、攻撃の目的のHTMLシーケンスを実現できます。影響します。

他にもいくつかの攻撃があります。攻撃者が_\_を入力した場合、ブラウザに終了引用符を見逃させる可能性があります(_\_はJavaScriptのエスケープ文字であるため)。

これは次のようにレンダリングされます。

_var userProdividedData = "\";
_

これは、適切に終了したステートメントではないため、JavaScriptエラーをトリガーします。これにより、アプリケーションが目立つ場所にレンダリングされた場合、アプリケーションにサービス拒否が発生する可能性があります。

さらに、ユーザーが制御するデータが2つあったとします。

_var userProdividedData = "<%=serverVariableSetByUser1 %>" + ' - ' + "<%=serverVariableSetByUser2 %>";
_

次に、ユーザーは最初に_\_を入力し、2番目に;alert('xss');//を入力できます。これにより、文字列の連結が1つの大きな割り当てに変更され、その後にXSS攻撃が続きます。

_var userProdividedData = "\" + ' - ' + ";alert('xss');//";
_

このようなEdgeのケースがあるため、OWASPガイドラインに従うことをお勧めします。これは、可能な限り防弾に近いためです。 HTMLエンコード値のリストに_\_を追加するとこれが解決すると思うかもしれませんが、この方法でコンテンツをレンダリングするときにJSの後にHTMLを使用する理由は他にもあります。これは、この方法が属性値のデータに対しても機能するためです。

_<a href="javascript:void(0)" onclick="myFunction('<%=JsEncode(serverVariableSetByUser) %>'); return false">
_

一重引用符であるか二重引用符であるかにかかわらず、次のようになります。

_<a href='javascript:void(0)' onclick='myFunction("<%=JsEncode(serverVariableSetByUser) %>"); return false'>
_

または引用されていない:

_<a href=javascript:void(0) onclick=myFunction("<%=JsEncode(serverVariableSetByUser) %>");return false;>
_

コメントに記載されているようにHTMLエンコードした場合、エンティティ値は次のようになります。

_onclick='var userProdividedData ="&quot;;"'_(短縮バージョン)

コードは実際には最初にブラウザのHTMLパーサーを介して実行されるため、userProdividedDataは次のようになります。

_";;
_

の代わりに

_&quot;;
_

したがって、それをinnerHTML呼び出しに追加すると、XSSが再び使用されます。 _<script>_ブロックは、終了_</script>_タグを除いて、ブラウザのHTMLパーサーを介して処理されないことに注意してください。 しかし それは 別の話 です。

上記のように、可能な限りlateとしてエンコードすることをお勧めします。次に、JavaScriptコンテキスト以外で値を出力する必要がある場合(たとえば、実際のアラートボックスがHTMLをレンダリングしない場合でも、正しく表示されます)。

つまり、上記で私は呼び出すことができます

_alert(serverVariableSetByUser);
_

hTMLを設定するのと同じくらい簡単

_element.innerHTML = escapeHTML(userProdividedData);
_

どちらの場合も、特定の文字が出力を中断したり、望ましくないコード実行を引き起こしたりすることなく、正しく表示されます。

18
SilverlightFox

elementのコンテンツが適切にエンコードされている(そしてHTMLとして解析されない)ことを確認する簡単な方法は、textContentの代わりにinnerHTMLを使用することです。

element.textContent = "User provided variable with <img src=a>";

もう1つのオプションは、使用する値をエンコードした後でのみinnerHTMLを使用することです(機会があればサーバー上で)。

7
adriann

ASP.NETWebformsアプリケーションでこの問題に直面しました。これに対する修正は比較的簡単です。

NuGetパッケージマネージャーからHtmlSanitizationLibraryをインストールし、アプリケーションでこれを参照します。コードビハインドでは、次のようにサニタイザークラスを使用してください。

たとえば、現在のコードが次のようになっている場合、

YourHtmlElement.InnerHtml = "Your HTML content" ;

次に、これを次のように置き換えます。

string unsafeHtml = "Your HTML content"; 
YourHtmlElement.InnerHtml = Sanitizer.GetSafeHtml(unsafeHtml);

この修正により、Veracodeの脆弱性が削除され、文字列がHTMLとしてレンダリングされるようになります。コードビハインドで文字列をエンコードすると、レンダリングが開始される前にエンコードされるため、RAW HTMLではなく、「エンコードされていない文字列」としてレンダリングされます。

2
Navneet Sharma