要素内で<script>を実行しないようにブラウザに要求できますか?


84

HTMLドキュメントの特定の部分からJavaScriptを実行しないようにブラウザに指示することは可能ですか?

お気に入り:

<div script="false"> ...

追加のセキュリティ機能として役立つ可能性があります。必要なすべてのスクリプトは、ドキュメントの特定の部分にロードされます。ドキュメントの他の部分にスクリプトがあってはならず、スクリプトがある場合は実行しないでください。


1
私が知っていることではありません。100%とは言えないので、これは答えではなくコメントです
freefaller 2014

4
@ chiastic-security DOMをトラバースできるのは、DOMがロードされた後だけです。その時点で、そのブロック内のJSが実行されます。
カウル2014

8
私が考えることができる最も近いものは、コンテンツセキュリティポリシーです。このポリシーでは、スクリプトをその発信元によって制限できます(おそらくそれが必要です)。たとえばscript-src:"self"、ドメインのスクリプトのみをページで実行することを許可するように指定します。興味がある場合は、CSPに関するMikeWestのこの記事を読んでください
クリストフ

1
@ chiastic-securityヘッダーがすでに送信された後、ヘッダーで指定された制限をどのように緩和する予定ですか?とにかく、CSPはデフォルトでインラインスクリプトを無効にし、ホワイトリストに登録されない限りリモートスクリプトは読み込まれないので、なぜそれを他のものと組み合わせる必要があるのでしょうか。CSPはおそらくOPの最善の策です。誰かがCSPの答えを残してくれることを願っています。
Dagg Nabbit 2014

6
誰かがHTMLに任意のブロックを挿入することを懸念している場合、提案されたソリューションは、</div>このDOM要素を閉じて<div>、スクリプトが実行されていないものの兄弟となる新しい要素を開始することをどのように防止しますか?
damien_The_Unbeliever 2014

回答:


92

はい、できます:-)答えは次のとおりです:コンテンツセキュリティポリシー(CSP)。

最近のほとんどのブラウザはこのフラグをサポートしています。このフラグは、信頼できる外部ファイルからJavaScriptコードをロードし、すべての内部JavaScriptコードを禁止するようにブラウザに指示します。唯一の欠点は、ページ全体でインラインJavaScriptを使用できないことです(単一のJavaScriptだけでなく<div>)。異なるセキュリティポリシーを持つ外部ファイルからdivを動的に含めることで回避策がある可能性がありますが、それについてはよくわかりません。

ただし、外部JavaScriptファイルからすべてのJavaScriptをロードするようにサイトを変更できる場合は、このヘッダーを使用してインラインJavaScriptを完全に無効にすることができます。

これが例のある素晴らしいチュートリアルです:HTML5Rocksチュートリアル

このHTTPヘッダーフラグを送信するようにサーバーを構成できれば、世界はより良い場所になります!


2
+1それは実際には本当にクールです、私は存在することを知りませんでした!ここでは、ブラウザのサポートの荒廃だ(1つのノートでは、あなたのwikiリンクは直接ドイツ語版にある):caniuse.com/#feat=contentsecuritypolicy
BrianH

4
これを行ったとしても、ページ上でエスケープされていないユーザー入力を許可することは依然として悪い考えであることに注意してください。これにより、基本的にユーザーはページを好きなように変更できます。すべてのコンテンツセキュリティポリシー(CSP)設定が最大に設定されていても(インラインスクリプト、スタイルなどを禁止)、ユーザーはGETリクエストの画像srcを使用して、またはユーザーをだましてクロスサイトリクエストフォージェリ(CSRF)攻撃を実行できます。 POSTリクエストのフォーム送信ボタンをクリックします。
ajedi32 2014

@ Ajedi32もちろん、ユーザー入力は常にサニタイズする必要があります。しかし、CSPは画像やcssなどのGETリクエストのポリシーを設定することもでき、それらをブロックするだけでなく、サーバーに通知することもできます。
ファルコ

1
@Falcoドキュメントを読みましが、これらのリクエストは特定のドメインにのみ制限でき、ドメイン上の特定のサブページのセットには制限できないと理解していました。おそらく、設定したドメインはサイトと同じドメインであり、CSRF攻撃に対して引き続きオープンであることを意味します。
ajedi32 2014

