Wie kann ich eine statische Webseite in einem AWS S3 Bucket mit einem Passwort schützen?

Amazon Web Service (AWS) erlaubt es einen auf einfache Art, eine Webseite in einem S3 Bucket hochzuladen und dafür eine URL zu generieren, so dass man in einem Browser Zugriff dafür hat.

Wie man das macht, lernt man zum Beispiel im Udemy.com Kurs Angular Essentials von Maximilian Schwarzmüller, welcher ich wärmstens empfehlen kann!

In diesem Kurs erstellt man eine Liste von Starwars Charakteren und lernt am Ende, wie man das Ganze in einen S3 Bucket hochlädt.

Was aber, wenn ich diese Seite nun mit einem Passwort schützen möchte?

Wenn ich eine Webseite auf einem Server habe ist es üblicherweise kein grosses Problem, darauf eine Authentifizierung dazwischenzuschalten (bei einem Apache oder Nginx kann das zum Beispiel via htpasswd geschehen).

Bei einer serverless Seite, die auf einem S3 Bucket liegt, ist das hinzufügen einer Authentifizierung ein wenig trickreicher. Es gibt aber eine Möglichkeit, die Seite Passwort geschützt zu machen, indem man vor die Webseite eine Lambda-Funktion stellt und mittels AWS CloudFront die Verlinkung vornimmt.

Hochladen einer Beispielseite in einen S3 Bucket

Um diesen Post durchführen zu können braucht man natürlich eine Webseite. Hat an keine zur Verfügung kann man einfach irgendeine Beispielseite als „index.html“ speichern, etwa folgende:

<html>
  <head>    
    <title>Testseite</title>  
  </head>
  <body>
  Meine Seite funktioniert!
  </body>
</html>

Und natürlich braucht man einen AWS Account dafür. Falls Sie noch keinen haben, können Sie sich problemlos einen kostenlosen Account erstellen, der ein Jahr gültig ist.

Sobald Sie in AWS eingeloggt sind, wählen Sie „S3“ als Service aus und erstellen mit „Create Bucket“ einen Kübel.

Nun kann die Datei „index.html“ mittels „Upload“ Button hochgeladen werden.

Damit man diese Seite online aufrufen kann, klicken Sie auf „Properties“ und dann „Static website hosting„. Als Index und Error Document können sie zweimal „index.html“ angeben.

Wenn Sie im Bucket auf „Permissions“ klicken, sehen Sie, dass der generelle Zugriff auf aussen geblockt ist („Block all public access„). Damit man die Webseite im Internet einsehen kann, muss man dies natürlich ändern und den Haken entfernen und dies in einem Dialog „confirmen„.

Jetzt müssen Sie noch die „index.html“ Datei klicken und dann unter „Overview“ auf „Make public„. Ganz unten sieht man nun die „Object URL„, unter der die Seite aufgerufen werden kann.

Alternativ klicken Sie nochmals auf „Properties“ und dann auf „Static website hosting“ und sie sehen oben die öffentliche URL.

Nun haben wir eine Testseite, für welche wir Authentifizierung einbauen können.

Verbreitung der Webseite mittels AWS CloudFront

CloudFront ist der AWS Verteilungsservice, mit welchem Sie Ihre Webseite nun auf Server auf der ganzen Welt verteilen können, damit Benutzer immer den schnellstmöglichen Zugriff darauf erhalten. Zusätzlich dazu erlaubt uns CloudFront in Zusammenarbeit mit Lambda auch, eine Authentifizierung aufzusetzen.

Wählen Sie zuerst in AWS den CloudFront Service aus. Da wahrscheinlich noch keine „Distribution“ verfügbar ist, klicken Sie auf „Create Distribution“ und dann im „Web“ Bereich auf „Get Started„.

Dort sind folgende Einstellungen wichtig:

  • Origin Domain Name: Dies muss der vorher erstellte Bucket sein. Kliken Sie einfach in das Feld und wählen Sie den Bucket aus.
  • Setzen Sie „Restrict Bucket Access“ auf „Yes
  • Unter „Origin Access Identity“ klicken Sie „Create a New Identity
  • Setzen Sie „Yes, Update Bucket Policy
  • Unten gibt es noch ein Feld „Default Root Object„: Geben Sie hier „index.html“ ein.
  • Klicken Sie auf „Create Distribution

