67


50

クライアント側でのサニタイズ/ HTMLの書き換え

クロスドメインリクエストによってロードされた外部リソースを表示し、必ず "safe"コンテンツのみを表示する必要があります。

スクリプトブロックを削除するのにPrototypeのhttp://www.prototypejs.org/api/string/stripScripts[String#stripScripts]を使うことができます。 しかし、 onclick`や onerror`のようなハンドラはまだあります。

少なくともできるライブラリはありますか

  • スクリプトブロックを削除します。

  • DOMハンドラを殺す

  • ブラックリストのタグを削除します(例: embed`や object`)。

JavaScript関連のリンクや例はありますか。

10 Answer


98


アップデート2016:Caja sanitizerをベースにしたhttps://github.com/google/closure-library/blob/master/closure/goog/html/sanitizer/htmlsanitizer.js [Google Closure]パッケージが追加されました。

それはよりきれいなAPIを持ち、現代のブラウザで利用可能なAPIを考慮に入れるように書き直され、そしてClosure Compilerとよりよく相互作用する。

'' '' '

恥知らずなプラグイン:クライアントサイドについてはhttps://github.com/google/caja/blob/master/src/com/google/caja/plugin/html-sanitizer.js[caja/plugin/html-sanitizer.js]を参照してください。徹底的に見直されているhtml消毒剤。

ブラックリストではなくホワイトリストですが、ホワイトリストはhttps://github.com/google/caja/wiki/CajaWhitelists[CajaWhitelists]に従って設定可能です。

'' '' '

すべてのタグを削除したい場合は、次の手順に従ってください。

var tagBody = '(?:[^"\'>]|"[^"]*"|\'[^\']*\')*';

var tagOrComment = new RegExp(
    '<(?:'
    // Comment body.
    + '!--(?:(?:-*[^->])*--+|-?)'
    // Special "raw text" elements whose content should be elided.
    + '|script\\b' + tagBody + '>[\\s\\S]*?[\\s\\S]*?',
    'gi');
