- Dass die Website von David Bushell für einige Nutzer seit Längerem kaputt dargestellt wurde, lag an CSS, das die Grammarly-Browser-Erweiterung heimlich in die Seite injizierte
- In Firefox fügt die Grammarly-Erweiterung ein Stylesheet aus lokalen Erweiterungs-Assets ein, das sich über die
StyleSheetList der Webseite nur schwer finden lässt und zudem die Content Security Policy umgeht
- Der Konflikt entstand, weil Grammarly mit
--rem:16 eine globale Definition auf :root setzte und die Website für die Berechnung ihrer Fluid-Typografie ebenfalls --rem als Namen verwendete
- Das
--rem der Website befand sich innerhalb einer Cascade Layer, und wegen der CSS-Regel, dass Styles außerhalb von Layern Vorrang haben, konnte der Grammarly-Wert die Berechnung überschreiben
- Übergangsweise halfen ein Mutation Observer und
!important, die endgültige Lösung war jedoch, den Attributnamen in --🤡 zu ändern; wenn eine Erweiterung gewöhnliche Namen global in :root injiziert, kann sie leicht mit Webseiten kollidieren
Grammarly-CSS innerhalb der Seite
- Über mehrere Monate gab es vereinzelte Meldungen, dass das Layout der Website verrutscht sei und Größen seltsam wirkten; dazu wurden auch Screenshots geschickt
- Technisch versierte Leser nannten die Grammarly browser extension als Hauptursache, und David Bushell installierte sie selbst im Firefox-basierten Mullvad browser, um das zu überprüfen
- Bei der Installation fordert die Erweiterung unter anderem folgende Berechtigungen an
- Zugriff auf die Daten aller Websites
- Anzeigen von Benachrichtigungen
- Zugriff auf Browser-Tabs
- Grammarly injiziert ein Stylesheet in die Webseite, das aus lokalen Erweiterungs-Assets geladen wird
- Dieses Stylesheet kann von der Webseite nicht über die StyleSheetList gefunden werden
- Es umgeht auch die Content Security Policy
- In Firefox verhält es sich wie ein Stealth-Stylesheet, das von der Website selbst nur schwer erkannt werden kann
- Die Erweiterung fügt dem
<html>-Dokument jeder Website auch dann ein benutzerdefiniertes <grammarly-desktop-integration>-Element hinzu, wenn der Nutzer gar nicht mit ihr interagiert
Wie ein einzelner Name --rem das Layout zerstörte
- Am Ende des Grammarly-Stylesheets steht folgendes CSS
:host,
:root {
--rem:16
}
- In anderen Teilen desselben Stylesheets wird
--rem verwendet, um Schriftgröße und Zeilenhöhe zu berechnen
.kE2Bj {
font-size:calc(0.86px*(var(--rem) - 2));
line-height:calc(1.2868px*(var(--rem) - 2));
}
- Auch die Website selbst verwendete für eigene Experimente mit Fluid-Typografie die Custom Property
--rem
@layer base {
:root {
--rem: 0.0625rem;
--fluid: calc((100vi - (400 * var(--rem))) / (1920 - 400));
--font-size-h1: clamp(
calc(31 * var(--rem)),
calc((31 * var(--rem)) + (80 - 31) * var(--fluid)),
calc(80 * var(--rem))
);
}
}
- Das
--rem der Website war innerhalb einer Cascade Layer definiert, und Styles außerhalb von Layern haben unabhängig von der CSS-Spezifität Vorrang vor Styles innerhalb von Layern
- Auch die Reihenfolge im Quelltext spielt eine Rolle, daher könnte Grammarlys
--rem gewonnen haben
- Dadurch wurden die Berechnungsformeln der Website beschädigt und Layout-Probleme ausgelöst
- Anfangs reagierte man, indem man mit einem Mutation Observer die hinzugefügte Web-Komponente erkannte und dann Styles mit
!important ergänzte
- Nachdem die genaue Ursache geklärt war, wurde der Name der Custom Property der Website in
--🤡 geändert
- Dieser Name ist in CSS ein gültiger Name für eine Custom Property
--rem ist ein riskanter Name geworden, wenn Grammarly ihn global verwendet
- Obwohl Grammarly zufällige Klassennamen erzeugt, wendet es die generische Custom Property
--rem global auf :root an und injiziert Code in jede Webseite, selbst wenn die Erweiterung dort gar nicht aktiv genutzt wird
- Das Grammarly-Supportteam wurde kontaktiert, aber bisher hat die Anfrage noch keine technisch zuständige Person erreicht
1 Kommentare
Meinungen auf Hacker News
Mein Fall mit einem Extension-Problem ist etwas anders. Ich veröffentliche eine Extension, mit der man für Geolocation-Tests einfach Proxy-Server wechseln kann.
Vor ein paar Monaten hatte ich die schlimmste Kundendemo überhaupt: Es sah einfach so aus, als würde das Produkt gar nicht funktionieren. Nach langem Debugging stellte sich heraus, dass ein aktuelles Update der 1Password-Extension unsere Extension kaputtgemacht hatte. 1Password hatte Authentifizierungs-Events abonniert, sie aber nicht zurückgegeben, sodass es zu einem Timeout kam und unser Subscriber deshalb nie aufgerufen wurde. Unsere Extension hatte den Browser angewiesen, den Proxy-Server zu ändern, und war bereit, Zugangsdaten bereitzustellen, aber die Anfrage kam nie. Der Support von 1Password war besser als Grammarlys, aber es ist schwer, über den Support irgendeinen unbekannten PM von der Priorität zu überzeugen.
Später stellte sich heraus, dass eine bestimmte Extension, die für Websites der russischen Regierung nötig ist, dasselbe Problem hat.
Aus der Sicht von jemandem, der seit über zehn Jahren mit Extensions zu tun hat, trägt am Ende Google einen großen Teil der Verantwortung. Abgesehen von der politischen Frage rund um Änderungen bei Adblockern ist Manifest v3 in vielerlei Hinsicht deutlich schlechter als erwartet.
Insgesamt fühlt es sich so an, als sei die Qualität der Chromium-Codebasis im Vergleich zu früher stark gesunken.
Wenn man Skripte oder Styles in unbekannte Seiten injiziert, sollte man wenigstens Variablen-Namensräume trennen.
Der Interviewer tat das abfällig ab, als würden heutige Tools so etwas sowieso alles erledigen und jeder würde es machen. Ich musste ihm bis zu einem gewissen Grad zustimmen, weil ich aktuell nicht mehr in dem Bereich arbeite und es daher nicht wirklich weiß. Wie sich herausstellt, macht es aber eben doch nicht jeder.
So konnten wir klar unterscheiden, was von uns eingefügt wurde und was ursprünglich vorhanden war, und potenzielle Konflikte vermeiden.
Es ist beängstigend, diesen grünen Eindringling bei Screen-Sharing oder Aufzeichnungen standardmäßig auf jeder Website zu sehen. Es geht nicht nur darum, dass es optisch stört; damit kommen Datenschutzprobleme und ein offensichtlicher Angriffsvektor.
In Chrome kann man Extensions nur bei Bedarf aktivieren – ich verstehe nicht, warum das niemand macht. Und ich frage mich auch, warum das nicht in allen Browsern die Standardeinstellung ist.
Einige Kollegen fühlen sich unwohl damit, dass Informationen möglicherweise an Dritte gelangen, und unterbrechen das Meeting, bis die Extensions deaktiviert sind.
Ich bin Engineer an der Grammarly Extension. Zunächst einmal tut es uns wirklich leid, dass unsere Extension die User Experience auf dbushell.com beeinträchtigt und den Autor Zeit und Mühe gekostet hat, die Ursache zu finden.
Das war nicht beabsichtigt, und wir verwenden mehrere Techniken, um solche Fälle zu verhindern. Sie waren aber nicht ausreichend, und der Beitrag zeigt klar, wo Verbesserungsbedarf besteht.
Als schnelle Korrektur haben wir dbushell.com vorübergehend als Ausnahme hinzugefügt. Gleichzeitig arbeiten wir an Änderungen, die eine angemessene Style-Isolation sicherstellen; solche Probleme sollten niemals auftreten.
Ich habe ein ähnliches Problem, bei dem Google Translate meine Web-App kaputtmacht. Nutzer verwenden Google Translate und beschweren sich dann, dass meine App defekt sei, tatsächlich aber hat Google auf einer höheren Meta-Ebene den Zustand der App verändert. Das ist wirklich schlechte Praxis.
Ich versuche gerade, Google Translate zu erkennen und eine Warnung anzuzeigen.
Manchmal muss zum Beispiel ein Satz wie „[Hier klicken], um weitere Informationen zu sehen“ übersetzt werden. In einer anderen Sprache muss der Link vielleicht ans Satzende verschoben werden, etwa zu „Um weitere Informationen zu sehen, [hier klicken]“. Dafür ist eine Neuanordnung von DOM-Elementen nötig, und das kann mit interaktiven Apps kollidieren.
Das Google-Translate-Team kann viel tun, um Eingriffe zu reduzieren, aber ohne neue Browser-APIs dürfte es schwer sein, sie vollständig zu eliminieren.
Ich habe es an das Engineering-Team weitergeleitet.
Auch dort, wo ich arbeite, macht mich wahnsinnig, dass die Leute das nicht tun. Sogar der Engineering Director fügt Dinge als eigenes Ticket hinzu, die weniger Zeit kosten würden, wenn man sie einfach erledigt. Trotzdem ist es ein gutes Zeichen, dass ich oft höre: „Ich habe kein Ticket erstellt, um eine Nachricht zu schicken, sondern deiner Vorgehensweise folgend direkt der Person geschrieben.“
In Unternehmen gibt es viele Sentry-Fehler, die dadurch entstehen, dass Browser-Erweiterungen seltsame Dinge tun.
Auch Google Translate in Chrome ist berüchtigt dafür, React-basierte Websites kaputtzumachen.
Am Ende wird daraus eine mühsame Triage-Arbeit, bei der man neue Erweiterungsprobleme einzeln ignoriert. Um das Erfassungsvolumen zu reduzieren, nutzen wir clientseitiges Filtering. Insgesamt gibt es im Frontend viel mehr Rauschen als im Backend, daher müssen die Schwellenwerte deutlich höher liegen.
Dass es im Frontend viel mehr Fehler gibt, ist nicht überraschend. Man muss dort deutlich mehr Client-Varianten unterstützen als bei einem typischen Backend. Eine große Web-App zu bauen, die für alle gut funktioniert, kann sehr schwierig sein.
Ich frage mich, welche einzelne Variable man einschleusen müsste, um das Web maximal kaputtzumachen. Mir fällt so etwas ein:
--primary-color: transparent--serif: "Comic Sans MS"Wie sollte man mit feindseligen Browser-Erweiterungen umgehen?
Während ich darüber nachdachte, öffnete ich irgendeine Seite von The Guardian in den DevTools, und jemand hatte dort ein Script und ein iframe eingefügt, die auf twitter.com verweisen.
Ich mag Grammarly oder dessen technisches Modell nicht, aber es ist nicht fair, Böswilligkeit anzunehmen, wenn Dummheit als Erklärung ausreicht.
Es ist schon eine Weile her, dass ich Frontend-Arbeit gemacht habe, aber sollten nicht sowohl die Grammarly-Erweiterung als auch der eigene Code namensraumgetrennte Attributnamen verwenden?
Ich frage mich, ob man das nutzen könnte, um dieses Plugin zu hijacken. Zumindest dürfte man Text injizieren können, und vermutlich könnte man auch ein hübsches Login-Formular rendern, das das Vertrauen der Nutzer in die Erweiterung ausnutzt.
Ist es wirklich sicher, Elemente in ein Dokument einzufügen, das von jemand anderem kontrolliert wird?
Was man tun kann, ist, die UI der Erweiterung innerhalb der Website nachzuahmen; dafür braucht man aber keine Injektion. Man kopiert einfach das Design.