Und weiter unten:

Wenn Sie nun im linken Menü auf „Distributions“ klicken sehen Sie die neu erstellte Distribution. Merken Sie sich den „Domain Name“ – Sobald sie fertig initialisiert wurde wird Ihre Webseite unter dieser URL aufgerufen werden können. Die Initialisierung dauert aber ein paar Minunten. In dieser Zeit können wir die IAM Rolle erstellen.

Erstellen der IAM Rolle

Damit die Lambda Funktion auf den S3 Bucket zugreifen kann, brauchen wir eine IAM Rolle, welche den Zugriff regelt. Gehen Sie also in den „IAM“ Service und klicken Sie links „Roles“ und dann „Create Role„.

Unter „Choose the service that will use this role“ wählen Sie „Lambda“ und klicken auf „Next: Permissions„.

Suchen Sie nach „AWSLambdaExecute“ und setzen Sie links davon einen Haken. Dann können Sie zweimal „Next“ klicken und dürfen Ihrer neuen Rolle noch einen Namen geben, etwa „lambda-website-execute„. Klicken Sie auf „Create Role“ um die Rolle zu erstellen.

Für die Authentifizierung benötigen wir Lambda@Edge, was wir der Lambda Policy angeben müssen. Klicken Sie Ihre neue Rolle und wählen dann „Trust relationships“ und dann „Edit trust relationship„.

Ersetzen Sie den gesamten Inhalt mit folgendem Text und klicken Sie auf „Update trust relationship„:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": [
            "lambda.amazonaws.com",
            "edgelambda.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Authentifizierung mit einer Lambda Funktion einbauen

Nun sind alle Teile bereit für den Einbau der eigentlichen Passwortabfrage mittels Lambda.

Zuerst aber noch etwas wichtiges: Damit wir später unsere CloudFront Distribution als Trigger hinzufügen können, müssen wir die „AWS Region“ auf „US East (N. Virginia)“ ändern (im schwarzen Balken ganz oben rechts. Das funktioniert übrigens erst im „Lambda“ Service, nicht im „IAM„.)

Lambda Funktionen laufen serverless, das bedeutet, wir müssen nur die Funktion erstellen, aber keinen Server, auf dem diese läuft. Die Funktion wird aufgerufen, wenn sie benötigt wird und in einer produktiven Umgebung zahlt man dann auch entsprechend der Anzahl Aufrufe.

Klicken Sie in „Lambda“ auf „Create a function„.

  • Lassen Sie auf der nächsten Seite „Author from scratch“ selektiert, um anzuzeigen, dass Sie eine leere Funktion erstellen möchten.
  • Als „Function name“ wählen Sie zum Beispiel „website-authentication
  • Als „Runtime“ ist die neueste Node.js Version auszuwählen.
  • Unter „Permissions“ klicken Sie auf das schwarze Dreieck, setzen dann „Use an existing role“ und wählen dann die erstellte IAM Rolle „lambda-website-execute“ aus, in dem Sie in das Feld klicken.
  • Anschliessend können Sie auf den orangen „Create function“ Knopf klicken.

Scrollen Sie nun runter in den Code Editor und erstezen den vorhandenen Code mit folgendem Snippet:

exports.handler = (event, context, callback) => {

  // Get the request and its headers
  const request = event.Records[0].cf.request;
  const headers = request.headers;

  // Specify the username and password to be used
  const user = 'user';
  const pw = 'password';

  // Build a Basic Authentication string
  const authString = 'Basic ' + new Buffer(user + ':' + pw).toString('base64');

  // Challenge for auth if auth credentials are absent or incorrect
  if (typeof headers.authorization == 'undefined' || headers.authorization[0].value != authString) {
    const response = {
      status: '401',
      statusDescription: 'Unauthorized',
      body: 'Unauthorized',
      headers: {
        'www-authenticate': [{key: 'WWW-Authenticate', value:'Basic'}]
      },
    };
    callback(null, response);
  }

  // User has authenticated
  callback(null, request);
};

Dieser Code nimmt den HTTP Request des Benutzers und überprüft, ob die Informationen im Header mit dem angegebenen Benutzer und Passwort übereinstimmen. Wenn nicht, gibt es keinen Zugriff auf die Seite.