3
@Falcoええ、それは基本的にStackExchangeが新しいスタックスニペット機能で行ったことです:blog.stackoverflow.com/2014/09/…ただし、入力を適切にサニタイズする場合は、個別のドメインは必要ありません。
ajedi32 2014

13

イベント<script>を使用して、によってロードされたJavaScriptをブロックできますbeforescriptexecute

<script>
  // Run this as early as possible, it isn't retroactive
  window.addEventListener('beforescriptexecute', function(e) {
    var el = e.target;
    while(el = el.parentElement)
      if(el.hasAttribute('data-no-js'))
        return e.preventDefault(); // Block script
  }, true);
</script>

<script>console.log('Allowed. Console is expected to show this');</script>
<div data-no-js>
  <script>console.log('Blocked. Console is expected to NOT show this');</script>
</div>

これbeforescriptexecuteはHTML5.0で定義されていましたが、HTML5.1では削除されていることに注意してください。Firefoxはそれを実装した唯一の主要なブラウザです。

信頼できないHTMLの束をページに挿入する場合、その要素内でスクリプトをブロックしてもセキュリティが向上しないことに注意してください。信頼できないHTMLはサンドボックス化された要素を閉じる可能性があるため、スクリプトは外部に配置されて実行されます。

そして、これはのようなものをブロックしません<img onerror="javascript:alert('foo')" src="//" />


フィドルは期待どおりに機能していないようです。「ブロックされた」部分が見えないはずですよね?
Salman A

@SalmanAその通り。おそらく、お使いのブラウザはbeforescriptexecuteイベントをサポートしていません。Firefoxで動作します。
オリオール2014

おそらく、Chromeで機能しておらず、スニペットが提供されているためですが、スニペットに変換しただけだと思います:-)
Mark Hurd

beforescriptexecuteサポートされていないようで、ほとんどの主要なブラウザではサポートされません。 developer.mozilla.org/en-US/docs/Web/Events/beforescriptexecute
Matt Pennington 2016年

8

興味深い質問ですが、それは不可能だと思います。しかし、そうだとしても、それはハックのように聞こえます。

そのdivの内容が信頼できない場合は、HTTP応答で送信されてブラウザーにレンダリングされる前に、サーバー側でデータをエスケープする必要があります。

<script>タグを削除して他のhtmlタグのみを許可する場合は、コンテンツからそれらを削除し、残りを残します。

XSS防止について調べてください。

https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet


7

JavaScriptは「インライン」で実行されます。つまり、DOMに表示される順序で実行されます(そうでない場合は、別のスクリプトで定義された変数が初めて使用されたときに表示されるかどうかを確認できませんでした。 )。

つまり、理論的には、ページの先頭(つまり、最初の<script>要素)に、DOMを調べて、<script>内のすべての要素とイベントハンドラーを削除するスクリプトを作成できます<div>

しかし、現実はもっと複雑です。DOMとスクリプトのロードは非同期で行われます。これは、ブラウザーが、スクリプトがそのにあるDOMの部分(つまり、この例ではこれまでのヘッダー)を認識できることのみを保証することを意味します。それ以上の保証はありません(これはに関連していますdocument.write())。したがって、次のスクリプトタグが表示される場合と、表示されない場合があります。

onloadドキュメントのイベントにラッチすることもできますが(これにより、DOM全体を確実に取得できます)、その時点で、悪意のあるコードがすでに実行されている可能性があります。他のスクリプトがDOMを操作し、そこにスクリプトを追加すると、事態はさらに悪化します。したがって、DOMのすべての変更もチェックする必要があります。

したがって、@ cowlsソリューション(サーバーでのフィルタリング)は、すべての状況で機能させることができる唯一のソリューションです。


1

ブラウザにJavaScriptコードを表示する場合:

JavaScriptとHTMLを使用すると、HTMLエンティティを使用してJavaScriptコードを表示し、このコードが実行されないようにする必要があります。ここに、HTMLエンティティのリストがあります。

