Detaillierte Erklärung der Front-End-Sicherheit: JavaScript-Anti-HTTP-Hijacking und XSS

Detaillierte Erklärung der Front-End-Sicherheit: JavaScript-Anti-HTTP-Hijacking und XSS

HTTP-Hijacking, DNS-Hijacking und XSS

Lassen Sie mich zunächst kurz erklären, was HTTP-Hijacking und DNS-Hijacking sind.

HTTP-Hijacking

Was ist HTTP-Hijacking? In den meisten Fällen handelt es sich um HTTP-Hijacking durch den Betreiber. Wenn wir eine HTTP-Anfrage verwenden, um eine Website-Seite anzufordern, fügt der Netzwerkbetreiber sorgfältig entworfene Netzwerkdatenpakete in den normalen Datenstrom ein, damit der Client (normalerweise ein Browser) „falsche“ Daten anzeigt, normalerweise einige Popups, Werbeanzeigen oder direkt den Inhalt einer bestimmten Website. Jeder sollte das schon einmal erlebt haben.

DNS-Hijacking

Beim DNS-Hijacking geht es darum, den DNS-Server zu kapern, auf bestimmte Weise die Kontrolle über den Auflösungsdatensatz eines Domänennamens zu erlangen und dann das Auflösungsergebnis dieses Domänennamens zu ändern, wodurch der Zugriff auf den Domänennamen von der ursprünglichen IP-Adresse auf die geänderte angegebene IP-Adresse übertragen wird. Infolgedessen kann auf die bestimmte URL nicht zugegriffen werden oder die aufgerufene URL ist eine gefälschte URL. Dadurch wird das Ziel erreicht, Daten zu stehlen oder den ursprünglichen normalen Dienst zu zerstören.

DNS-Hijacking ist extremer als HTTP-Hijacking. Einfach ausgedrückt lautet unsere Anfrage http://www.a.com/index.html, wird aber direkt auf http://www.b.com/index.html umgeleitet. Dieser Artikel wird diese Situation nicht im Detail besprechen.

XSS Cross-Site-Scripting

XSS bezieht sich auf einen Angreifer, der eine Sicherheitslücke ausnutzt, um Schadcode in eine Webseite einzuschleusen. Wenn ein Benutzer die Seite durchsucht, wird der eingeschleuste Code ausgeführt, wodurch der spezifische Zweck des Angriffs erreicht wird.

In diesem Artikel wird nicht erläutert, wie diese Angriffe generiert werden oder wie Angreifer Schadcode in die Seite einschleusen. Es reicht zu wissen, dass es sich bei HTTP-Hijacking und XSS letztlich um Schadcode handelt, der auf der Clientseite, normalerweise dem Browser des Benutzers, ausgeführt wird. In diesem Artikel wird erläutert, wie Sie Javascript für einen effektiven Front-End-Schutz verwenden können, vorausgesetzt, dass die Einschleusung bereits erfolgt.

Die Seite ist in einen Iframe eingebettet und leitet den Iframe weiter

Lassen Sie uns zunächst über die Situation sprechen, in der unsere Seite in ein Iframe eingebettet ist. Das heißt, um die Auswirkungen eingebetteter Werbung auf den Originalseiten der Website zu minimieren, platzieren Netzwerkbetreiber die Originalseiten der Website normalerweise in einem Iframe derselben Größe wie die Originalseite, sodass das Iframe verwendet werden kann, um die Auswirkungen des Werbecodes auf der Originalseite zu isolieren.

Diese Situation ist relativ einfach zu handhaben. Wir müssen nur wissen, ob unsere Seite in einem Iframe verschachtelt ist. Wenn ja, leiten wir die äußere Seite auf unsere normale Seite um.

Gibt es also eine Möglichkeit festzustellen, ob unsere Seite derzeit in einem Iframe vorhanden ist? Ja, das sind window.self und window.top.

Fenster.selbst

Gibt eine Referenz auf das aktuelle Fensterobjekt zurück.

Fenster oben

Gibt eine Referenz auf das oberste Fenster in der Fensterhierarchie zurück.

Bei Domänennamen mit unterschiedlichem Ursprung kann die untergeordnete Iframe-Seite die spezifische Seitenadresse nicht über parent.location oder top.location abrufen, kann jedoch in top.location schreiben, was bedeutet, dass sie den Sprung der übergeordneten Seite steuern kann.

Die beiden Attribute können jeweils als „self“ und „top“ abgekürzt werden. Wenn wir also feststellen, dass unsere Seite in einem Iframe verschachtelt ist, können wir die übergeordnete Seite umleiten:

wenn (selbst != oben) {
  // Unsere normale Seite var url = location.href;
  // Umleitung der übergeordneten Seite top.location = url;
}

Verwenden Sie eine Whitelist, um die normale Verschachtelung von Iframes zu ermöglichen.

Natürlich werden unsere Seiten oft, vielleicht aus betrieblichen Gründen, auf verschiedene Weise beworben oder für normale geschäftliche Zwecke in Iframes verschachtelt. Zu diesem Zeitpunkt benötigen wir eine Whitelist oder Blacklist. Wenn unsere Seite in einem Iframe verschachtelt ist und der Domänenname der übergeordneten Seite in der Whitelist steht, wird kein Umleitungsvorgang ausgeführt.

Wie oben erwähnt, können Sie die URL der übergeordneten Seite nicht mithilfe von top.location.href abrufen. In diesem Fall müssen Sie document.referrer verwenden.