function removeTags(html) {
  var oldHtml;
  do {
    oldHtml = html;
    html = html.replace(tagOrComment, '');
  } while (html !== oldHtml);
  return html.replace(/

人々は、あなたが要素を作成し、そして innerHTML`を割り当て、そしてそれから innerText`または textContent`を得、そしてその中の実体をエスケープすることができるとあなたに言うでしょう。 そんなことしたらダメ。 ノードがDOMに接続されていない場合でも、 ` onerror`ハンドラを実行するため、XSSインジェクションに対して脆弱です。


38


Google Caja https://code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/plugin/html-sanitizer.js [HTMLサニタイザー]を作成することができます "web- https://developer.mozilla.org/en/Using_web_workers [web worker]に埋め込むことで、準備ができました。 サニタイザーによって導入されたグローバル変数はすべてワーカー内に含まれ、さらに処理はそれ自身のスレッドで行われます。

Webワーカーをサポートしていないブラウザの場合は、サニタイザーを動作させるための独立した環境としてiframeを使用できます。 Timothy Chienはhttps://github.com/timdream/simworker[polyfill]を持っています。これは、Webワーカーをシミュレートするためにiframeを使用しているので、その部分は私たちのために行われます。

Cajaプロジェクトのhttps://code.google.com/p/google-caja/wiki/JsHtmlSanitizer [Cajaをスタンドアロンのクライアントサイドサニタイザーとして使用する方法]にWikiページがあります。

  • ソースをチェックアウトしてから `ant`を実行してビルドしてください

  • html-sanitizer-minified.js`を含めるか、 あなたのページの `html-css-sanitizer-minified.js

  • `html_sanitize(…​)`を呼び出します

ワーカースクリプトはこれらの指示に従うだけでよいです。

importScripts('html-css-sanitizer-minified.js'); // or 'html-sanitizer-minified.js'

var urlTransformer, nameIdClassTransformer;

// customize if you need to filter URLs and/or ids/names/classes
urlTransformer = nameIdClassTransformer = function(s) { return s; };

// when we receive some HTML
self.onmessage = function(event) {
    // sanitize, then send the result back
    postMessage(html_sanitize(event.data, urlTransformer, nameIdClassTransformer));
};

(simworkerライブラリを動かすためにはもう少しコードが必要ですが、この議論にとって重要ではありません。)

デモ:https://dl.dropbox.com/u/291406/html-sanitize/demo.html


14


クライアントを決して信頼しないでください。 サーバーアプリケーションを作成している場合は、クライアントが常に不衛生な悪意のあるデータを送信すると想定してください。 それはあなたをトラブルから守るための経験則です。 可能であれば、サーバーコードですべての検証とサニタイズを実行することをお勧めします。これは(合理的な範囲で)邪魔されることはありません。 クライアントサイドコードのプロキシとしてサーバーサイドWebアプリケーションを使用することもできます。これはサードパーティから取得し、クライアント自体に送信する前にサニタイズを行いますか。

すみません、質問を誤解しました。 しかし、私は私の忠告を支持します。 サーバーに送信する前にサーバーをサニタイズすると、ユーザーはおそらくより安全になります。


12


ブラックリストから逃れるためにどこかのブラウザがトリップする可能性がある、奇妙な形式のマークアップのすべての奇妙なタイプを予想することはできません。 script / embed / objectやhandlerだけでなく、削除しなければならない構造がたくさんあります。

代わりに、HTMLを階層内の要素と属性に解析してから、可能な限り最小限のホワイトリストに対してすべての要素名と属性名を実行します。 また、通過したURL属性もホワイトリストと照らし合わせてチェックしてください(単なるjavascript:より危険なプロトコルがあることを忘れないでください)。

入力が整形式のXHTMLの場合、上記の最初の部分ははるかに簡単です。

HTMLのサニタイズと同様に、他の方法でそれを回避できる場合は、代わりにそれを実行してください。 潜在的な穴がたくさんあります。 主要なWebメールサービスがこの長年の後もまだ悪用を見つけている場合、どうすればあなたがより良いことができると思いますか?


9


すべての主要なブラウザがサンドボックス化されたiframeをサポートしているので、私が安全であると考えることができるもっと簡単な方法があります。 この答えがこの種のセキュリティ問題にもっと精通している人々によって見直されることができれば私はそれが大好きです。

*注:この方法は間違いなくIE 9以前では機能しません。 サンドボックスをサポートするブラウザのバージョンについては この表を参照してください。

アイデアは、JavaScriptを無効にして隠れたiframeを作成し、信頼できないHTMLをその中に貼り付けて解析させることです。 その後、DOMツリーをたどって、安全と見なされているタグと属性をコピーします。

ここに示されているホワイトリストは単なる例です。 ホワイトリストに最適なものはアプリケーションによって異なります。 タグと属性の単なるホワイトリストよりも洗練されたポリシーが必要な場合は、この方法で対応できますが、このコード例では対応できません。

var tagWhitelist_ = {'A':真、 'B':真、 'BODY':真、 'BR':真、 'DIV':真、 'EM':真、 'HR':真、 'I': true、 'IMG':true、 'P':true、 'SPAN':true、 'STRONG':true};

var attributeWhitelist_ = {'href':true、 'src':true};

関数sanitizeHtml(入力){var iframe = document.createElement( 'iframe'); if(iframe ['sandbox'] === undefined){alert( 'お使いのブラウザはサンドボックス化されたiframeをサポートしていません。 最新のブラウザにアップグレードしてください。 ''を返します。 iframe ['sandbox'] = 'allow-same-origin'; iframe.style.display = 'none'; document.body.appendChild(iframe); // iframeにドキュメントが含まれるようにする必要があるiframe.contentDocument.body.innerHTML = input;

関数makeSanitizedCopy(node){if(node.nodeType == Node.TEXT_NODE){var newNode = node.cloneNode(true);そうでなければ(node.nodeType == Node.ELEMENT_NODE

var resultElement = makeSanitizedCopy(iframe.contentDocument.body); document.body.removeChild(iframe); resultElement.innerHTMLを返します。 ;

あなたはそれを試してみることができます here

この例ではスタイル属性とタグは許可されていません。 許可した場合は、おそらくCSSを解析し、それがあなたの目的にとって安全であることを確認したいと思うでしょう。

私はこれをいくつかの最近のブラウザ(Chrome 40、Firefox 36ベータ版、IE 11、Android版Chrome)、そして古いブラウザ(IE 8)でテストし、スクリプトを実行する前にそれが失敗することを確認しました。 問題があるブラウザがあるかどうか、または私が見逃しているエッジケースがあるかどうかを知りたいと思います。


7


それで、それは2016年です、そして私たちの多くは今私たちのコードで npm`モジュールを使っていると思います。 https://www.npmjs.com/package/sanitize-html [`sanitize-html]はhttps://www.npmjs.com/browse/keyword/sanitize[othersがありますがnpmの主要なオプションのように思えます]。

この質問に対する他の回答では、自分自身をロールバックする方法について優れた情報が提供されていますが、十分にテストされたコミュニティソリューションがおそらく最善の解決策であるという十分に難しい問題です。

インストールするためにコマンドラインでこれを実行してください: npm install --save sanitize-html

ES5: `var sanitizeHtml = require( 'sanitize-html'); // …​ var sanitized = sanitizeHtml(htmlInput); `

ES6: `sanitize-htmlからsanitizeHtmlをインポートする; // …​ sanitized = sanitizeHtml(htmlInput); `とします。


5


String.prototype.sanitizeHTML = function(white、black){if(!white)white = "b | i | p | br"; // ifタグの許可(!black)black = "script | object | embed"; / /完全にタグを削除var e = new RegExp( "(<(" black ")[^>] *>。* |(?!<[/]?(" white ")(\\ s [^ <] *> | [/]> |>))<[^ <>] *> |(?!<[^ <> \\ s])\\ s [^ </>](?= [/>])) " 、 "gi"); return this.replace(e、 ""); }
  • ブラックリスト - >タグとコンテンツを完全に削除

  • ホワイトリスト - >タグを保持

その他のタグは削除されますがタグの内容は保持されます

ホワイトリストタグのすべての属性(残りのもの)は削除されます


1


上記で提案したGoogle Cajaライブラリは、Webアプリケーション用に設定してプロジェクトに含めるには複雑すぎるため、ブラウザ上で実行しています。 私たちがすでにCKEditorコンポーネントを使用しているので、代わりに私が頼ったのは、組み込みのHTMLサニタイズおよびホワイトリスト機能を使用することです。これは設定がはるかに簡単です。 そのため、隠しiframeにCKEditorインスタンスをロードして、次のようにすることができます。

CKEDITOR.instances['myCKEInstance'].dataProcessor.toHtml(myHTMLstring)

プロジェクトでCKEditorを使用していないのであれば、コンポーネント自体は約半分のメガバイト(最小化)であるため、これはやややり過ぎになるかもしれません。ホワイトリスト( CKEDITOR.htmlParser?)とそれをもっと短くする。


1


_ [免責事項:私は著者の一人] _

私達は「ウェブのみ」を書いた。 これには「ブラウザが必要です」オープンソースライブラリ、https://github.com/jitbit/HtmlSanitizerが「ホワイトリスト」以外のすべての「タグ/属性/スタイル」を削除します。

使用法:

var input = HtmlSanitizer.SanitizeHtml(" Alert('xss!'); </scr"+"ipt>");
</code></pre>

<p>P.S. Works much faster than a "pure JavaScript" solution since it uses the browser to parse and manipulate DOM. If you're interested in a "pure JS" solution please try <a href="https://github.com/punkave/sanitize-html" rel="nofollow noreferrer">https://github.com/punkave/sanitize-html</a> (not affiliated)</p>


0


私はあなたの人生からフレームワークを切り取ることを勧めます、それはあなたにとって長期的に物事を過度に容易にするでしょう。

cloneNode:ノードのクローンを作成すると、そのすべての属性とその値がコピーされますが、イベントリスナはコピーされません

私はしばらく前からtreewalkerを使ってきましたが、それらはJavaScriptの最も過小評価された部分の1つですが、以下はテストされていません。 これは、クロールできるノードタイプのリストです。通常は* SHOW_ELEMENT または SHOW_TEXT *を使用します。

function xhtml_cleaner(id){変数= document.getElementById(id); var f = document.createDocumentFragment(); f.appendChild(e.cloneNode(true));

var walker = document.createTreeWalker(f、NodeFilter.SHOW_ELEMENT、null、false);

while(walker.nextNode()){var c = walker.currentNode; if(c.hasAttribute( 'contentEditable')){c.removeAttribute( 'contentEditable');} if(c.hasAttribute( 'style')){c.removeAttribute( 'style');}

if(c.nodeName.toLowerCase()== 'スクリプト'){element_del(c);}}

alert(new XMLSerializer()。serializeToString(f)); fを返します。 }

function element_del(element_id){if(document.getElementById(element_id)){document.getElementById(element_id).parentNode.removeChild(document.getElementById(element_id));そうでなければ(element_id){element_id.parentNode.removeChild(element_id); } else {alert( 'エラー:オブジェクトまたは要素\' 'element_id' \ 'が見つからなかったため削除できませんでした。); }}