サーバーサイドスクリプト言語(PHP、ASP.NETなど)を使用している場合は、おそらく、文字列をエスケープして特殊文字をHTMLエンティティに変換する関数があります。PHPでは、「htmlspecialchars()」または「htmlentities()」を使用します。後者はすべてのHTML文字をカバーします。

JavaScriptコードを適切に表示したい場合は、次のコードハイライターのいずれかを試してください。


1

私は理論を持っています:

  • ドキュメントの特定の部分をnoscriptタグで囲みます。
  • DOM関数を使用しscriptて、タグ内のすべてのタグを破棄しnoscript、その内容をアンラップします。

概念実証の例:

window.onload = function() {
    var noscripts = /* _live_ list */ document.getElementsByTagName("noscript"),
        memorydiv = document.createElement("div"),
        scripts = /* _live_ list */ memorydiv.getElementsByTagName("script"),
        i,
        j;
    for (i = noscripts.length - 1; i >= 0; --i) {
        memorydiv.innerHTML = noscripts[i].textContent || noscripts[i].innerText;
        for (j = scripts.length - 1; j >= 0; --j) {
            memorydiv.removeChild(scripts[j]);
        }
        while (memorydiv.firstChild) {
            noscripts[i].parentNode.insertBefore(memorydiv.firstChild, noscripts[i]);
        }
        noscripts[i].parentNode.removeChild(noscripts[i]);
    }
};
body { font: medium/1.5 monospace; }
p, h1 { margin: 0; }
<h1>Sample Content</h1>
<p>1. This paragraph is embedded in HTML</p>
<script>document.write('<p style="color: red;">2. This paragraph is generated by JavaScript</p>');</script>
<p>3. This paragraph is embedded in HTML</p>
<h1>Sample Content in No-JavaScript Zone</h1>
<noscript>
    <p>1. This paragraph is embedded in HTML</p>
    <script>document.write('<p style="color: red;">2. This paragraph is generated by JavaScript</p>');</script>
    <p>3. This paragraph is embedded in HTML</p>
</noscript>
<noscript>
    <p>1. This paragraph is embedded in HTML</p>
    <script>document.write('<p style="color: red;">2. This paragraph is generated by JavaScript</p>');</script>
    <p>3. This paragraph is embedded in HTML</p>
</noscript>


divのコンテンツが信頼できない場合は、質問が表示されると思います。<noscript>タグを閉じて、好きなものを挿入するだけです。
カウル2014

はい、適切な解決策はサーバー側で問題を修正することです。私がJavaScriptを介して行っていることは、サーバー側で行う方がよいでしょう。
Salman A

0

後でスクリプトタグを再度有効にしたい場合、私の解決策はブラウザ環境を壊して、実行されるスクリプトがかなり早い段階でエラーをスローするようにすることでした。ただし、完全に信頼できるわけではないため、セキュリティ機能として使用することはできません。

グローバルプロパティにアクセスしようとすると、Chromeは例外をスローします。

setTimeout("Math.random()")
// => VM116:25 Uncaught Error: JavaScript Execution Inhibited  

のすべての上書き可能なプロパティを上書きしてwindowいますが、他の機能を壊すために拡張することもできます。

window.allowJSExecution = inhibitJavaScriptExecution();
function inhibitJavaScriptExecution(){

    var windowProperties = {};
    var Object = window.Object
    var console = window.console
    var Error = window.Error

    function getPropertyDescriptor(object, propertyName){
        var descriptor = Object.getOwnPropertyDescriptor(object, propertyName);
        if (!descriptor) {
            return getPropertyDescriptor(Object.getPrototypeOf(object), propertyName);
        }
        return descriptor;
    }

    for (var propName in window){
        try {
            windowProperties[propName] = getPropertyDescriptor(window, propName)
            Object.defineProperty(window, propName, {
                get: function(){
                    throw Error("JavaScript Execution Inhibited")
                },
                set: function(){
                    throw Error("JavaScript Execution Inhibited")
                },
                configurable: true
            })
        } catch (err) {}
    }

    return function allowJSExecution(){
        for (var propName in window){
            if (!(propName in windowProperties)) {
                delete windowProperties[propName]
            }
        }

        for (var propName in windowProperties){
            try {
                Object.defineProperty(window, propName, windowProperties[propName])
            } catch (err) {}
        }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.