Im Internet spielt die Authentifizierung eine zentrale Rolle. Um sicherzustellen, dass nur autorisierte Benutzer Zugriff auf bestimmte Ressourcen haben, werden diese durch das Abfragen von Anmeldedaten abgesichert. Diese scheinbar einfache, aber dennoch effektive Sicherheitsmaßnahme ist auch auf einem ESP8266 Webserver anwendbar. Durch das Implementieren eines Logins können Daten vor unbefugtem Zugriff geschützt werden.
Zum Kapitel springen Vergleich mit klassischen Webservern
Bei traditionellen Webservern wie Apache oder NGINX kann eine Authentifizierung unter anderem über einfache .htaccess
-Dateien geregelt werden. Diese Dateien definieren Zugangsbeschränkungen und fordern vom Benutzer eine Anmeldung, bevor er auf geschützte Inhalte zugreifen kann. Hierbei kommt die Basis-Authentifizierung (Basic HTTP Authentication) zum Einsatz, wobei die Anmeldedaten in einem bestimmten Verzeichnis des Servers gespeichert sind und durch die .htaccess-Datei gesteuert werden.
Auf einem ESP8266 Webserver, der aufgrund seiner Ressourcen erheblich eingeschränkter ist als ein vollwertiger Webserver, gibt es keine .htaccess-Dateien im herkömmlichen Sinne. Stattdessen kann die Authentifizierung schnell und einfach direkt im Code implementiert werden, sodass die Funktionalität ähnlich bleibt: Der Benutzer wird zur Eingabe von Benutzernamen und Passwort aufgefordert, bevor er auf geschützte Inhalte zugreifen kann.
Produktempfehlungen und -suche in Verbindung mit dem Amazon Partnerprogramm:
¹ Angaben ohne Gewähr. Bei einem Kauf über den Link erhalten wir eine Provision.
Trotz der Unterschiede in der Infrastruktur kann eine ähnliche, einfache und effektive Authentifizierungsebene direkt auf dem ESP8266-Webserver aufgesetzt werden.
Zum Kapitel springen Implementierung der HTTP-Authentifizierung auf dem ESP8266
Für dieses Beispiel greife ich auf das Code-Snippet aus dem Tutorial ESP8266 Webserver mit echten HTML Dateien. zurück. Es ist wichtig, dass du diesen Code verstehst und auf den ESP8266 hochlädst, bevor du die folgenden Code-Snippets einbindest.
Produktempfehlungen und -suche in Verbindung mit dem Amazon Partnerprogramm:
¹ Angaben ohne Gewähr. Bei einem Kauf über den Link erhalten wir eine Provision.
Um die Basis-Authentifizierung auf einem ESP8266 Webserver zu implementieren, erstellen wir eine Funktion, die bei jeder Anfrage die Anmeldedaten überprüft.
Zunächst definieren wir dafür einen Benutzernamen und ein Passwort, die optional durch die authenticate-Methode der AsyncWebServerRequest-Klasse verifiziert werden können.
Diese Methode wird also nicht automatisch ausgeführt, sondern muss explizit in den gewünschten Routen aufgerufen werden. Falls die Authentifizierung fehlschlägt, gibt die Methode false zurück und verhindert das Anzeigen der Seite.
const char* http_username = "admin";
const char* http_password = "1234";
bool handleAuthentication(AsyncWebServerRequest *request) {
if (!request->authenticate(http_username, http_password)) {
request->requestAuthentication();
return false;
}
return true;
}
Der Aufruf der Authentifizierungsfunktion kann nun in nur einer Zeile erfolgen:
if (!handleAuthentication(request)) return;
Diese Zeile kann in jeder Route eingefügt werden, die abgesichert werden soll. Dadurch wird die Authentifizierung gleich zu Beginn der Routenverarbeitung geprüft. In diesem Beispiel sichern wir alle Routen des Servers ab, sowohl die Server-Routen als auch die Datei-Routen.
Die Startseite mit der vorgeschalteten Authentifizierungsprüfung sieht dann wie folgt aus:
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
// Hier findet die Prüfung der vorher festgelegten Zugangsdaten statt
if (!handleAuthentication(request)) return;
request->send(LittleFS, "/index.html", "text/html");
});
Beim ersten Besuch des Webservers und der geschützten Route werden die Anmeldedaten abgefragt.
Benutzername: admin
Passwort: 1234
Zum Kapitel springen Vollständiger Beispielcode
//
// Einfacher Webserver der Dateien aus dem LittleFS (Filesystem) ausliefert und mit einem Login abgesichert ist
// cooper.bin @ makesmart.net
// mehr Infos unter: https://makesmart.net/blog/read/esp8266-webserver-absichern-mit-basic-http-auth
//
void setupServerRoutes();
void setupFilePaths();
// WLAN
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
// Webserver
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
// Filesystem
#include <LittleFS.h>
// Standard HTTP-Port 80
AsyncWebServer server(80);
// WLAN-Zugangsdaten
const char* ssid = "ssid";
const char* password = "password";
// Login-Zugangsdaten für die HTTP-Authentifizierung
const char* http_username = "admin";
const char* http_password = "1234";
// Hostname des ESPs -> http://makesmartesp.local/
const char* espHostname = "makesmartesp";
// Was passiert, wenn eine Seite nicht gefunden werden kann
// Es wird die Datei `404.html` ausgeliefert
void notFound(AsyncWebServerRequest *request) {
AsyncWebServerResponse* response =
request -> beginResponse(LittleFS, "/404.html", "text/html");
response -> setCode(404);
request -> send(response);
}
// Funktion zum Prüfen der Zugangsdaten für die HTTP-Authentifizierung
bool handleAuthentication(AsyncWebServerRequest *request) {
if (!request->authenticate(http_username, http_password)) {
request->requestAuthentication();
return false;
}
return true;
}
void setup() {
delay(500);
Serial.begin(115200);
Serial.println();
delay(500);
// Dateisystem initialisieren
if(LittleFS.begin()){
Serial.println("Dateisystem: Initialisiert");
}else{
Serial.println("Dateisystem: Fehler beim Initialisieren");
}
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("WLAN Verbindung fehlgeschlagen");
return;
}
Serial.print("Verbunden! IP-Adresse: "); Serial.println(WiFi.localIP());
// Starten des mDNS-Servers
if ( !MDNS.begin(espHostname) ) {
Serial.println("Fehler beim Staren des mDNS-Servers!");
}
// Server Routen werden in einer eigenen Funktion definiert
setupServerRoutes();
// Datei Routen werden in einer eigenen Funktion definiert
setupFilePaths();
server.begin();
}
void loop() {
MDNS.update();
}
void setupServerRoutes(){
// Als Document-Root gilt der Ordner `data`;
// siehe: https://makesmart.net/blog/read/dateien-auf-dem-esp8266-speichern-und-lesen-das-spiffs-dateisystem
// Alle Pfade zu Dateien müssen relativ vom Ordner "data" definiert werden
// Datei `index.html` wird ausgeliefert
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
// Hier findet die Prüfung der vorher festgelegten Zugangsdaten statt
if (!handleAuthentication(request)) return;
request->send(LittleFS, "/index.html");
});
server.onNotFound(notFound);
}
void setupFilePaths(){
// In `server.on` wird die Webserver-Route definiert
// in der Logik wird dann die CSS Datei aus dem LittleFS ausgeliefert: `assets/css/style.css`
server.on("/assets/css/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
if (!handleAuthentication(request)) return;
request->send(LittleFS, "/assets/css/style.css", "text/css");
});
// Gleiches mit einer Javascript-Datei
server.on("/assets/js/script.js", HTTP_GET, [](AsyncWebServerRequest *request){
if (!handleAuthentication(request)) return;
request->send(LittleFS, "/assets/js/script.js", "application/javascript");
});
// Gleiches mit einem PNG-Bild
server.on("/assets/images/favicon-96.png", HTTP_GET, [](AsyncWebServerRequest *request){
if (!handleAuthentication(request)) return;
request->send(LittleFS, "/assets/images/favicon-96.png", "image/png");
});
}
Zum Kapitel springen Wo wird der Login gespeichert?
Die HTTP-Basis-Authentifizierung speichert den Login-Zustand nicht direkt in einer Session oder einem Cookie. Stattdessen sendet der Browser bei jeder Anfrage die Anmeldedaten, also Benutzername und Passwort, erneut als Teil des HTTP-Headers. Diese Anmeldedaten bleiben in der Regel während der gesamten Sitzung des Benutzers im Browser zwischengespeichert, sodass der Benutzer nicht bei jeder Anfrage erneut zur Eingabe aufgefordert wird. Die "Sitzung" in diesem Fall hängt also vom Browser ab.
Solange der Browser geöffnet bleibt und die Anmeldedaten zwischengespeichert sind, bleibt der Benutzer angemeldet. Wenn der Browser geschlossen oder die zwischengespeicherten Daten gelöscht werden, muss der Benutzer die Anmeldedaten erneut eingeben.
Zum Kapitel springen Sicherheit der HTTP-Basis-Authentifizierung
Die HTTP-Basis-Authentifizierung ist einfach, aber nicht besonders sicher, da die Anmeldedaten bei jeder Anfrage als Klartext (Base64-kodiert) über das Netzwerk gesendet werden. Dies macht sie anfällig für Angriffe, insbesondere in ungesicherten HTTP Verbindungen.
Sicherheitstipps:
- Verwende HTTPS: Durch die Verwendung von HTTPS wird die gesamte Kommunikation, einschließlich der Anmeldedaten, verschlüsselt und schützt vor Abhörangriffen.
- Beschränke den Zugriff: Stelle sicher, dass der Webserver nur für autorisierte IP-Adressen oder über ein gesichertes Netzwerk zugänglich ist.
- Schütze den Quellcode: Da die Anmeldedaten im Quellcode hinterlegt sind, ist es wichtig, den Code vor unberechtigtem Zugriff zu schützen.
Unter der Annahme, dass der Quellcode sicher ist und HTTPS verwendet wird, kann die HTTP-Basis-Authentifizierung für viele einfache Anwendungen als ausreichend sicher betrachtet werden. Für Anwendungen, die ein höheres Sicherheitsniveau erfordern, sollte jedoch eine stärkere Authentifizierungsmethode in Betracht gezogen werden.
Die Implementierung einer Basic HTTP Auth auf dem ESP8266 ist eine praktische Methode, um den Zugriff auf deinen Webserver abzusichern. Während diese Methode einfach zu implementieren ist, sollten ihre Sicherheitsbeschränkungen berücksichtigt werden, insbesondere in ungesicherten Netzwerken. Durch den Einsatz von HTTPS und weiteren Maßnahmen kann jedoch ein angemessenes Sicherheitsniveau erreicht werden.