Posso chiedere a un browser di non eseguire <script> all'interno di un elemento?


84

È possibile dire ai browser di non eseguire JavaScript da parti specifiche di un documento HTML?

Piace:

<div script="false"> ...

Potrebbe essere utile come funzionalità di sicurezza aggiuntiva. Tutti gli script che desidero vengono caricati in una parte specifica del documento. Non dovrebbero esserci script in altre parti del documento e se ci sono non dovrebbero essere eseguiti.


1
Non che io sappia. Questo è un commento piuttosto che una risposta, perché non posso dire 100%
caduta libera

4
@ chiastic-security È possibile attraversare il DOM solo dopo che il DOM è stato caricato. A quel punto qualsiasi JS in quel blocco sarebbe stato eseguito.
cappucci

8
Il più vicino a cui riesco a pensare è la politica di sicurezza dei contenuti, in cui puoi limitare gli script in base alla loro origine (forse è quello che vuoi). Ad esempio, specificando script-src:"self"di consentire solo l'esecuzione degli script del tuo dominio nella pagina. Se sei interessato, leggi questo articolo di Mike West su CSP .
Christoph

1
@ chiastic-security come pensate di rilassare una restrizione specificata in un'intestazione dopo che le intestazioni sono già state inviate? Ad ogni modo, poiché CSP disabilita gli script inline per impostazione predefinita e gli script remoti non verranno caricati a meno che non vengano inseriti nella whitelist, perché dovresti combinarlo con qualcos'altro? CSP è probabilmente la migliore scommessa dell'OP; Spero che qualcuno lasci una risposta CSP.
Dagg Nabbit

6
Se sei preoccupato per qualcuno che inserisce blocchi arbitrari nel tuo HTML, in che modo la tua soluzione proposta impedirà loro di iniettare un </div>per chiudere questo elemento DOM e quindi avviarne uno nuovo <div>che sarà un fratello di quello in cui gli script non sono in esecuzione?
Damien_The_Unbeliever

Risposte:


92

SÌ, puoi :-) La risposta è: Content Security Policy (CSP).

La maggior parte dei browser moderni supporta questo flag , che dice al browser solo di caricare il codice JavaScript da un file esterno attendibile e di non consentire tutto il codice JavaScript interno! L'unico svantaggio è che non puoi utilizzare alcun JavaScript in linea in tutta la tua pagina (non solo per un singolo <div>). Anche se potrebbe esserci una soluzione alternativa includendo dinamicamente il div da un file esterno con una politica di sicurezza diversa, ma non ne sono sicuro.

Ma se puoi cambiare il tuo sito per caricare tutto JavaScript da file JavaScript esterni, puoi disabilitare del tutto JavaScript in linea con questa intestazione!

Ecco un bel tutorial con un esempio: HTML5Rocks Tutorial

Se puoi configurare il server per inviare questo flag HTTP-Header, il mondo sarà un posto migliore!


2
+1 In realtà è davvero fantastico, non avevo idea che esistesse! (una nota, il tuo link wiki è direttamente alla versione tedesca) Ecco la carrellata sul supporto del browser: caniuse.com/#feat=contentsecuritypolicy
BrianH

4
Si noti che anche se si esegue questa operazione, consentire l'input dell'utente senza caratteri di escape su una pagina è comunque una cattiva idea. Ciò consentirebbe fondamentalmente all'utente di cambiare la pagina in modo che appaia come preferisce. Anche con tutte le impostazioni CSP (Content Security Policy) impostate al massimo (che non consentono script inline, stili, ecc.), L'utente potrebbe comunque eseguire un attacco CSRF (Cross-Site Request Forgery) utilizzando srcs di immagine per le richieste GET o ingannando l'utente in facendo clic su un pulsante di invio del modulo per le richieste POST.
Ajedi32

@ Ajedi32 Ovviamente dovresti sempre disinfettare gli input degli utenti. Ma CSP può anche impostare criteri per le richieste GET come immagini o css, non solo le bloccherà, ma ne informerà anche il tuo server!
Falco

1
@Falco Ho letto i documenti e ho capito che puoi limitare quelle richieste solo a un determinato dominio, non a un insieme specifico di sottopagine del dominio. Presumibilmente il dominio che hai impostato sarebbe lo stesso del tuo sito, il che significa che saresti ancora aperto agli attacchi CSRF.
Ajedi32

3
@Falco Sì, questo è fondamentalmente ciò che ha fatto StackExchange con la nuova funzionalità Stack Snippets: blog.stackoverflow.com/2014/09/… Se si disinfetta correttamente l'input, tuttavia, non è necessario un dominio separato.
Ajedi32

13

Puoi bloccare JavaScript caricato da <script>, utilizzando l' beforescriptexecuteevento:

<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>

Nota che è beforescriptexecutestato definito in HTML 5.0 ma è stato rimosso in HTML 5.1. Firefox è l'unico browser principale che lo ha implementato.