Die URL der domänenübergreifenden Iframe-Übergeordnetenseite kann über document.referrer abgerufen werden.

// Eine Whitelist erstellen var whiteList = [
  „www.aaa.com“,
  „res.bbb.com“
];
 
wenn (selbst != oben) {
  var
    // Verwenden Sie document.referrer, um die URL der übergeordneten Seite des domänenübergreifenden Iframes abzurufen
    parentUrl = Dokument.Referrer,
    Länge = WhiteList.Länge,
    ich = 0;
 
  für(; i<Länge; i++){
    // Erstellen Sie einen regulären Ausdruck für eine Whitelist var reg = new RegExp(whiteList[i],'i');
 
    // Existiert in der Whitelist, freigeben if(reg.test(parentUrl)){
      zurückkehren;
    }
  }
 
  // Unsere normale Seite var url = location.href;
  // Umleitung der übergeordneten Seite top.location = url;
}

Ändern Sie URL-Parameter, um Operator-Tags zu umgehen

Ist das alles? Nein, obwohl wir die übergeordnete Seite umgeleitet haben, kann es sein, dass die Seite während des Umleitungsprozesses erneut per Iframe verschachtelt wird, da sie beim ersten Mal verschachtelt war. Das ist wirklich ärgerlich.

Natürlich hinterlässt diese Art der Entführung durch Betreiber in der Regel Spuren. Die gängigste Methode besteht darin, einen Parameter in der Seiten-URL festzulegen, z. B. http://www.example.com/index.html?iframe_hijack_redirected=1, wobei iframe_hijack_redirected=1 bedeutet, dass die Seite entführt wurde und der Iframe nicht mehr verschachtelt ist. Basierend auf dieser Funktion können wir unsere URL so umschreiben, dass es aussieht, als wäre sie entführt worden:

var flag = „iframe_hijack_redirected“;
// Die aktuelle Seite befindet sich in einem Iframe // Hier muss eine Whitelist-Matching-Regel erstellt werden, und die Whitelist wird standardmäßig freigegeben if (self != top) {
  var
    // Verwenden Sie document.referrer, um die URL der übergeordneten Seite des domänenübergreifenden Iframes abzurufen
    parentUrl = Dokument.Referrer,
    Länge = WhiteList.Länge,
    ich = 0;
 
  für(; i<Länge; i++){
    // Erstellen Sie einen regulären Ausdruck für eine Whitelist var reg = new RegExp(whiteList[i],'i');
 
    // Existiert in der Whitelist, freigeben if(reg.test(parentUrl)){
      zurückkehren;
    }
  }
 
  var url = Standort.href;
  var Teile = url.split('#');
  wenn (Standort.Suche) {
    Teile[0] += '&' + Flagge + '=1';
  } anders {
    Teile[0] += '?' + Flagge + '=1';
  }
  versuchen {
    console.log('Die Seite ist in ein Iframe eingebettet:', URL);
    top.location.href = teile.join('#');
  } fangen (e) {}
}

Wenn dieser Parameter geändert wird, wird der Anti-Nesting-Code natürlich ungültig. Daher müssen wir auch ein Berichtssystem einrichten. Wenn wir feststellen, dass eine Seite verschachtelt ist, senden wir einen Abfangbericht. Selbst wenn die Umleitung fehlschlägt, können wir die URL der im Iframe eingebetteten Seite erfahren. Durch die Analyse dieser URLs können wir unsere Schutzmaßnahmen kontinuierlich verbessern, was später erwähnt wird.

Inline-Ereignisse und Inline-Skript-Abfangen

In XSS gibt es tatsächlich viele Möglichkeiten, Skripte einzufügen, insbesondere nach der Veröffentlichung von HTML5. Wenn Sie nicht aufpassen, können viele neue Tags zum Einfügen ausführbarer Skripte verwendet werden.

Listen Sie einige gängige Injektionsmethoden auf:

1. <a href="javascript:alert(1)" rel="external nofollow" _fcksavedurl="javascript:alert(1)" ></a>

2. <iframe src="javascript:alert(1)" />

3. <img src='x' onerror="alert(1)" />

4. <video src='x' onerror="alert(1)" ></video>

5. <div onmouseover="alert(2)" ><div>

Abgesehen von einigen sehr seltenen und obskuren Injektionsmethoden, die hier nicht aufgeführt sind, handelt es sich bei den meisten um Javascript:... und Inline-Ereignisse bei*.

Angenommen, die Injektion hat bereits stattgefunden: Gibt es eine Möglichkeit, die Ausführung dieser Inline-Ereignisse und Inline-Skripte abzufangen?

Für die oben aufgeführten Skripte (1) und (5), die vor ihrer Ausführung einen Klick oder die Ausführung eines bestimmten Ereignisses durch den Benutzer erfordern, verfügen wir über Möglichkeiten, uns gegen sie zu wehren.

Browser-Ereignismodell

Bei der hier erwähnten Abfangfähigkeit handelt es sich um Prinzipien, die sich auf das Ereignismodell beziehen.

Wir alle wissen, dass das Standard-Browser-Ereignismodell aus drei Phasen besteht:

  • Erfassungsphase
  • Zielphase
  • Sprudelphase

Bei einem Tag wie <a href="javascript:alert(222)" rel="external nofollow" rel="external nofollow" _fcksavedurl="javascript:alert(222)" ></a> befindet sich das tatsächlich auslösende Element alert(222) in der Zielphase des Klickereignisses.

