Ghost

Ghost DSGVO-konform machen

  • dani

dani

11 min read
Photo by Tingey Injury Law Firm / Unsplash

In diesem Artikel zeige ich einige allgemeine Punkte auf, die Ghost konformer mit der Datenschutzgrundverordnung (DSGVO), auch als General Data Protection Regulation (GDPR) bekannt, machen. Die DSGVO ist die europäische Verordnung über den Datenschutz. Ihr Ziel ist es, den europäischen Bürgern die Kontrolle über ihre persönlichen Daten zu geben. Alle Unternehmen, die in der Europäischen Union tätig sind oder deren Websites in der EU verfügbar sind, müssen sich an die DSGVO halten. Das bedeutet, dass alle Blogger, die eine Ghost-Website betreiben, die in der EU nicht gesperrt ist, die Bestimmungen einhalten müssen.

⚠️
Die in diesem Artikel enthaltenen Informationen sind lediglich allgemeine und unverbindliche Angaben. Der Artikel stellt keine Rechtsberatung dar und ersetzt nicht die Beratung durch einen Rechtsanwalt.

Nach der Installation von Ghost ist dieses nicht DSGVO-konform oder befindet sich zumindest in einer rechtlichen Grauzone, in der du als Blogger möglicherweise nicht vor Abmahnungen sicher bist. Im Folgenden werde ich über einige Dinge sprechen, die beim Betrieb eines Ghost-Blogs zu beachten sind.

1) HTTP Requests an CDN

Ein Content Delivery/Distribution Network (CDN) dient der Bereitstellung von Web-Assets wie Bild-, JavaScript- oder CSS-Dateien. Es handelt sich um ein globales Netzwerk von Servern, das einen Server in der Nähe deines Standorts auswählt, um die Dateien schneller bereitzustellen. Wenn du eine Datei aus einem CDN in deine Website einbindest, weist du den Browser des Benutzers an, einen HTTP Request, also eine Anfrage über das HTTP Protokoll, für die Datei an das CDN zu senden. Dies hat zur Folge, dass der Benutzer, der deine Website besucht, nicht nur deine Website besucht, die auf den Servern unter deiner Domäne gehostet wird, sondern auch die Server des CDN besucht. Die gesendete HTTP-Anfrage enthält Daten über den Nutzer, die unter die DSGVO fallen (z. B. die IP-Adresse). Daher solltest du alle Anfragen an CDNs vollständig auslassen, vor allem, da diese möglicherweise an Server außerhalb der Europäischen Union gerichtet sind. Es besteht indes sowieso keine Notwendigkeit, CDNs überhaupt einzubeziehen.

Welche Anfragen werden also von deiner Website an CDNs gesendet? Du kannst das mit jedem modernen Browser wie Firefox oder Chrome leicht überprüfen. In Firefox gibt es den Page Inspector, der in den Web Developer Tools enthalten ist, und in Chrome gibt es die DevTools. Du kannst beide öffnen, indem du mit der rechten Maustaste auf deine Webseite klickst und "inspizieren" wählst. Daraufhin öffnet sich ein Fenster am unteren oder rechten Rand deines Browsers. Wähle die Registerkarte "Netzwerk". Lade deine Seite mit F5 neu und es werden alle gesendeten HTTP-Anfragen angezeigt.

ℹ️
Im Folgenden werde ich zeigen, wie man mit Firefox arbeitet, da ich diesen Browser hauptsächlich für die Webentwicklung verwende. Chrome (und sogar Safari) sollten ähnlich sein.

Bei einem frisch installierten Ghost mit dem Casper Theme wirst du die folgenden Anfragen sehen:

HTTP-Anfragen, die von Ghost mit Casper gesendet werden, werden im Firefox-Seiteninspektor auf der Registerkarte Netzwerk angezeigt
HTTP-Anfragen, die von Ghost mit Casper gesendet werden, werden im Firefox-Seiteninspektor auf der Registerkarte Netzwerk angezeigt

Im Idealfall zeigt diese Registerkarte nur Anfragen an deine eigene Domäne an. In diesem Fall ist meine Domäne localhost:2368, da ich eine lokale Umgebung verwende. Man kann sehen, dass es auch Anfragen an die Domänen cdn.jsdelivr.net (2x), code.jquery.com und static.ghost.org (2x) gibt. Im Folgenden werde ich zeigen, wie man sie alle loswerden kann.

💡
Von jeder Unterseite deiner Website solltest du nur deine eigene Domain in den gesendeten Anfragen sehen. Überprüfe alle deine Unterseiten, ob du eine Anfrage vergessen hast, die unter bestimmten Bedingungen gesendet wird, und entferne die Anfrage.

