From 4de83916d1800da6a3ca18f5fe4813bba7ce4d8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xose=20P=C3=A9rez?= Date: Sun, 13 Nov 2016 23:38:03 +0100 Subject: [PATCH] Added basic authetication and CSRF check to websocket requests --- code/html/custom.js | 14 ++++++++++---- code/src/defaults.h | 15 ++++++++++++--- code/src/web.ino | 39 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/code/html/custom.js b/code/html/custom.js index 828c26dc..581a192e 100644 --- a/code/html/custom.js +++ b/code/html/custom.js @@ -1,8 +1,9 @@ var websock; +var csrf; function doUpdate() { var data = $("#formSave").serializeArray(); - websock.send(JSON.stringify({'config': data})); + websock.send(JSON.stringify({'csrf': csrf, 'config': data})); $(".powExpected").val(0); return false; } @@ -10,19 +11,19 @@ function doUpdate() { function doReset() { var response = window.confirm("Are you sure you want to reset the device?"); if (response == false) return false; - websock.send(JSON.stringify({'action': 'reset'})); + websock.send(JSON.stringify({'csrf': csrf, 'action': 'reset'})); return false; } function doReconnect() { var response = window.confirm("Are you sure you want to disconnect from the current WIFI network?"); if (response == false) return false; - websock.send(JSON.stringify({'action': 'reconnect'})); + websock.send(JSON.stringify({'csrf': csrf, 'action': 'reconnect'})); return false; } function doToggle(element, value) { - websock.send(JSON.stringify({'action': value ? 'on' : 'off'})); + websock.send(JSON.stringify({'csrf': csrf, 'action': value ? 'on' : 'off'})); return false; } @@ -40,6 +41,11 @@ function toggleMenu() { function processData(data) { + // CSRF + if ("csrf" in data) { + csrf = data.csrf; + } + // messages if ("message" in data) { window.alert(data.message); diff --git a/code/src/defaults.h b/code/src/defaults.h index bb99d593..fd1ede80 100644 --- a/code/src/defaults.h +++ b/code/src/defaults.h @@ -68,13 +68,22 @@ // ----------------------------------------------------------------------------- -// WIFI +// WIFI & WEB // ----------------------------------------------------------------------------- #define WIFI_RECONNECT_INTERVAL 300000 #define WIFI_MAX_NETWORKS 3 -#define AP_PASS "fibonacci" -#define OTA_PASS "fibonacci" +#define ADMIN_PASS "fibonacci" +#define AP_PASS ADMIN_PASS +#define HTTP_USERNAME "admin" +#define HTTP_PASSWORD ADMIN_PASS +#define CSRF_BUFFER_SIZE 5 + +// ----------------------------------------------------------------------------- +// OTA & NOFUSS +// ----------------------------------------------------------------------------- + +#define OTA_PASS ADMIN_PASS #define OTA_PORT 8266 #define NOFUSS_SERVER "http://192.168.1.100" #define NOFUSS_INTERVAL 3600000 diff --git a/code/src/web.ino b/code/src/web.ino index da532745..958b2afa 100644 --- a/code/src/web.ino +++ b/code/src/web.ino @@ -10,14 +10,16 @@ Copyright (C) 2016 by Xose PĂ©rez #include #include #include -#include "FS.h" +#include #include -#include "AsyncJson.h" +#include #include AsyncWebServer server(80); AsyncWebSocket ws("/ws"); +unsigned long _csrf[CSRF_BUFFER_SIZE]; + // ----------------------------------------------------------------------------- // WEBSOCKETS // ----------------------------------------------------------------------------- @@ -43,6 +45,16 @@ void webSocketParse(uint32_t client_id, uint8_t * payload, size_t length) { return; } + // CSRF + unsigned long csrf = 0; + if (root.containsKey("csrf")) csrf = root["csrf"]; + if (csrf != _csrf[client_id % CSRF_BUFFER_SIZE]) { + DEBUG_MSG("[WEBSOCKET] CSRF check failed\n"); + ws.text(client_id, "{\"message\": \"Session expired, please reload page...\"}"); + return; + } + + // Check actions if (root.containsKey("action")) { @@ -144,6 +156,12 @@ void webSocketStart(uint32_t client_id) { DynamicJsonBuffer jsonBuffer; JsonObject& root = jsonBuffer.createObject(); + // CSRF + if (client_id < CSRF_BUFFER_SIZE) { + _csrf[client_id] = random(0x7fffffff); + } + root["csrf"] = _csrf[client_id % CSRF_BUFFER_SIZE]; + root["app"] = app; root["manufacturer"] = String(MANUFACTURER); root["chipid"] = chipid; @@ -222,14 +240,29 @@ void webSocketEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsE // WEBSERVER // ----------------------------------------------------------------------------- +void onHome(AsyncWebServerRequest *request){ + String password = getSetting("httpPassword", HTTP_PASSWORD); + char httpPassword[password.length() + 1]; + password.toCharArray(httpPassword, password.length() + 1); + Serial.println(httpPassword); + if (!request->authenticate(HTTP_USERNAME, httpPassword)) { + return request->requestAuthentication(); + } + request->send(SPIFFS, "/index.html"); +} + void webSetup() { // Setup websocket plugin ws.onEvent(webSocketEvent); server.addHandler(&ws); + // Serve home + server.on("/", HTTP_GET, onHome); + server.on("/index.html", HTTP_GET, onHome); + // Serve static files - server.serveStatic("/", SPIFFS, "/").setDefaultFile("/index.html"); + server.serveStatic("/", SPIFFS, "/"); // 404 server.onNotFound([](AsyncWebServerRequest *request){