webentwicklung-frage-antwort-db.com.de

Erkennen Sie Chrome im kopflosen Modus aus JavaScript ausgeführt

Mit der Veröffentlichung von Chrome 59 ist der "Headless" -Modus jetzt verfügbar in stabilen Builds für Linux und MacOS (und bald auch Windows mit Chrome 60). Auf diese Weise können wir eine Vollversion von Chrome ohne sichtbare Benutzeroberfläche ausführen. Dies ist eine hervorragende Funktion für automatisierte Tests. Hier sind Beispiele.

chrome --headless --disable-gpu --dump-dom https://stackoverflow.com/

In meinem JavaScript-Testläufer möchte ich so viele Informationen wie möglich über den verwendeten Browser aufzeichnen, um Probleme einzugrenzen. Zum Beispiel zeichne ich viele der Eigenschaften von navigator auf, einschließlich der aktuellen Browser-Plugins:

JSON.stringify(Array.from(navigator.plugins).map(p => p.name))
["Chrome PDF Viewer","Widevine Content Decryption Module","Shockwave Flash","Native Client","Chrome PDF Viewer"]

Meines Wissens nach sollte sich Chrome im kopflosen Modus identisch verhalten , aber ich habe genug Erfahrung, um skeptisch gegenüber einer neuen Funktion zu sein, die sich möglicherweise erheblich ändert die Rendering-Pipeline.

Im Moment werde ich Tests in beiden Modi durchführen. Ich möchte dem Testläufer mitteilen, ob der Headless-Modus verwendet wird. Ich könnte diese Informationen in den Testkonfigurationen weitergeben, hätte aber lieber eine reine JavaScript-Lösung, die ich in den Testläufer selbst einbauen kann. Ich konnte jedoch keine Browseroberfläche finden, die angibt, ob der Headless-Modus aktiv ist.

Gibt es eine Möglichkeit, über JavaScript zu erkennen, ob Chrome im Headless-Modus ausgeführt wird?

51
Jeremy Banks

Du kannst nachschauen navigator.webdriver Eigenschaft, die ist:

Die schreibgeschützte Eigenschaft webdriver der Schnittstelle navigator gibt an, ob der Benutzeragent durch die Automatisierung gesteuert wird.

...

Das navigator.webdriver Eigenschaft ist wahr, wenn in:

Chrome Die --enable-automation oder der --headless flag wird verwendet.
Firefox Die marionette.enabled Präferenz oder --marionette Flagge wird übergeben.

Die W3C WebDriver-Empfehlung beschreibt sie wie folgt:

navigator.webdriver Definiert eine Standardmethode für kooperierende Benutzeragenten, um das Dokument darüber zu informieren, dass es von WebDriver gesteuert wird, z. B. damit während der Automatisierung alternative Codepfade ausgelöst werden können.

16
Justas

Die Zeichenfolge des Benutzeragenten enthält HeadlessChrome anstelle von Chrome . Dies ist wahrscheinlich das Signal, nach dem Sie suchen möchten. Sie können also Folgendes verwenden:

/\bHeadlessChrome\//.test(navigator.userAgent)

Andere interessante Signale sind:

  • Es sieht aus wie window.chrome ist im kopflosen Zustand undefiniert.
  • [innerWidth, innerHeight] ist [800, 600] (fest codiert in headless_browser.cc), während [outerWidth, outerHeight] ist [0, 0] (was normalerweise nicht passieren sollte).
29
Josh Lee

Lesen Sie einfach dieser Artikel von Antoine Vastel , der einige Möglichkeiten bietet:

  • testen des Benutzeragenten mit /HeadlessChrome/.test(window.navigator.userAgent), dies kann jedoch leicht gefälscht werden
  • plugins testen mit navigator.plugins.length == 0
  • sprachen testen mit navigator.languages == ""
  • testen von WebGL-Hersteller- und Renderer-Informationen (siehe Artikel für interessante Details)
  • testen der von Modernizr erkannten unterstützten Funktionen: Es scheint, dass "Haarlinien" nicht unterstützt werden (Hidpi/Retina-Haarlinien, CSS-Ränder mit einer Breite von weniger als 1 Pixel, die auf Hidpi-Bildschirmen physisch 1 Pixel betragen). Test ist !Modernizr["hairline"].
  • testen der Größe des Platzhalters auf fehlendes Bild. Füge ein Bild mit einer ungültigen URL ein und teste auf image.width == 0 && image.height == 0 im image.onerror (sie fanden, dass dies das robusteste war).

Ich kann nicht für Googles Motivationen sprechen ( ist kopflos Chrome nur zum Ermöglichen von Tests von Webanwendungen? Hmmm ... ), aber dies könnte als eine Liste von Fehlern angesehen werden, die eines Tages behoben werden könnten, daher muss man sich fragen, wie lange diese Tests funktionieren werden :)

8
Hugues M.

Die beste Lösung, die ich bisher habe, ist dieser Hack. Ich würde es nicht in Prod-Code verwenden, aber möglicherweise beim Testen.

Der Popup-Blocker von Chrome ist normalerweise für alle Websites aktiviert, im Headless-Modus jedoch deaktiviert. Wir können die Möglichkeit nutzen, Popups als ziemlich genauen Proxy für den Headless-Modus zu öffnen. Die Implementierung ist einfach: Versuchen Sie, ein Fenster zu open(...) und prüfen Sie, ob wir null (was darauf hinweist, dass es blockiert wurde) anstelle eines Window -Objekts erhalten. Wenn wir einen öffnen, schließen Sie ihn so schnell wie möglich.

function canPopUp() {
  var w = open("");
  if (w !== null) {
    w.close();
    return true;
  } else {
    return false;
  }
}

var isHeadless = canPopUp;

Für ein kurzes Beispiel können Sie Folgendes mit und ohne das Flag --headless Versuchen:

chrome --headless --disable-gpu --dump-dom 'data:text/html,<!doctype html><body><script>document.body.innerHTML = `headless: ${open("") !== null}`;</script>'

5
Jeremy Banks

navigator.plugins sollte eine Reihe von Plugins enthalten, die im Browser vorhanden sind (z. B. Flash, ActiveX oder Java Applets). Für den Browser headless ist dies nullwertfähig.

Im Rahmen der Sicherheitsprüfung kann es alert verwendet werden, bei Headless wird es ignoriert:

var start = Date.now();
alert('Press OK');
var elapse = Date.now() - start;
if (elapse < 15) {
    console.log("headless environment detected");
}

In der Diskussion zu OWASP AppSecUSA 2014 werden verschiedene Techniken zum Erkennen von Headless-Browsern erläutert. Headless Browser Hide & Seek ( video , Folien ) von Sergey Shekyan und Bei Zhang.

4
FieryCat