Die Ghost-Entwickler haben sich dafür entschieden, den JavaScript-Code für die Portalfunktion (Mitgliederanmeldung und -zugang) und die Suchfunktion aus dem jsdelivr CDN zu übernehmen. Zumindest geben die Entwickler einem die Möglichkeit, die Dateien auf dem eigenen Server zu hosten. Folge dazu einfach den Anfragen zu deren URL. Du findest die URL im Page Inspector, indem du auf die Anfragen klickst. Entferne den Dateinamen am Ende, um die Ordner der Projekte anzuzeigen, die alle Dateien enthalten. Sei vorsichtig, vielleicht willst du auch die .css-Dateien aus den Verzeichnissen. Öffne die Dateien in einer neuen Registerkarte des Browsers und lade die Dateien auf deinen Computer herunter.

Die URL für die Anfrage wird auf der rechten Seite im Page Inspector angezeigt
Die URL für die Anfrage wird auf der rechten Seite im Page Inspector angezeigt

Die Dateien für das Portal unter https://cdn.jsdelivr.net/ghost/portal@~2.12/umd/ sollten sein:

  • https://cdn.jsdelivr.net/npm/@tryghost/portal@2.12.0/umd/portal.min.js

Achte darauf, die richtige Version für deine Ghost-Version zu wählen. Führe eine Kreuzvalidierung mit den von deiner Ghost-Installation gesendeten Anfragen durch. Die Dateien für sodo-search finden sich unter https://cdn.jsdelivr.net/npm/@tryghost/sodo-search@1.1.0/umd/ und sollten wie folgt lauten:

  • https://cdn.jsdelivr.net/npm/@tryghost/sodo-search@1.1.0/umd/main.css
  • https://cdn.jsdelivr.net/npm/@tryghost/sodo-search@1.1.0/umd/sodo-search.min.js

Nachdem alle Dateien heruntergeladen sind, erstelle ein Verzeichnis assets/jsdevlir in deinem Theme. Lege die Dateien in diesem Verzeichnis ab. Ich habe sie so umbenannt, dass sie die Versionsnummer enthalten, damit ich die Version der Dateien leicht erkennen kann:

daniel@DESKTOP:~/ghost/content/themes/casper$ ls assets/jsdelivr/
portal-2.12.min.js  sodo-search-1.1.main.css  sodo-search-1.1.min.js

Als nächstes passen wir die Konfigurationsdatei im Ghost Home-Verzeichnis namens config.<environment>.json an und fügen die Einstellungen für das Portal und sodo-search wie folgt hinzu:

...
  "sodoSearch": {
    "url": "/assets/jsdelivr/sodo-search-1.1.min.js",
    "styles": "/assets/jsdelivr/sodo-search-1.1.main.css"
  },
  "portal": {
    "url": "/assets/jsdelivr/portal-2.12.min.js"
  },
 ...

Dadurch wird Ghost angewiesen, die Dateien aus unserem Verzeichnis assets/jsdevlir aus dem aktiven Theme zu laden. Du kannst das Theme mit den von uns hinzugefügten Dateien ohne Probleme in deine Ghost-Produktionsumgebung hochladen. Bearbeite einfach die Produktionskonfiguration und fügen die oben genannten Einstellungen hinzu.

Nach der Änderung der Konfiguration musst du Ghost möglicherweise neu starten, damit die Änderungen wirksam wird. Nach dem Neustart sollten die Anfragen an das CDN cdn.jsdelivr.net für immer verschwunden sein.

ℹ️
Bitte beachte: Wenn du die Kommentarfunktion auf deiner Blogseite aktivierst, musst du dies auch für die JavaScript- und CSS-Dateien tun, die für die Kommentarfunktion erforderlich sind. Diese werden standardmäßig ebenfalls von cdn.jsdevlir.net geladen. Bitte schaue dir die Ghost Dokumentation über den Kommentarbereich an, um die Konfiguration anzupassen.

1.2) Anfragen an jQuery

jQuery ist eine sehr bekannte Bibliothek für JavaScript, die die Entwicklung mit JavaScript erleichtert. Sie wurde um 2010 herum sehr berühmt, als die Internetverbindungen schnell genug wurden, um größere Bibliotheksdateien zu laden, und die Leute begannen, ausgefallene Webseiten mit AJAX-Anfragen und allen Arten von UI-Effekten zu erstellen.

