Ausführen mit .innerHTML eingefügte Elemente

Lesezeit: 10 Minuten

Ausfuhren mit innerHTML eingefugte Elemente
Phidah

Ich habe ein Skript, das mithilfe von Inhalt in ein Element einfügt innerHTML.

Inhalte könnten beispielsweise sein:

<script type="text/javascript">alert('test');</script>
<strong>test</strong>

Das Problem ist, dass der Code in der <script> Tag wird nicht ausgeführt. Ich habe ein bisschen gegoogelt, aber es gab keine offensichtlichen Lösungen. Wenn ich den Inhalt mit jQuery eingefügt habe $(element).append(content);die Skriptteile erhalten eval‘d, bevor es in das DOM injiziert wurde.

Hat jemand einen Codeschnipsel, der alle ausführt <script> Elemente? Der jQuery-Code war ein bisschen komplex, daher konnte ich nicht wirklich herausfinden, wie es gemacht wurde.

Bearbeiten:

Durch einen Blick in den jQuery-Code konnte ich herausfinden, wie jQuery das macht, was zu folgendem Code führte:

Demo:
<div id="element"></div>

<script type="text/javascript">
  function insertAndExecute(id, text)
  {
    domelement = document.getElementById(id);
    domelement.innerHTML = text;
    var scripts = [];

    ret = domelement.childNodes;
    for ( var i = 0; ret[i]; i++ ) {
      if ( scripts && nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
            scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
        }
    }

    for(script in scripts)
    {
      evalScript(scripts[script]);
    }
  }
  function nodeName( elem, name ) {
    return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
  }
  function evalScript( elem ) {
    data = ( elem.text || elem.textContent || elem.innerHTML || "" );

    var head = document.getElementsByTagName("head")[0] || document.documentElement,
    script = document.createElement("script");
    script.type = "text/javascript";
    script.appendChild( document.createTextNode( data ) );
    head.insertBefore( script, head.firstChild );
    head.removeChild( script );

    if ( elem.parentNode ) {
        elem.parentNode.removeChild( elem );
    }
  }

  insertAndExecute("element", "<scri"+"pt type="text/javascript">document.write('This text should appear as well.')</scr"+"ipt><strong>this text should also be inserted.</strong>");
</script>

  • Warum können Sie nicht einfach die untergeordneten Elemente des Elements iterieren, und für jedes Element, das ein Skriptelement ist, eval() einfach das innerHtml dieses untergeordneten Elements auswerten? So habe ich es von einem großen Komponentenanbieter gesehen, jedes Mal, wenn sie einen Ajax-Callback abschließen, der Dinge zum DOM hinzufügt, tun sie genau das. Beachten Sie jedoch, dass es langsam sein kann, insbesondere in IE7.

    – Schläger

    7. April 2010 um 12:33 Uhr

  • Andreas: Wenn ich zum Beispiel eine Funktion hinzufüge function testFunction(){ alert('test'); } zu dem in innerHTML eingefügten Code und versuchen Sie es dann aufzurufen, heißt es, dass die Funktion nicht definiert ist.

    – Phidah

    7. April 2010 um 12:52 Uhr

  • Ehrfürchtige Phidah, funktioniert wie Charme, Prost

    – Marcin

    14. Oktober 2012 um 17:12 Uhr

  • Ich denke, es ist absolut wichtig zu verstehen, dass dies ein beabsichtigtes Verhalten des Browsers ist, um Cross-Site-Scripting-Angriffe zu verhindern. Wenn der Text, den Sie als innerHTML festlegen, von Bob bereitgestellt wird, würde er auf Alices Browser ausgeführt werden und Schaden anrichten (denken Sie an ein Forum, in dem Leute Kommentare schreiben können, die ihnen Skript-Tags hinzufügen). Hier kannst du mehr darüber lesen: en.wikipedia.org/wiki/Cross-site_scripting. Bleib sicher!

    – Xatian

    3. Mai 2017 um 9:08 Uhr

  • A HTML hat sich seit 2010 stark verändert. Heutzutage möchten Sie vielleicht nachsehen: stackoverflow.com/a/58862506/890357

    – marciowb

    14. November 2019 um 19:24 Uhr


1646642112 623 Ausfuhren mit innerHTML eingefugte Elemente
allenhwkim

Vereinfachte ES6-Version von @joshcomleys Antwort mit einem Beispiel.