Nun müssen wir noch angeben, dass die Lambda Funktion von CloudFront aufgerufen wird – dazu fügen wir CloudFront als Trigger hinzu. Zuerst müssen Sie aber die Funktion mittels „Save“ Knopf speichern und mittels „Actions -> Publish“ veröffentlichen.

Klicken Sie dann auf „Add trigger“ und wählen Sie CloudFront aus. Wie geschrieben werden Sie CloudFront nur sehen, wenn die Region „US East (N. Virginia)“ ist (siehe anfang dieses Kapitels).

Klicken Sie auf „Deploy to Lambda@Edge“ und setzen Sie die folgenden Einstellungen:

  • Distribution: Hier sollte der Code ihrer CloudFront Distribution stehen. Sie können jederzeit in einem neuen Tab CloudFront aufrufen und den Code überprüfen.
  • CloudFront event: Wählen Sie hier „Viewer request
  • Setzen Sie einen Haken bei „I acknowledge that on deploy…
  • Klicken Sie unten auf „Deploy„.

Aufruf der Seite in einem Browser

Et voilà – Wir haben es geschafft!

Nun können Sie die URL der CloudFront Distribution in einen Browser eingeben und sollten mit einem Login begrüsst werden. Die URL können Sie in CloudFront einsehen, falls Sie sie nicht mehr wissen.

Im Lambda haben wir als Benutzer und Passwort „user“ und „password“ genannt. Dies sollten Sie natürlich noch ändern und die Lambda Funktion nochmals mit „Publish“ veröffentlichen.

Den S3 Bucket nicht mehr public halten

Der S3 Bucket ist immer noch public und momentan kann immer noch jeder mit der URL darauf zugreifen. Gehen Sie also nochmals in den „S3“ Service, dann in den Bucket, dann auf „Permissons“ und stellen Sie „Block all public access“ wieder ein.

Der Bucket muss nicht öffentlich zugänglich sein, da man ja nun über CloudFront und über Lambda auf den Bucket mittlels Benutzer und Passwort zugreift.

Wenn Sie nun die Bucket URL (Achtung: Nicht die CloudFront URL!) aufrufen, sollte eine „403 Forbidden“ Fehlermeldung erscheinen.

Wunderbar!

Was ist 2-Faktor Authentifizierung?

Beim Thema 2-Faktor Authentifizierung, geht es darum, wie ein Benutzer sich in einem System einloggen kann. Ob das dabei eine Webseite oder ein Server ist, ist unerheblich. Liest man im Internet zu dem Thema, wird grundsätzlich gesagt, dass 2-Faktor Authentifizierung gewährleistet ist, wenn der Benutzer etwas Bestimmtes weiss und etwas Bestimmtes besitzt.

Grundsätzlich gilt: Aus der folgenden Liste der Authentifizierungsklassen muss der Benutzer zwei von den drei Punkten besitzen/erfüllen/wissen, um 2-Faktor Authentifizierung zu gewährleisten.

Die drei Authentifizierungsklassen

1. Etwas was du weisst – Benutzername und Passwort
2. Etwas was du hast – Login-Karte, Zertifikat
3. Etwas was du bist – Fingerabdruck, Iris-Scan

Das heisst gleichzeitig, wenn ein Benutzer zwei Passwörter eingeben muss, ist das nicht 2-Faktor Authentifizierung.

Was ist Mutual SSL Authentication?

Mutual Authentication referenziert sich darauf, dass sich zwei Parteien gegenseitig authentifizieren. Es wird auch zertifikatbasiertes gegenseitiges Authentifizieren genannt. Die zwei Parteien verifizieren sich dabei anhand der ausgetauschten digitalen Zertifikaten, so dass jede Partei sicher sein kann, mit wem sie spricht.

Es geht also darum, dass sich ein Client (etwa ein Webbrowser) einem Server gegenüber authentifiziert. Der Server wiederum macht dasselbe beim Client. Der Server prüft dafür das Public Key Certificate/digitale Zertifikat, welches von einer zertifiziertern Stelle, der Certificate Authority (CA) ausgestellt wurde. Die Authentifizierung im Netz basiert auf Zertifikaten, weshalb Anbieter wie Verisign Microsoft Certificate Server ein wichtiger Teil der Prozesses sind. Grosse Firmen haben dabei üblicherweise ihre eigene CA.

Die benötigten Schritte

Grob betrachtet sind bei einer Mutual Authentication folgende Schritte durchzuführen:

1. Ein Client beantragt Zugang zu einer geschützten Ressource

2. Der Server präsentiert sein Zertifikat dem Client

3. Der Client verifiziert dieses Zertificate

4. Ist der Client zufrieden, sendet er sein Zertifikat dem Server

5. Der Server überprüft die Eigenschaften des Clients

6. Ist der Server zufrieden, gibt er dem Client Zugang zu der gewünschten Ressource

Der Unterschied zur SSL Authentifizierung

Mutual Authentication funktioniert ähnlich wie die SSL (Secure Socket Layer) Authentifizierung. Der Unterschied ist jedoch, dass noch die Client Authentifizierung mittels Zertifikaten dazukommt. Mutual Authentication wird deshalb auch Two way SSL authentication genannt.

Will man genau verstehen, bei einer SSL Authentifizierung geschieht, sollte man die Spezialitäten der Handshake Nachrichten kennen.

SSL Authentication Handshake

Bei der SSL Authentifizierung wird dem Client das Server Zertifikat angezeigt – Der Server authentifiziert sich also gegenüber dem Client. Der Client probiert dann die CA des Serverzertifikat in der Liste der CA’s zu finden, denen er vertraut (trusted CA’s). Während des ganzen Prozesses werden neun Nachrichten ausgetauscht.

1. Der Client sendet die Nachricht ClientHello mit einem Vorschlag von SSL Optionen

2. Der Server antwortet mit dem ServerHello und wählt darin die SSL Optionen

3. Der Server schickt sein Server Zertifikat

4. Der Server bestätigt seine Nachricht mit einem ServerHelloDone

5. Der Client schickt die Session Key Information in einer ClientKeyExchange Nachricht – Verschlüsselt mit dem Public Key des Servers, welche er aus dem Zertifikat entnommen hat

6. Der Client schickt die ChangeCipherSpec Nachricht um die gewählte Verschlüsselungsmethode zu bestätigen

7. Der Client schickt eine Finished Nachricht, damit der Server die aktivierten Optionen prüfen kann

8. Der Server schickt ebenfalls eine ChangeCipherSpec Nachricht, um die gewählte Verschlüsselungsmethode zu aktivieren.

9. Der Server schickt ebenfalls eine Finished Nachricht zum Abschluss

Mutual SSL Authentication

Bei der Mutual SSL Authentication authentifizieren sich der Server und der Client gegenseitig. Um einen verschlüsselten Kanal zu öffnen werden insgesamt 12 Nachrichten verwendet.

1. Der Client sendet die Nachricht ClientHello mit einem Vorschlag von SSL Optionen

2. Der Server antwortet mit dem ServerHello und wählt darin die SSL Optionen

3. Der Server schickt sein Server Zertifikat

4. Der Server verlangt das Zertifikat des Kunden in einem CertificateRequest, damit die Verbindung gegenseitig authentifiziert werden kann

5. Der Server bestätigt seine Nachricht mit einem ServerHelloDone

6. Der Client antwortet mit seinem Client Zertifikat

7. Der Client schickt die Session Key Information in einer ClientKeyExchange Nachricht – Verschlüsselt mit dem Public Key des Servers, welche er aus dem Zertifikat entnommen hat

8. Der Client schickt eine CertificateVerify Nachricht, um den Server wissen zu lassen, dass er der Besitzer des Zertifikats ist

9. Der Client schickt die ChangeCipherSpec Nachricht um die gewählte Verschlüsselungsmethode zu bestätigen

10. Der Client schickt eine Finished Nachricht, damit der Server die aktivierten Optionen prüfen kann

11. Der Server schickt ebenfalls eine ChangeCipherSpec Nachricht, um die gewählte Verschlüsselungsmethode zu aktivieren.

12. Der Server schickt ebenfalls eine Finished Nachricht zum Abschluss

Die Nachrichten mit Wireshark aufgenommen:

mutualauthenticationpic

Klick mich für das Bild in gross