Die jQuery-Bibliothek wird durch das Casper Theme geladen, nicht durch den Ghost Kern selbst. In der Datei default.hbs findest du ab Zeile 89 einen <script> Tag, der jQuery direkt von code.jquery.com lädt. Auch hier könnten wir die Datei von der dort angegebenen URL herunterladen, sie einfach in ein Verzeichnis innerhalb unseres Themas legen und die lokale Datei einbinden. Ich werde jedoch einen saubereren Ansatz wählen, nämlich die jQuery-Abhängigkeit mit yarn zu verwalten. Dadurch wird jQuery zur package.json-Datei hinzugefügt und die Versionsnummer gesperrt. Wir können die Version später einfach ändern.

Um jQuery hinzuzufügen, führen wir den folgenden Befehl aus dem Terminal heraus in unserem Themenverzeichnis aus:

yarn add jquery

Als nächstes müssen wir jQuery zu unserem Paket hinzufügen. Im gulpfile.js fügen wir eine neue Funktion hinzu, z.B. nach der schließenden Klammer von function js(done). Die Funktion weist gulp an, die Dateien aus dem Ordner node_modules in unseren Ordner assets/built zu kopieren:

function jquery(done) {
    pump([
        src('node_modules/jquery/dist/*.js'),
        dest('assets/built/', {sourcemaps: '.'}),
        livereload()
    ], handleError(done));
}

Ändere die Zeile 95 const build = series(css, js); und füge die gerade erstellte jquery Funktion vor js ein:

const build = series(css, jquery, js);

Schließlich können wir den <script> Tag, der die jQuery-Dateien in default.hbs lädt, ändern:

<script src="{{asset "built/jquery.js"}}"></script>

Führe den Befehl yarn build in deinem Theme aus und starte Ghost eventuell neu. Danach sollte die Anfrage an das jQuery CDN verschwunden sein, aber dein jQuery sollte noch funktionieren.

1.3) Anfrage an static.ghost.org

Die Anfragen holen nur die Dateien publication-cover.jpg und feature-image.jpg von den Ghost-Servern ab. In deinem Ghost Admin > Einstellungen > Design > Brand ändere das "publication cover", indem du es entweder löschst oder durch deine eigene Datei ersetzt (du kannst die Datei auch von den Ghost-Servern auf deinen Computer herunterladen und erneut hochladen). Speichere die Änderungen. Dann sollte die Anfrage zu publication-cover.jpg verschwunden sein.

Für feature-image.jpg musst du zu dem Standardbeitrag mit dem Titel "Coming soon" navigieren und sein Feature-Bild löschen oder ersetzen und den Beitrag speichern oder den Beitrag ganz löschen. Dann sollte auch die Anfrage für feature-image.jpg verschwunden sein.

Bitte ersetze keines der Bilder durch ein Bild von Unsplash. Platziere niemals ein Bild von Unsplash in deinen Inhalten, indem du die von Ghost bereitgestellte Integration verwendest. Ich werde dies im nächsten Abschnitt erläutern.

2) Embeds

Beim Schreiben von Blogbeiträgen kannst du mit Ghost ganz einfach beliebige Inhalte einbetten. Sei es ein Foto von Unsplash als Feature-Image oder Content-Image, ein YouTube-Video oder ein Twitter-Post. Das ist alles schlecht.

Die Unsplash-Fotos sind hier vielleicht das kleinere Übel. Wenn du ein Foto von Unsplash mit der von Ghost bereitgestellten integrierten Funktion einbindest, bleibt die eingebundene Datei auf dem Unsplash-Server gespeichert. Damit befindest du dich also im Grunde in der gleichen Situation wie bei den CDNs. Die Dateien werden von einem externen Server geladen, der die Daten des Benutzers, wie z.B. seine IP-Adresse, erhält. Als Workaround kannst du Unsplash.com oder Pexels.com selbst durchsuchen, die Bilddateien herunterladen und sie mit der bereitgestellten Upload-Funktion hochladen. Und natürlich den Urhebernachweis selbst einfügen.

Andere Embeds bzw. Einbettungen wie YouTube, Instagram, Twitter, Vimeo usw. sind noch schlimmer. Sie betten üblicherweise JavaScript in deine Webseite ein. Du hast absolut keine Kontrolle darüber, was das JavaScript macht. Es kann seine eigenen Tracking-Cookies erstellen, so dass der Nutzer über Hunderte von Webseiten hinweg verfolgt werden kann. So sendet z.B. eine YouTube-Einbettung Anfragen an mehrere Google-Server (selbst wenn du dich für den erweiterten Datenschutz entscheidest) und erstellt mehrere Cookies. Als Abhilfe kannst du dein eigenes Titelbild für ein Video hochladen und dieses Titelbild mit dem Videolink verknüpfen. Auf diese Weise muss der Nutzer auf das Titelbild klicken, um deine Seite ausdrücklich zu verlassen. Auf diese Weise gibst du dem Nutzer die Kontrolle zurück. Im besten Fall gibst du in der Bildbeschreibung unterhalb des Bildes einen Hinweis darauf, dass der Nutzer deine Seite verlässt und zu YouTube weitergeleitet wird.

3) Code Injektionen

Eine der Philosophien von Ghost ist, dass es keine Plugins gibt. Im Gegensatz zu WordPress, wo man irgendwie ein Dutzend Plugins installieren muss, um die Funktionen zu erhalten, die man braucht.

Der WordPress-Weg – ein Plugin nach dem anderen hinzuzufügen – ist schlecht, weil man den Quellcode der Plugins nicht wirklich kontrollieren kann. Sie können CDN-Anfragen oder andere Tracking-Funktionen einführen, die möglicherweise nicht DSGVO-konform sind. WordPress eigenes Plugin JetPack ist ein gutes Beispiel.

Die Weg von Ghost beruht auf Code Injections bzw. Injektionen. Auf der Ghost-Integrationsseite wird für Integrationen fast immer die Code-Injektion empfohlen. Du kannst Code global für alle Ghost-Seiten über Ghost Admin > Einstellungen > Code Injection oder für jeden Beitrag oder jede Seite einzeln einfügen.

Wie bereits bei den Embeds erwähnt, kann durch Code Injektionen beliebiger JavaScript Code in deiner Seite eingefügt werden, den du als Website-Administrator nicht kontrollieren kannst. Er kann sich von einem Tag auf den anderen ändern. Du müsstest also die Änderungen am eingefügten Code im Auge behalten. Außerdem kann der Code Tracking-Cookies erstellen und mehrere Anfragen senden. Bei jeder Code Injektion muss der Benutzer zustimmen, d.h. er muss um Erlaubnis zum Laden des Codes gebeten werden. Das kannst du mit einer Cookie-Einwilligung (bzw. Cookie Consent) erreichen (darüber werde ich im nächsten Abschnitt sprechen). Außerdem musst du die Injektion und die verwendeten Cookies in deiner Datenschutzrichtlinie offenlegen und dem Nutzer die Möglichkeit geben, die Cookies wieder abzulehnen (opt-out). Es ist also besser, überhaupt keine Injektionen vorzunehmen.

Du musst deine Benutzer über die auf deiner Website verwendeten Cookies informieren und ein Opt-In für alle nicht unbedingt erforderlichen Cookies einholen. Wesentliche Cookies sind beispielsweise der Cookie für die Ghost-Sitzungs-ID. Ohne dieses Cookie würden bestimmte Funktionen der Ghost-Website nicht funktionieren und die Website könnte nicht wie erwartet funktionieren. Alle anderen Cookies wie Analyse-Cookies (ich werde im nächsten Abschnitt über die Analyse des Nutzerverhaltens sprechen) oder alle Einbettungs- oder Injektions-Cookies wie YouTube usw. sind nicht unbedingt erforderlich. Der Nutzer muss sich für diese nicht wesentlichen Cookies entscheiden.

Es gibt einige gut ausgebaute Bibliotheken für Cookie-Einwilligungen. Ich habe mich für orestbida/cookieconsent entschieden. Du kannst dir auf der Homepage Demoprojekte ansehen. Um diese Bibliothek zu deinem Ghost Theme hinzuzufügen, führe den folgenden yarn Befehl über das Terminal in deinem  Theme Verzeichnis aus:

yarn add vanilla-cookieconsent

Dadurch wird die Bibliothek über die package.json Datei als eine Abhängigkeit hinzugefügt. Passe dein gulpfile.js an und füge eine Funktion hinzu, um die neue Abhängigkeit zu dem Paket hinzuzufügen, das aus dem gulp Build-Prozess resultiert:

function cookieconsent(done) {
    pump([
        src(['node_modules/vanilla-cookieconsent/dist/*.js',
        'node_modules/vanilla-cookieconsent/dist/*.css']),
        dest('assets/built/', {sourcemaps: '.'}),
        livereload()
    ], handleError(done));
}

Ändere auch hier, wie bei jQuery, die Zeile const build = series(css, js, ...);, um die gerade erstellte Funktion cookieconsent einzuschließen:

const build = series(css, jquery, js, cookieconsent);

Als Nächstes, füge das Skript-Tag in default.hbs in Zeile 89 vor jQuery ein:

<script src="{{asset "built/cookieconsent.js"}}"></script>

Als letzten Schritt müssen wir die cookieconsent-Bibliothek initialisieren. Dazu erstellen wir eine neue JavaScript-Datei in unserem Theme im Verzeichnis assets/js und nennen sie cookieconsentinit.js. Der Name spielt keine große Rolle, denn sie wird zusammen mit allen anderen Dateien im Verzeichnis assets/js in die Datei casper.js kompiliert. Wir erstellen einfach eine neue Datei, damit wir sie als Theme-Entwickler von den anderen Dateien getrennt haben. In der neuen Datei assets/js/cookieconsentinit.js rufen wir die Funktion zum Initialisieren der Bibliothek mit deinen gewünschten Parametern auf:

// obtain plugin
var cc = initCookieConsent();

// run plugin with your configuration
cc.run({
  // your parameters here ...
});

Eine Standardkonfiguration erhälst du über den Toggle "As external script" auf der Dokumentationsseite für die Installation und Verwendung. Nachdem du die Dateien hinzugefügt hast, führe yarn build aus und starte Ghost neu. Dann sollte die Cookie-Einwilligung als Popup auf deiner Seite erscheinen.

5) Analyse des Nutzerverhaltens

Um eine erfolgreiche Website aufzubauen, sammelt jeder Website-Besitzer gerne Daten über das Nutzerverhalten auf seiner Seite. Das bekannteste Analysetool ist Google Analytics. Aber es ist wirklich schwierig, Google Analytics mit der DSGVO in Einklang zu bringen – darüber werde ich hier nicht sprechen. Ich schlage vor, eine Instanz von Matomo (früher bekannt als Piwik) auf deinen eigenen Webservern zu hosten. Matomo ist in wenigen Minuten einfach installiert. Nach der Installation solltest du einige Datenschutzeinstellungen in Matomo ändern, um es DSGVO-konform zu machen. Auch darüber werde ich hier nicht sprechen. Stattdessen bietet Matomo einige großartige Ressourcen zum Thema Datenschutz.

Unter Umständen solltest du das MATOMO_SESSID-Cookie in deine Cookie-Einwilligung aus dem letzten Abschnitt aufnehmen. Außerdem musst du in deiner Datenschutzerklärung angeben, welche Daten du sammelst, welchem Zweck sie dienen usw. Zu guter Letzt solltest du auch die Möglichkeit zur Abmeldung von der Analyse (Opt-Out) vorsehen.

6) Erklärung zum Datenschutz

Du solltest auf deiner Website eine Datenschutzerklärung einfügen und z.B. in der Fußzeile darauf verweisen. Kurz gesagt sollte die Datenschutzerklärung angeben, welche Daten du sammelst, welchem Zweck die Daten dienen, wie du mit den Daten umgehst und wie der Nutzer die Daten kontrollieren kann. Du kannst deine Datenschutzrichtlinie mit dem Generator des Vertrauens erstellen. Du solltest jedoch immer einen Anwalt mit Fachkenntnissen in Sachen Datenschutz konsultieren, um sicherzustellen, dass deine Datenschutzrichtlinie und dein Ghost-Blog insgesamt DSGVO-konform sind.

7) Ghost Access Logs

Dieser Teil ist einer, den man leicht übersehen kann. Ghost verwendet einen nginx-Webserver, um seine Seiten über HTTP auszuliefern. Normalerweise erzeugt jeder Webserver Dateien mit Zugriffsprotokolls (Access Logs), die Header-Informationen des HTTP-Requests (z. B. die IP-Adresse) von den Benutzern enthalten, die deine Seite besuchen. Der nginx-Webserver hinter deiner Ghost-Seite erstellt tatsächlich solche Logs. Die Zugriffsprotokolls findest du in ghost/content/logs/:

  1. Ein <site>_<environment>.error.log, das alle Fehler enthält, die in deiner Ghost-Instanz auftreten.
  2. Ein <site>_<environment>.log, das das Zugriffsprotokoll mit allen Besuchen auf deinen Ghost-Seiten enthält.

Ich habe bisher jedoch noch keine Möglichkeit gefunden, die Protokolle zu deaktivieren. Laut der Konfigurationsdokumentation zur Protokollierung können die Protokollstufe (Log Levels) geändert werden. Ich habe versucht, die Protokollstufe auf "nur Fehler" zu ändern, aber das allgemeine Zugriffsprotokoll wurde weiterhin erstellt. Als Abhilfe kannst du die Protokolle mit einem serverseitigen Cronjob löschen, der die Dateien in regelmäßigen Abständen löscht. Das solltest du auf jeden Fall tun, da die Dateien je nach Datenverkehr auf deiner Website an Größe zunehmen können. Zumindest musst du in deiner Datenschutzerklärung auf die Protokollierung hinweisen.