Kein JQuery, keine Bibliothek, kein Test, keine DOM-Änderung, nur reines Javascript.

http://plnkr.co/edit/MMegiu?p=preview

var setInnerHTML = function(elm, html) {
  elm.innerHTML = html;
  Array.from(elm.querySelectorAll("script")).forEach( oldScript => {
    const newScript = document.createElement("script");
    Array.from(oldScript.attributes)
      .forEach( attr => newScript.setAttribute(attr.name, attr.value) );
    newScript.appendChild(document.createTextNode(oldScript.innerHTML));
    oldScript.parentNode.replaceChild(newScript, oldScript);
  });
}

Verwendungszweck

$0.innerHTML = HTML;    // does *NOT* run <script> tags in HTML
setInnerHTML($0, HTML); // does run <script> tags in HTML

  • Tippfehler: Der Name der Funktion ist setInnerHtml nicht setInnerHTML

    – pery mimon

    12. August 2018 um 15:30 Uhr

  • Beachten Sie, dass in dem verknüpften plnkr der Funktionsname lautet setInnerHTML aber es wird gerufen setInnerHtml von innen runB Funktion. Daher funktioniert das Beispiel nicht

    – Danbars

    1. August 2019 um 3:28 Uhr

  • Arbeitsbeispiel plnkr.co/edit/b54rQgoSkguOFpIJ

    – Visier

    10. Februar 2021 um 12:03 Uhr

  • Ich habe ein Problem mit VUE und v-html, wo ich etwas HTML mit Skripten einfügen möchte … das hilft mir, mein Problem zu beheben. stackoverflow.com/questions/68042540/…

    – MiBol

    21. Juni 2021 um 14:30 Uhr

@phidah … Hier ist eine sehr interessante Lösung für Ihr Problem:
http://24ways.org/2005/have-your-dom-and-script-it-too

Also würde es stattdessen so aussehen:

<img src="https://stackoverflow.com/questions/2592092/empty.gif" onload="alert('test');this.parentNode.removeChild(this);" />

  • und noch besser, es ist kein Image mit “onerror”-Ereignis erforderlich, was für eine schnelle XSS-Injektion geeignet ist jvfconsulting.com/blog/47/… 🙂

    – tauf

    30. Juni 2012 um 13:58 Uhr


  • Sie können verwenden <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" onload="alert('test');"> wenn Sie eine nutzlose HTTP-Anfrage verhindern möchten.

    – Savas Vedova

    14. April 2015 um 8:56 Uhr

  • Liebe es ! (hinzugefügt style="display:none;), um das defekte Bildsymbol auszublenden

    – kris

    26. April 2017 um 5:57 Uhr

  • Tatsächlich, <style> ist besser als <img>weil es keine Netzwerkanfrage stellt

    – Becken

    22. März 2019 um 8:01 Uhr

  • Ja, @basin ist richtig. ich benutzte <style onload="alert('test');"/> und es funktionierte wie ein Zauber. Keine Netzwerkanfrage, minimale Vergrößerung des Dokuments/der Anfrage, unsichtbar… Wenn Sie es trotzdem entfernen möchten, können Sie auch den Trick removeChild verwenden. Danke!

    – Bestritter

    24. August 2021 um 19:36 Uhr

1646642113 610 Ausfuhren mit innerHTML eingefugte Elemente
Andreas

Sie sollten nicht die innerHTML-Eigenschaft verwenden, sondern die appendChild-Methode des Knotens: ein Knoten in einem Dokumentbaum [HTML DOM]. Auf diese Weise können Sie später Ihren eingeschleusten Code aufrufen.

Stellen Sie sicher, dass Sie das verstehen node.innerHTML ist nicht dasselbe wie node.appendChild. Vielleicht möchten Sie einige Zeit mit der Javascript-Client-Referenz verbringen, um weitere Details und das DOM zu erhalten. Hoffe das folgende hilft…

Probeninjektion funktioniert:

<!DOCTYPE HTML>
<html>
<head>
    <title>test</title>
    <script language="javascript" type="text/javascript">
        function doOnLoad() {
            addScript('inject',"function foo(){ alert('injected'); }");
        }
    
        function addScript(inject,code) {
            var _in = document.getElementById('inject');
            var scriptNode = document.createElement('script');
            scriptNode.innerHTML = code;
            _in.appendChild(scriptNode);
        }
    </script>
</head>
<body onload="doOnLoad();">
    <div id="header">some content</div>
    <div id="inject"></div>
    <input type="button" onclick="foo(); return false;" value="Test Injected" />
</body>
</html>

Grüße

  • Endlich jemand, der das Problem tatsächlich ein wenig erklärt und nicht alles andere try this, look how clever I am Antworten. Verdient eine UV, es hat meine bekommen.

    – RiggsFolly

    7. Februar 2016 um 12:05 Uhr

  • Ich uv dies, weil es die einfachste Möglichkeit ist, Javascript-Code einzufügen, der nach dem Einfügen ausgeführt wird. Ich verstehe nur nicht den Unterschied zwischen dem Hinzufügen mit innerHTML, das nicht ausgeführt wird, und dem Weg oben mit appendChild, das ausgeführt wird. Ich habe dies erfolgreich verwendet, um mit socket.io eine dynamische Seite mit Skript von Grund auf neu zu erstellen

    – Nasslippe

    26. Dezember 2016 um 10:21 Uhr


  • var _in = document.getElementById(inject);Ich denke.

    – Ron Burk

    16. Dezember 2017 um 6:40 Uhr

  • Ok, aber ich bekomme eine Fehlermeldung: parameter 1 is not of type 'Node'.

    – Camo

    3. August 2021 um 12:45 Uhr


Das Skript des OP funktioniert nicht in IE 7. Mit Hilfe von SO ist hier ein Skript, das dies tut:

exec_body_scripts: function(body_el) {
  // Finds and executes scripts in a newly added element's body.
  // Needed since innerHTML does not run scripts.
  //
  // Argument body_el is an element in the dom.

  function nodeName(elem, name) {
    return elem.nodeName && elem.nodeName.toUpperCase() ===
              name.toUpperCase();
  };

  function evalScript(elem) {
    var data = (elem.text || elem.textContent || elem.innerHTML || "" ),
        head = document.getElementsByTagName("head")[0] ||
                  document.documentElement,
        script = document.createElement("script");

    script.type = "text/javascript";
    try {
      // doesn't work on ie...
      script.appendChild(document.createTextNode(data));      
    } catch(e) {
      // IE has funky script nodes
      script.text = data;
    }

    head.insertBefore(script, head.firstChild);
    head.removeChild(script);
  };

  // main section of function
  var scripts = [],
      script,
      children_nodes = body_el.childNodes,
      child,
      i;

  for (i = 0; children_nodes[i]; i++) {
    child = children_nodes[i];
    if (nodeName(child, "script" ) &&
      (!child.type || child.type.toLowerCase() === "text/javascript")) {
          scripts.push(child);
      }
  }

  for (i = 0; scripts[i]; i++) {
    script = scripts[i];
    if (script.parentNode) {script.parentNode.removeChild(script);}
    evalScript(scripts[i]);
  }
};

1646642114 311 Ausfuhren mit innerHTML eingefugte Elemente
GeteiltdurchNull

Hier ist ein kürzeres, effizienteres Skript, das auch für Skripte mit dem funktioniert src Eigentum:

function insertAndExecute(id, text) {
    document.getElementById(id).innerHTML = text;
    var scripts = Array.prototype.slice.call(document.getElementById(id).getElementsByTagName("script"));
    for (var i = 0; i < scripts.length; i++) {
        if (scripts[i].src != "") {
            var tag = document.createElement("script");
            tag.src = scripts[i].src;
            document.getElementsByTagName("head")[0].appendChild(tag);
        }
        else {
            eval(scripts[i].innerHTML);
        }
    }
}

Hinweis: während eval kann eine Sicherheitslücke verursachen, wenn es nicht richtig verwendet wird, ist es viel schneller als das spontane Erstellen eines Skript-Tags.

  • das hat mir geholfen, aber ich fühle mich schmutzig mit eval. Um sicherzustellen, dass Text nicht kompromittiert werden kann, sehe ich keine Schwachstelle.

    – John

    30. Dezember 2014 um 16:56 Uhr

  • @random-user eval wurde entwickelt, um Benutzer zu verletzen. Jede dynamische Skriptausführung ist ein Risiko und deshalb nennt CSP es 'unsafe-eval' denn es ist. Sie verletzen auch die Sicherheit Ihrer Websites, wenn Sie sie in einer Bibliothek verwenden, da sie sie nicht deaktivieren können.

    – Jonathan Kingston

    22. August 2015 um 19:05 Uhr

  • Das Testen in Chrome 44 verursacht eine Endlosschleife, wenn appendChild aufgerufen wird, da dies den scripts.length-Wert erhöht.

    – Codewithcheese

    11. Oktober 2015 um 11:45 Uhr

  • Skripte mit src -Eigenschaft wird asynchron heruntergeladen und wie angekommen ausgeführt. Die Bestellung wird nicht gespeichert. Inline-Skripte werden auch außerhalb der Reihenfolge ausgeführt, synchron vor den asynchronen.

    – Robert4

    11. Februar 2016 um 22:30 Uhr

  • Neuer Aufruf: [...document.querySelectorAll(`#${id} script`)].forEach(script => { if (scripts.src != "") { ... }})

    – mplungjan

    12. November 2020 um 13:09 Uhr


1646642114 703 Ausfuhren mit innerHTML eingefugte Elemente
fantactuka

Probieren Sie diesen Ausschnitt aus:

function stripAndExecuteScript(text) {
    var scripts="";
    var cleaned = text.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(){
        scripts += arguments[1] + '\n';
        return '';
    });

    if (window.execScript){
        window.execScript(scripts);
    } else {
        var head = document.getElementsByTagName('head')[0];
        var scriptElement = document.createElement('script');
        scriptElement.setAttribute('type', 'text/javascript');
        scriptElement.innerText = scripts;
        head.appendChild(scriptElement);
        head.removeChild(scriptElement);
    }
    return cleaned;
};