Nel caso in cui stai inserendo un gruppo di HTML non attendibile nella tua pagina, tieni presente che il blocco degli script all'interno di quell'elemento non fornirà maggiore sicurezza, perché l'HTML non affidabile può chiudere l'elemento sandbox, e quindi lo script verrà posizionato all'esterno ed eseguito.

E questo non bloccherà cose come <img onerror="javascript:alert('foo')" src="//" />.


Il violino non sembra funzionare come previsto. Non dovrei essere in grado di vedere la parte "bloccata", giusto?
Salman A

@SalmanA Esattamente. Probabilmente, il tuo browser non supporta l' beforescriptexecuteevento. Funziona su Firefox.
Oriol

Probabilmente perché non funziona in Chrome, con lo snippet fornito, anche se vedo che l'hai appena convertito in uno snippet :-)
Mark Hurd

beforescriptexecutesembra che non sia supportato e non sarà supportato dalla maggior parte dei principali browser. developer.mozilla.org/en-US/docs/Web/Events/beforescriptexecute
Matt Pennington

8

Domanda interessante, non credo sia possibile. Ma anche se lo fosse, sembra che sarebbe un hack.

Se i contenuti di quel div non sono attendibili, è necessario eseguire l'escape dei dati sul lato server prima che vengano inviati nella risposta HTTP e visualizzati nel browser.

Se vuoi solo rimuovere i <script>tag e consentire altri tag html, rimuovili semplicemente dal contenuto e lascia il resto.

Considera la prevenzione XSS.

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


7

JavaScript viene eseguito "inline", cioè nell'ordine in cui appare nel DOM (se così non fosse, non potresti mai essere sicuro che qualche variabile definita in uno script diverso fosse visibile quando l'hai usata per la prima volta ).

Quindi ciò significa che in teoria potresti avere uno script all'inizio della pagina (cioè il primo <script>elemento) che guarda attraverso il DOM e rimuove tutti gli <script>elementi e gestori di eventi all'interno del tuo <div>.

Ma la realtà è più complessa: il caricamento di DOM e script avviene in modo asincrono. Ciò significa che il browser garantisce solo che uno script possa vedere la parte del DOM che si trova prima di esso (cioè l'intestazione fino ad ora nel nostro esempio). Non ci sono garanzie per nulla oltre (questo è correlato a document.write()). Quindi potresti vedere il prossimo tag di script o forse no.

Potresti agganciarti onloadall'evento del documento, il che ti assicurerebbe di ottenere l'intero DOM, ma in quel momento il codice dannoso potrebbe essere già stato eseguito. Le cose peggiorano quando altri script manipolano il DOM, aggiungendo script lì. Quindi dovresti controllare anche ogni modifica del DOM.

Quindi la soluzione @cowls (filtraggio sul server) è l'unica soluzione che può essere fatta funzionare in tutte le situazioni.


1

Se stai cercando di visualizzare il codice JavaScript nel tuo browser:

Utilizzando JavaScript e HTML, dovrai utilizzare entità HTML per visualizzare il codice JavaScript ed evitare che questo codice venga eseguito. Qui puoi trovare l'elenco delle entità HTML:

Se stai usando un linguaggio di scripting lato server (PHP, ASP.NET, ecc.), Molto probabilmente, c'è una funzione che farebbe l'escape di una stringa e convertirà i caratteri speciali in entità HTML. In PHP, useresti "htmlspecialchars ()" o "htmlentities ()". Quest'ultimo copre tutti i caratteri HTML.

Se stai cercando di visualizzare il tuo codice JavaScript in un modo carino, prova uno degli evidenziatori di codice:


1

Ho una teoria:

  • Avvolgi la parte specifica del documento all'interno di un noscripttag.
  • Usa le funzioni DOM per scartare tutti i scripttag all'interno del noscripttag e poi scartare i suoi contenuti.

Esempio di prova di concetto:

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>


Se il contenuto nel div non è attendibile, suppongo che venga data la domanda. Potrebbero semplicemente chiudere il <noscript>tag e poi iniettare quello che vogliono.
cappucci

Sì, la soluzione corretta è risolvere il problema sul lato server. Quello che sto facendo tramite JavaScript dovrebbe essere fatto meglio sul lato server.
Salman A,

0

Se si desidera riattivare i tag di script in un secondo momento, la mia soluzione è stata quella di interrompere l'ambiente del browser in modo che qualsiasi script eseguito generasse un errore abbastanza presto. Tuttavia, non è totalmente affidabile, quindi non puoi usarlo come funzione di sicurezza.

Se provi ad accedere alle proprietà globali, Chrome genererà un'eccezione.

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

Sto sovrascrivendo tutte le proprietà sovrascrivibili window, ma potresti anche espanderlo per interrompere altre funzionalità.

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) {}
        }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.