<a href="javascript:alert(222)" rel="external nofollow" rel="external nofollow" >Klick mich</a>

<Skript>
  document.addEventListener('klicken', Funktion(e) {
    Alarm (111);
  }, WAHR);
</Skript>

Klicken Sie oben auf „Klick mich“, zuerst wird 111 und dann 222 angezeigt.

Dann müssen wir während der Erfassungsphase des Klickereignismodells nur noch eine Keyword-Blacklist für den JavaScript:...-Inhalt im Tag erstellen und diese filtern und überprüfen, um den gewünschten Abfangeffekt zu erzielen.

Dasselbe gilt für Inline-Ereignisse vom Typ on*, aber es gibt zu viele solcher Ereignisse und wir können sie nicht manuell aufzählen. Wir können Code verwenden, um Inline-Ereignisse und Inline-Skripte automatisch aufzuzählen und abzufangen.

Am Beispiel des Abfangens von href="javascript:..." im a-Tag können wir es folgendermaßen schreiben:

// Eine schwarze Liste mit Schlüsselwörtern erstellen var keywordBlackList = [
  'xss',
  „BAIDU_SSP__wrapper“,
  „BAIDU_DSPUI_FLOWBAR“
];
   
document.addEventListener('klicken', Funktion(e) {
  var code = "";
 
  // Nach Skripten für <a href="javascript:" rel="external nofollow" > suchen if (elem.tagName == 'A' && elem.protocol == 'javascript:') {
    var code = elem.href.substr(11);
 
    wenn (blackListMatch(SchlüsselwortBlackList, Code)) {
      // Abmeldecode elem.href = 'javascript:void(0)';
      console.log('Verdächtige Ereignisse abfangen:' + Code);
    }
  }
}, WAHR);
 
/**
 * [Blacklist-Übereinstimmung]
 * @param {[Array]} blackList [schwarze Liste]
 * @param {[String]} value [Zu prüfender String]
 * @return {[Boolean]} [false – Überprüfung fehlgeschlagen, true – Überprüfung bestanden]
 */
Funktion blackListMatch(blackList, Wert) {
  var Länge = schwarzeListe.Länge,
    ich = 0;
 
  für (; i < Länge; i++) {
    // Einen regulären Ausdruck für die Blacklist erstellen var reg = new RegExp(whiteList[i], 'i');
 
    // Existiert in der Blacklist, abfangen wenn (reg.test(value)) {
      gibt true zurück;
    }
  }
  gibt false zurück;
}

Klicken Sie auf die Schaltflächen im Bild, um Folgendes anzuzeigen:

Hierbei kommt das Blacklist-Matching zum Einsatz, welches weiter unten noch genauer erläutert wird.

Statische Skriptblockierung

Das Wesen von XSS-Cross-Site-Scripting ist nicht „Cross-Site“, sondern „Scripting“.

Im Allgemeinen fügen Angreifer oder Betreiber ein <script>-Skript in die Seite ein, und alle spezifischen Vorgänge werden im Skript implementiert. Diese Hijacking-Methode muss nur einmal eingefügt werden, und wenn es Änderungen gibt, muss sie nicht jedes Mal erneut eingefügt werden.

Wir gehen davon aus, dass ein <script src="http://attack.com/xss.js">-Skript in die Seite eingeschleust wird, und unser Ziel besteht darin, die Ausführung dieses Skripts abzufangen.

Das klingt schwierig. Was bedeutet das? Der Zweck besteht darin, das verdächtige Skript vor seiner Ausführung zu erkennen und zu zerstören, sodass es den internen Code nicht ausführen kann.

Daher müssen wir einige erweiterte APIs verwenden, um die generierten Knoten beim Laden der Seite zu erkennen.

MutationObserver

MutationObserver ist eine leistungsstarke neue API, die in HTML5 hinzugefügt wurde. Sie bietet Entwicklern die Möglichkeit, angemessen zu reagieren, wenn sich ein DOM-Baum innerhalb eines bestimmten Bereichs ändert.

Das klingt sehr mysteriös, bedeutet aber grob, dass es die Änderungen im DOM-Baum der Seite überwachen und entsprechend reagieren kann.

MutationObserver()Dieser Konstruktor wird verwendet, um ein neues Mutation-Observer-Objekt zu instanziieren.

MutationObserver
  Funktionsrückruf
);

Ich war fassungslos. Was sollte dieser lange Absatz? Dies bedeutet, dass der MutationObserver bei seiner Beobachtung nicht sofort zurückruft, wenn er ein neues Element findet, sondern alle in einem Zeitabschnitt vorkommenden Elemente gemeinsam übergibt. Daher müssen wir im Rückruf eine Stapelverarbeitung durchführen. Darüber hinaus wird der callback aufgerufen, wenn sich der angegebene DOM-Knoten (Zielknoten) ändert. Beim Aufruf übergibt das Observer-Objekt zwei Parameter an die Funktion. Der erste Parameter ist ein Array, das mehrere MutationRecord-Objekte enthält, und der zweite Parameter ist das Observer-Objekt selbst.

Daher können wir mit MutationObserver jede statische Skriptdatei überwachen, die von der Seite geladen wird:

// Unterschiedliche Kompatibilitätsmethoden zum Schreiben von MutationObserver var MutationObserver = window.MutationObserver || window.WebKitMutationObserver ||
Fenster.MozMutationObserver;
// Dieser Konstruktor wird verwendet, um ein neues Mutation Observer-Objekt zu instanziieren // Mutation Observer-Objekte können DOM-Baumänderungen innerhalb eines bestimmten Bereichs überwachen var observer = new MutationObserver(function(mutations) {
  mutationen.forEach(Funktion(Mutation) {
    // Gibt den hinzugefügten Knoten oder null zurück.
    var Knoten = Mutation.addedNodes;
 
    für (var i = 0; i < Knoten.Länge; i++) {
      var Knoten = Knoten[i];
      wenn (/xss/i.test(node.src))) {
        versuchen {
          node.parentNode.removeChild(Knoten);
          console.log('Verdächtige statische Skripte abfangen:', node.src);
        } fangen (e) {}
      }
    }
  });
});
 
// Übergeben Sie den Zielknoten und die Beobachtungsoptionen // Wenn das Ziel ein Dokument oder ein Dokument.documentElement ist
// Alle Operationen zum Hinzufügen und Löschen von Knoten im aktuellen Dokument werden beobachtet observer.observe(document, {
  Teilbaum: wahr,
  childList: wahr
});

<script type="text/javascript" src="./xss/a.js"></script> ist ein statisches Skript, das zu Beginn des Seitenladens existiert (siehe Seitenstruktur). Wir verwenden MutationObserver, um während des Zeitraums zwischen dem Laden und der Ausführung des Skripts reguläre Ausdrücke auf seinen Inhalt abzugleichen. Wenn bösartiger Code gefunden wird, wird removeChild() verwendet, um seine Ausführung unmöglich zu machen.

Verwenden Sie eine Whitelist, um die Quelle abzugleichen

Im obigen Code verwenden wir diesen Satz, um zu bestimmen, ob ein JS-Skript bösartig ist:

wenn (/xss/i.test(node.src)) {}

Natürlich wird die Person, die den Schadcode einschleust, in der Realität nicht so dumm sein, den Namen in XSS zu ändern. Daher ist es für uns notwendig, eine Whitelist zur Filterung zu verwenden und ein Abfang- und Meldesystem einzurichten.

// Eine Whitelist erstellen var whiteList = [
  „www.aaa.com“,
  „res.bbb.com“
];
 
/**
 * [Whitelist-Abgleich]
 * @param {[Array]} whileList [Whitelist]
 * @param {[String]} value [Zu prüfender String]
 * @return {[Boolean]} [false – Überprüfung fehlgeschlagen, true – Überprüfung bestanden]
 */
Funktion whileListMatch(whileList, Wert) {
  var Länge = whileList.length,
    ich = 0;
 
  für (; i < Länge; i++) {
    // Erstellen Sie einen regulären Ausdruck für eine Whitelist var reg = new RegExp(whiteList[i], 'i');
 
    // Existiert in der Whitelist, freigeben if (reg.test(value)) {
      gibt true zurück;
    }
  }
  gibt false zurück;
}
 
// Whitelist nur freigeben, wenn (!whileListMatch(blackList, node.src)) {
  node.parentNode.removeChild(Knoten);
}

Wir haben das Whitelist-Matching hier schon mehrmals erwähnt und es wird weiter unten erneut verwendet, daher können wir es hier einfach in einen Methodenaufruf einbinden.

Dynamische Skriptblockierung

MutationObserver wird oben verwendet, um statische Skripte abzufangen. Neben statischen Skripten gibt es auch dynamisch generierte Skripte.

var Skript = Dokument.createElement('Skript');
script.type = "text/javascript";
script.src = "http://www.example.com/xss/b.js";
 
document.getElementsByTagName('body')[0].appendChild(script);

Um diese Art dynamisch generierter Skripts abzufangen (der Abfangzeitpunkt sollte beim Einfügen in den DOM-Baum und vor der Ausführung sein), können Sie in Mutation Events auf das Ereignis DOMNodeInserted hören.

Mutationsereignisse und DOMNodeInserted

Öffnen Sie MDN, der erste Satz lautet:

Diese Funktion wurde aus dem Webstandard entfernt. Obwohl einige Browser sie noch unterstützen, wird sie möglicherweise irgendwann nicht mehr unterstützt. Bitte versuchen Sie, diese Funktion nicht zu verwenden.

Obwohl es nicht verfügbar ist, können Sie es dennoch verstehen:

document.addEventListener('DOMNodeInserted', Funktion(e) {
  var Knoten = e.Ziel;
  wenn (/xss/i.test(node.src) || ​​​​/xss/i.test(node.innerHTML)) {
    node.parentNode.removeChild(Knoten);
    console.log('Verdächtige dynamische Skripte abfangen:', node);
  }
}, WAHR);

Leider kann der obige Code dynamisch generierte Skripte abfangen, aber der Code wird auch ausgeführt: DOMNodeInserted kann, wie der Name schon sagt, strukturelle Änderungen innerhalb eines DOM-Bereichs überwachen. Im Vergleich zu MutationObserver wird es früher ausgeführt.

DOMNodeInserted wird jedoch nicht mehr empfohlen, daher sollte die Aufgabe der Überwachung dynamischer Skripte ebenfalls an MutationObserver übergeben werden.

Leider ist das Ergebnis der Verwendung von MutationObserver in der Praxis dasselbe wie bei DOMNodeInserted. Es kann die Generierung dynamischer Skripts überwachen und abfangen, kann jedoch nicht mit removeChild entfernt werden, bevor das Skript ausgeführt wird. Daher müssen wir uns andere Möglichkeiten überlegen.

setAttribute und document.write überschreiben

Überschreiben Sie die native Methode Element.prototype.setAttribute

Bevor das dynamische Skript eingefügt und ausgeführt wird, ist es nicht möglich, es durch Überwachen der Änderungen im DOM-Baum abzufangen, und das Skript wird trotzdem ausgeführt.

Dann müssen wir das Skript nachschlagen und erfassen, bevor es in den DOM-Baum eingefügt wird. Erst dann wird das Skript erstellt.

Nehmen wir nun an, dass ein dynamisches Skript wie folgt erstellt wurde:

var Skript = Dokument.createElement('Skript');
script.setAttribute('Typ', 'Text/Javascript');
script.setAttribute('src', 'http://www.example.com/xss/c.js');
 
document.getElementsByTagName('body')[0].appendChild(script);

Es ist auch möglich, Element.prototype.setAttribute neu zu schreiben: Wir stellen fest, dass hier die Methode setAttribute verwendet wird. Wenn wir diese native Methode neu schreiben können, beim Festlegen des src-Attributs auf den Wert achten und ihn anhand der Blacklist oder Whitelist beurteilen können, können wir die Legitimität des Tags bestimmen.

// Ursprüngliche Schnittstelle speichern var old_setAttribute = Element.prototype.setAttribute;
 
// setAttribute-Schnittstelle neu schreiben Element.prototype.setAttribute = function(name, value) {
 
  // Passt zum Typ <script src='xxx' >, wenn (this.tagName == 'SCRIPT' && /^src$/i.test(name)) {
    // Whitelist-Abgleichwenn (!whileListMatch(whiteList, value)) {
      console.log('Verdächtige Module abfangen:', Wert);
      zurückkehren;
    }
  }
   
  //Rufen Sie die ursprüngliche Schnittstelle auf old_setAttribute.apply(this, arguments);
};
 
// Eine Whitelist erstellen var whiteList = [
„www.yy.com“,
„res.cont.yy.com“
];
 
/**
 * [Whitelist-Abgleich]
 * @param {[Array]} whileList [Whitelist]
 * @param {[String]} value [Zu prüfender String]
 * @return {[Boolean]} [false – Überprüfung fehlgeschlagen, true – Überprüfung bestanden]
 */
Funktion whileListMatch(whileList, Wert) {
  var Länge = whileList.length,
    ich = 0;
 
  für (; i < Länge; i++) {
    // Erstellen Sie einen regulären Ausdruck für eine Whitelist var reg = new RegExp(whiteList[i], 'i');
 
    // Existiert in der Whitelist, freigeben if (reg.test(value)) {
      gibt true zurück;
    }
  }
  gibt false zurück;
}

Schreiben Sie Element.prototype.setAttribute neu, d. h. speichern Sie zuerst die ursprüngliche Schnittstelle und prüfen Sie dann, wenn ein Element setAttribute aufruft, ob das übergebene src in der Whitelist vorhanden ist. Wenn es vorhanden ist, wird es freigegeben. Wenn nicht, wird es als verdächtiges Element betrachtet, gemeldet und nicht ausgeführt. Schließlich wird das native setAttribute auf den freigegebenen Elementen ausgeführt, d. h. old_setAttribute.apply(this, arguments);.

Der oben genannte Whitelist-Abgleich kann auch durch einen Blacklist-Abgleich ersetzt werden.

Überschreiben Sie Element.prototype.setAttribute innerhalb eines verschachtelten Iframes

Wenn old_setAttribute = Element.prototype.setAttribute einem Angreifer ausgesetzt ist, kann die direkte Verwendung von old_setAttribute natürlich unsere überschriebene Methode umgehen, daher muss dieser Code in einen Abschluss eingebettet werden.

Dies ist natürlich nicht sicher, obwohl Element.prototype.setAttribute im aktuellen Fenster überschrieben wurde. Es gibt jedoch immer noch eine Möglichkeit, das native Element.prototype.setAttribute zu erhalten. Dazu ist lediglich ein neues Iframe erforderlich.

var newIframe = document.createElement('iframe');
Dokument.Body.AppendChild(neuesIframe);
 
Element.prototype.setAttribute = neuesIframe.contentWindow.Element.prototype.setAttribute;

Durch diese Methode können Sie das native Element.prototype.setAttribute erneut abrufen, da die Umgebung innerhalb des Iframes und des äußeren Fensters vollständig isoliert sind. was zum Teufel?

was zu tun? Wir sehen, dass „createElement“ zum Erstellen eines Iframes verwendet wird. Ist es also möglich, das native „createElement“ neu zu schreiben? Aber zusätzlich zu createElement gibt es auch createElementNS, und es ist möglich, dass auf der Seite bereits ein Iframe vorhanden ist, sodass dies nicht geeignet ist.

Dann wird jedes Mal, wenn ein neues Iframe erstellt wird, setAttribute geschützt und neu geschrieben, und MutationObserver wird hier erneut verwendet:

/**
 * Verwenden Sie MutationObserver, um die generierte Iframe-Seite zu überwachen.
 * Verhindern Sie den Aufruf interner nativer setAttribute- und document.write-Befehle
 * @return {[Typ]} [Beschreibung]
 */
Funktion VerteidigungIframe() {
  // Schützen Sie zuerst die aktuelle Seite installHook(window);
}
 
/**
 * Implementieren Sie setAttribute-Schutz für ein einzelnes Fenster* @param {[BOM]} window [Browserfensterobjekt]
 * @return {[Typ]} [Beschreibung]
 */
Funktion installHook(Fenster) {
  // Schreiben Sie die setAttribute-Eigenschaft eines einzelnen Fensters neu resetSetAttribute(window);
 
  // Unterschiedliche Kompatibilitätsmethoden zum Schreiben von MutationObserver var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
 
  // Dieser Konstruktor wird verwendet, um ein neues Mutation Observer-Objekt zu instanziieren // Mutation Observer-Objekte können DOM-Baumänderungen innerhalb eines bestimmten Bereichs überwachen var observer = new MutationObserver(function(mutations) {
    mutationen.forEach(Funktion(Mutation) {
      // Gibt den hinzugefügten Knoten oder null zurück.
      var Knoten = Mutation.addedNodes;
 
      // Einer nach dem anderen durchlaufen for (var i = 0; i < nodes.length; i++) {
        var Knoten = Knoten[i];
 
        // Installieren Sie den Rewrite-Hook in der generierten Iframe-Umgebung, wenn (node.tagName == 'IFRAME') {
          installHook(node.contentWindow);
        }
      }
    });
  });
 
  Beobachter.beobachten(Dokument, {
    Teilbaum: wahr,
    childList: true
  });
}
 
/**
 * Überschreibe die setAttribute-Eigenschaft eines einzelnen Fensters * @param {[BOM]} window [Browserfensterobjekt]
 * @return {[Typ]} [Beschreibung]
 */
Funktion ResetSetAttribute(Fenster) {
  //Ursprüngliche Schnittstelle speichern var old_setAttribute = window.Element.prototype.setAttribute;
 
  // Schreiben Sie die setAttribute-Schnittstelle neu window.Element.prototype.setAttribute = function(name, value) {
    ...
  };
}

Wir definieren eine installHook-Methode mit einem Fenster als Parameter. In dieser Methode überschreiben wir das setAttribute des übergebenen Fensters, installieren einen MutationObserver und überwachen die Iframes, die in Zukunft in diesem Fenster erstellt werden können. Wenn in Zukunft ein Iframe in diesem Fenster erstellt wird, wird die installHook-Methode auch auf dem neuen Iframe installiert, um einen schichtweisen Schutz zu bieten.

Überschreiben Sie document.write

Basierend auf den oben genannten Methoden können wir weiter untersuchen, welche anderen Methoden umgeschrieben werden können, um die Seite besser zu schützen.

Document.write ist eine sehr gute Wahl. Injection-Angreifer verwenden diese Methode normalerweise, um Popup-Werbung in die Seite einzuschleusen.

Wir können document.write umschreiben und eine Schlüsselwort-Blacklist verwenden, um den Inhalt abzugleichen.

Welche Schlüsselwörter eignen sich besser zum Blacklisting? Schauen wir uns einige Seiten mit vielen Anzeigen an:

Hier ist unten auf der Seite ein Iframe eingebettet, der den Anzeigencode enthält. Der äußerste ID-Name hier, id="BAIDU_SSP__wrapper_u2444091_0", ist für uns ein guter Indikator, um zu beurteilen, ob es sich um Schadcode handelt. Nehmen wir an, wir haben basierend auf Abfangberichten eine Reihe von Blacklists zusammengestellt:

// Regelmäßige Abfangschlüsselwörter erstellen var keywordBlackList = [
'xss',
„BAIDU_SSP__wrapper“,
„BAIDU_DSPUI_FLOWBAR“
];

Als Nächstes müssen wir diese Schlüsselwörter nur verwenden, um eine reguläre Ausdrucksbeurteilung des von document.write übergebenen Inhalts durchzuführen, um zu bestimmen, ob der document.write-Code abgefangen werden soll.

// Eine schwarze Liste mit Schlüsselwörtern erstellen var keywordBlackList = [
  'xss',
  „BAIDU_SSP__wrapper“,
  „BAIDU_DSPUI_FLOWBAR“
];
 
/**
 * Überschreibe die document.write-Eigenschaft eines einzelnen Fensters * @param {[BOM]} window [Browserfensterobjekt]
 * @return {[Typ]} [Beschreibung]
 */
Funktion resetDocumentWrite(Fenster) {
  var old_write = fenster.dokument.schreiben;
 
  window.document.write = Funktion(Zeichenfolge) {
    wenn (blackListMatch(SchlüsselwortBlackList, Zeichenfolge)) {
      console.log('Verdächtige Module abfangen:', Zeichenfolge);
      zurückkehren;
    }
 
    //Rufen Sie die ursprüngliche Schnittstelle auf. old_write.apply(document, arguments);
  }
}
 
/**
 * [Blacklist-Übereinstimmung]
 * @param {[Array]} blackList [schwarze Liste]
 * @param {[String]} value [Zu prüfender String]
 * @return {[Boolean]} [false – Überprüfung fehlgeschlagen, true – Überprüfung bestanden]
 */
Funktion blackListMatch(blackList, Wert) {
  var Länge = schwarzeListe.Länge,
    ich = 0;
 
  für (; i < Länge; i++) {
    // Einen regulären Ausdruck für die Blacklist erstellen var reg = new RegExp(whiteList[i], 'i');
 
    // Existiert in der Blacklist, abfangen wenn (reg.test(value)) {
      gibt true zurück;
    }
  }
  gibt false zurück;
}

Wir können „resetDocumentWrite“ in die oben stehende Methode „installHook“ einfügen und dann „document.write“ im aktuellen Fenster und in allen generierten Iframe-Umgebungen neu schreiben.

Sperre anwenden und anrufen

Als nächstes werde ich das Sperren der nativen Methoden Function.prototype.apply und Function.prototype.call einführen. Sperren bedeutet, dass sie nicht überschrieben werden können.

Object.defineProperty wird hier verwendet, um Anwenden und Aufrufen zu sperren.

Objekt.defineProperty

Die Methode Object.defineProperty() definiert eine neue Eigenschaft direkt für ein Objekt oder ändert eine vorhandene Eigenschaft und gibt das Objekt zurück.

Object.defineProperty(Objekt, Eigenschaft, Deskriptor)

In:

  • obj – das Objekt, dessen Eigenschaften definiert werden müssen
  • prop – der Name der zu definierenden oder zu ändernden Eigenschaft
  • Deskriptor – der Deskriptor der zu definierenden oder zu ändernden Eigenschaft

Mit dem folgenden Code können wir verhindern, dass „call“ und „apply“ neu geschrieben werden.

// Anruf sperren
Objekt.defineProperty(Funktion.prototype, 'call', {
  Wert: Function.prototype.call,
  // Diese Eigenschaft kann durch den Zuweisungsoperator genau dann geändert werden, wenn der Schreibwert dieser Eigenschaft wahr ist. Schreibbar: falsch,
  // Diese Eigenschaft kann nur geändert und gelöscht werden, wenn configurable true ist. configurable: false,
  aufzählbar: wahr
});
// Sperre anwenden
Objekt.defineProperty(Funktion.prototype, 'anwenden', {
  Wert: Function.prototype.apply,
  beschreibbar: false,
  konfigurierbar: false,
  aufzählbar: wahr
});

Warum so schreiben? Tatsächlich hängt es immer noch mit der oben beschriebenen Neufassung von setAttribute zusammen.

Obwohl wir das ursprüngliche Element.prototype.setAttribute in einem Abschluss gespeichert haben, gibt es immer noch einige Tricks, um es aus dem Abschluss zu „stehlen“.

Versuchen Sie Folgendes:

(Funktion() {})(
    // Ursprüngliche Schnittstelle speichern var old_setAttribute = Element.prototype.setAttribute;
    // setAttribute-Schnittstelle neu schreiben Element.prototype.setAttribute = function(name, value) {
        // Einzelheiten, wenn (this.tagName == 'SCRIPT' && /^src$/i.test(name)) {}
        //Rufen Sie die ursprüngliche Schnittstelle auf old_setAttribute.apply(this, arguments);
    };
)();
// Umschreiben anwenden
Funktion.prototype.apply = Funktion(){
    konsole.log(dies);
}
// setAttribute aufrufen
document.getElementsByTagName('body')[0].setAttribute('data-test','123');

Raten Sie, was der obige Absatz ausgibt? schau mal rein:

Es hat tatsächlich die native setAttribute-Methode zurückgegeben!

Dies liegt daran, dass wir beim Umschreiben von Element.prototype.setAttribute am Ende old_setAttribute.apply(this, arguments); haben, das die Methode apply verwendet. Also schreiben wir apply um und geben dies aus. Wenn wir das umgeschriebene setAttribute aufrufen, können wir daraus das ursprünglich gespeicherte old_setAttribute abrufen.

Auf diese Weise ist das Umschreiben des verschachtelten Iframes „setAttribute“, das wir oben durchgeführt haben, bedeutungslos.

Die Verwendung von Object.defineProperty oben kann Apply und ähnliche Aufrufe blockieren. Machen Sie ein Umschreiben unmöglich, sodass unsere native Schnittstelle nicht aus der Closure gestohlen werden kann. Erst dann können wir sagen, dass wir die Eigenschaften, die wir neu schreiben möchten, erfolgreich umgeschrieben haben.

Abhörbericht erstellen

Jetzt haben wir einige Abwehrmaßnahmen. Als Nächstes müssen wir ein Berichtssystem erstellen, um das console.log()-Protokoll im obigen Text zu ersetzen.

Wozu dient das Meldesystem? Da wir Whitelists und Keyword-Blacklists verwenden, müssen diese Daten ständig ergänzt werden. Dabei verlassen wir uns auf das Meldesystem, das alle abgefangenen Informationen an den Server überträgt. So erfahren unsere Programmierer nicht nur zuerst von Angriffen, sondern wir sind auch in der Lage, fortlaufend relevante Informationen zu sammeln, um besser reagieren zu können.

In diesem Beispiel verwende ich node.js, um einen sehr einfachen Server zum Akzeptieren von HTTP-Berichtsanforderungen zu erstellen.

Definieren Sie zunächst eine Berichtsfunktion:

/**
 * Benutzerdefinierte Berichte – ersetzen Sie console.log() auf der Seite
 * @param {[String]} Name [Abfangtyp]
 * @param {[String]} Wert [Achsenabschnittswert]
 */
Funktion hijackReport(Name, Wert) {
  var img = document.createElement('img'),
    hijackName = Name,
    hijackValue = Wert.toString(),
    curDate = neues Date().getTime();
 
  // Bericht img.src = 'http://www.reportServer.com/report/?msg=' + hijackName + '&value=' + hijackValue + '&time=' + curDate;
}

Angenommen, unsere Serveradresse ist www.reportServer.com, verwenden wir img.src, um eine HTTP-Anfrage an den Server http://www.reportServer.com/report/ zu senden, jedes Mal mit unserem benutzerdefinierten Abfangtyp, Abfanginhalt und Berichtszeitpunkt.

Verwenden Sie Express, um einen Node.JS-Server zu erstellen und eine einfache Empfangsroute zu schreiben:

var express = erforderlich('express');
var app = express();
 
app.get('/Bericht/', Funktion(req, res) {
    var queryMsg = req.query.msg,
        Abfragewert = erforderlich.Abfragewert,
        queryTime = neues Datum(parseInt(req.query.time));
 
    wenn (AbfrageMsg) {
        console.log('Abfangtyp: ' + queryMsg);
    }
 
    if (Abfragewert) {
        console.log('Abfangwert: ' + Abfragewert);
    }
 
    wenn (Abfragezeit) {
        console.log('Abfangzeit: ' + req.query.time);
    }
});
 
app.listen(3002, Funktion() {
    console.log('HttpHijack-Server lauscht auf Port 3002!');
});

Führen Sie den Server aus. Wenn ein Bericht auftritt, erhalten wir die folgenden Daten:

Der nächste Schritt besteht darin, Daten zu speichern, zu analysieren, zur Blacklist hinzuzufügen, nodejs zu verwenden und natürlich E-Mails zu senden, um Programmierer zu benachrichtigen, wenn Daten abgefangen werden usw. Ich werde hierauf nicht näher eingehen.

HTTPS und CSP

Lassen Sie uns abschließend kurz über HTTPS und CSP sprechen. Die beste Abwehrmaßnahme gegen ein Hijacking besteht tatsächlich darin, beim Backend anzufangen, da das Frontend eigentlich nur sehr wenig ausrichten kann. Und da der Quellcode offengelegt ist, können Angreifer unsere Abwehrmaßnahmen leicht umgehen.

CSP

CSP steht für Content Security Policy, was übersetzt Inhaltssicherheitsrichtlinie bedeutet. Diese Spezifikation bezieht sich auf die Inhaltssicherheit und wird hauptsächlich verwendet, um zu definieren, welche Ressourcen eine Seite laden kann, um das Auftreten von XSS zu reduzieren.

MDN – CSP

HTTPS

Der grundlegende Grund, warum HTTP-Hijacking durchgeführt werden kann, besteht darin, dass das HTTP-Protokoll keine Möglichkeit bietet, die Identität des Kommunikationspartners und die Integrität der Daten zu überprüfen. Wenn dieses Problem gelöst werden kann, können Entführungen nicht mehr so ​​leicht durchgeführt werden.

HTTPS bedeutet HTTP über SSL. Das SSL-Protokoll ist ein Netzwerkprotokoll, das erstmals 1995 von Netscape vorgeschlagen wurde, um das Sicherheitsproblem der Transportschicht zu lösen. Sein Kern basiert auf der Theorie der Public-Key-Kryptographie, um Funktionen wie Serveridentitätsauthentifizierung, Datenschutz und Datenintegritätsprüfung zu implementieren.

Da es nicht eng mit dem Hauptinhalt dieses Artikels zusammenhängt, können Sie bei Google nach weiteren Informationen zu CSP und HTTPS suchen.

Oben finden Sie eine ausführliche Erläuterung der Front-End-Sicherheit von JavaScript, Anti-HTTP-Hijacking und XSS. Weitere Informationen zur Front-End-Sicherheit von JavaScript, Anti-HTTP-Hijacking und XSS finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • Eine kurze Diskussion über Web-Schwachstellen, Prinzipanalyse und Präventionsmethoden (sichere Dateispeichermethoden)
  • Sicheres Surfen im Internet, um zu verhindern, dass Trojaner ins Internet eindringen
  • Zusammenfassung gängiger Front-End-Sicherheitsprobleme und vorbeugender Maßnahmen

<<:  Warum wird deine Like-Aussage nicht indexiert?

>>:  So führen Sie SQL-Anweisungen automatisch aus, wenn MySQL in Docker gestartet wird

Artikel empfehlen

jQuery implementiert Formularvalidierungsfunktion

Beispiel für die Validierung eines jQuery-Formula...

Linux CentOS6.5 yum installiere mysql5.6

In diesem Artikel wird der einfache Prozess der I...

Vergleich von CSS-Schatteneffekten: Schlagschatten und Box-Schatten

Drop-Shadow und Box-Shadow sind beide CSS-Eigensc...

Was ist dies in einer Punkt-für-Punkt-Reihe von JavaScript?

Verstehe das Vielleicht haben Sie this in anderen...

HTML-Implementierung eines einfachen Rechners mit detaillierten Ideen

Code kopieren Der Code lautet wie folgt: <!DOC...

So implementieren Sie die Unschärfefunktion von DIV

Verwenden Sie Anti-Shake, um DIV verschwinden zu ...

Installations- und Konfigurationstutorial von MongoDB unter Linux

MongoDB -Installation Wählen Sie die Installation...

So richten Sie eine VSCode-Remoteverbindung zum Server-Docker-Container ein

Inhaltsverzeichnis Ziehen Sie das Bild Ausführen ...

Docker-Bereinigungsumgebungsvorgang

Beginnen Sie vorsichtig mit der Reinigung! Auflis...

Ubuntu-Installations-Grafiktreiber und CUDA-Tutorial

Inhaltsverzeichnis 1. Deinstallieren Sie den Orig...

So deinstallieren Sie MySQL vollständig unter CentOS

Dieser Artikel dokumentiert die vollständige Dein...