var scriptString = '<scrip' + 't + type="text/javascript">alert(\'test\');</scr' + 'ipt><strong>test</strong>';
document.getElementById('element').innerHTML = stripAndExecuteScript(scriptString);

  • das hat mir geholfen, aber ich fühle mich schmutzig mit eval. Um sicherzustellen, dass Text nicht kompromittiert werden kann, sehe ich keine Schwachstelle.

    – John

    30. Dezember 2014 um 16:56 Uhr

  • @random-user eval wurde entwickelt, um Benutzer zu verletzen. Jede dynamische Skriptausführung ist ein Risiko und deshalb nennt CSP es 'unsafe-eval' denn es ist. Sie verletzen auch die Sicherheit Ihrer Websites, wenn Sie sie in einer Bibliothek verwenden, da sie sie nicht deaktivieren können.

    – Jonathan Kingston

    22. August 2015 um 19:05 Uhr

  • Das Testen in Chrome 44 verursacht eine Endlosschleife, wenn appendChild aufgerufen wird, da dies den scripts.length-Wert erhöht.

    – Codewithcheese

    11. Oktober 2015 um 11:45 Uhr

  • Skripte mit src -Eigenschaft wird asynchron heruntergeladen und wie angekommen ausgeführt. Die Bestellung wird nicht gespeichert. Inline-Skripte werden auch außerhalb der Reihenfolge ausgeführt, synchron vor den asynchronen.

    – Robert4

    11. Februar 2016 um 22:30 Uhr

  • Neuer Aufruf: [...document.querySelectorAll(`#${id} script`)].forEach(script => { if (scripts.src != "") { ... }})

    – mplungjan

    12. November 2020 um 13:09 Uhr


1646642115 500 Ausfuhren mit innerHTML eingefugte Elemente
Bruce

function insertHtml(id, html)  
{  
   var ele = document.getElementById(id);  
   ele.innerHTML = html;  
   var codes = ele.getElementsByTagName("script");   
   for(var i=0;i<codes.length;i++)  
   {  
       eval(codes[i].text);  
   }  
}  

Es funktioniert in Chrome in meinem Projekt

  • Schnell und hübsch. Danke.

    – Jorge Fuentes González

    3. Dezember 2014 um 22:08 Uhr

  • Das ist es! Danke.

    – Flori

    11. Dezember 2017 um 18:04 Uhr

  • Mein Problem wurde nach 4 Stunden Trial-and-Error gelöst (übrigens mit Springboot und Thymeleaf, und das funktioniert immer noch).

    – Todbott

    14. Juli 2021 um 0:38 Uhr

964430cookie-checkAusführen mit .innerHTML eingefügte Elemente

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy