Formulardaten an ESP per JSON senden und char-arrays zuweisen

ja, hast Recht. Evtl. stufe ich das zu wichtig ein und man kann darauf verzichten.

iframes sollten ja auch in künftigen Browser-Versionen kein Problem darstellen, oder was meinst du?

Aber hast du noch einen Tipp (sorry, das ich dich grad so beanspruche :wink: wir man die übermittelten Daten am smartesten im SPIFFS speichert um diese später wieder auslesen zu können?

Mit dem was ich aktuell mache, also mit dem * als Trennzeichen, bin ich nicht wirklich sicher unterwegs. Aber warte: während ich diese Zeilen schreibe kommt mir glaube ich selber eine Lösung: wäre ein struct der beste Weg? Und diesen dann in eine (binäre) Datei schreiben?

Der Onkel hier sagt:

naja du machst jetzt einen Screenshot von einer Seite die sich mit CORS und SOP beschäftigt.
Nichts davon ist unmittelbar relevant für deinen Usecase.

Ich sag nicht das iframes schön sind, aber sie sind meines Wissens in HTML5 nicht abgekündigt.
Es dient lediglich als target.

SPIFFS ist auf einigen ESPs deprecated. Ich weis aber nicht auf welchem du bist.

Zum persistieren von Daten würde ich am ESP32 (???) preferences nehmen und die Daten in einem struct halten.
am ESP8266 halt in der EEPROM emulation - aber wieder in einem struct.

Ein über einen Browser gesendetes JSON würde ich nicht 1:1 ungeprüft in in ein File übernehmen oder verarbeiten wollen.

1 Like

Yep :wink:

Ich will dich nicht als Google verwenden, aber lauf ich mit ESP32 und SPIFFS nicht zukunftssicher? Ist die Alternative dazu dann LittleFS?

Was meinst du mit "preferences" nehmen? Im Struct halten kapier ich :wink:

Inwiefern prüfen? Welche Gefahr lauert? Sorry, ich hoffe die Frage ist nicht allzu dumm.
Aber wenn ich im HTML5 Formular die Form-Daten validiere (bzw. per Javascript) und dann per JSON sende, sollte doch hinten schon auch alles unbeschädigt ankommen, oder?
Du machst mir Angst :face_with_raised_eyebrow:

Dass man nach Analyse Deines Formulars problemlos beliebige Daten senden kann und Du das nicht verhindern kannst.
Beliebtes Beispiel: SQL-Injection (bei Dir wohl nicht zutreffend). Bei falschen Rechten mal ins Passwortfeld eingeben: aaa; Drop Database;

Gruß Tommy

Da hast Recht, SQL-Injection spielt im Web-Bereich definitiv ne Rolle.
Aber das Problem hab ich doch auch mit einem reinen HTML Formular (POST method) ohne ajax.
Das ist ja ein grundsätzliches Problem, oder?

Meiner Meinung nach: Zweimal ja.
Habe gelesen, dass ab ESP32 Core 2.0 LittleFS integriert sein wird (Link habe ich zu Hause).

Gut zu wissen, Danke.
Und da auch mit Core 1 LittleFS schon unterstützt wird, wäre es definitiv gut, gleich umzusteigen.
Ich hoff mal, die Unterschiede zwischen LittleFS und SPIFFS sind nicht allzu groß (von der Syntax). Hab mich bisher nur mit SPIFFS beschäftigt.

Ja. Deshalb schrieb ja @noiasca auch, dass er Daten aus einem Browser nicht ungeprüft 1:1 übernehmen würde, was Dich in Staunen versetzte.

Gruß Tommy

Naja, wenn du genau liest hat er geschrieben:

Und da die vorangeganene Diskussion ja darum ging, welche Übertragungsmethode am besten wäre, hab ich in seinem Kommentar schon das Wort JSON als hervorgehoben gesehen.
Aber ist ja völlig egal: ein HTML-Formular bietet immer Angriffspotential und sollte (wo erforderlich) natürlich auch entsprechend geprüft werden.

Preferences ist die Ablöse der EEPROM Emulation für den ESP32.

https://www.google.com/search?q=esp32+preferences+vs+eeprom

ich weis nicht in welchem deiner x Threads das war, aber wenn das von mir war dann habe ich deine Fragestellung nach der Übertragung um "Daten vom Server an den Client senden" aufgefasst. Das würde ich durchaus mit JSON machen.

Aber für ein paar Daten vom Client zum Server schicken, würde ich Parameter nehmen.

OK Danke dir @noiasca .
Wieder mal viel input. Auch die Preferences sind mir bisher unbekannt gewesen.
Muss ich mich dringend mit beschäftigen.

Bis zum heutigen Tage habe ich z.B. ssid, passwort und andere Einstelldaten in einer txt im SPIFFS abgelegt und diese SPIFFS beim Starten des ESP ausgelesen. Wieder was dazu gelernt.

Nabend,

ich hab jetzt mal versucht, alles umzusetzen, was mir hier genannt wurde.

  1. Ich sende die Daten nun mit einem "purem" HTML POST
  2. Ich speichere die Daten im eeprom (preferences)
  3. Ich lade die Seite direkt nach dem Senden der Formulardaten einfach neu. So bleib ich auf der Seite und brauche auch keinen iframe. Warum ich da niocht gleich drauf gekommen bin...?
  4. Ich verschlüssle die Daten vor dem Speichern auch gleich noch

Hier mein fertiger Testsketch:

#include <WiFi.h>
#include <WiFiAP.h>
#include <WebServer.h>
WebServer server(80);
#include <Preferences.h>
Preferences prefs;

// Zum Verschlüsseln...
#include "mbedtls/aes.h"
#include "Cipher.h"
char key[17] = "weogrbasztvszvas";

const char datei_wlan[12] = "/wlan.txt";

char APssid[25] = "MyAccesspoint";
char APpassword[20] = "123456789";

char ssid[30]="";
char password[30]="";

const char indexPage[] PROGMEM = R"=====(
  <!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
    <meta http-equiv="Pragma" content="no-cache">
    <meta http-equiv="Expires" content="0">
  </head>
    <body>
      <form action="/setData" method="POST" onsubmit="return setData()">
        <p>SSID:</p>
        <input type="text" name="ssid" id="ssid" value="">
        <p>Passwort:</p>
        <input type="password" name="password" id="password" value="">
        <button type="submit">Submit</button>
      </form>
    </body>
    <script>
      function setData() {
        // Daten ggf. noch prüfen...hier mal nur zum Test was dranhängen
        var ssid=document.getElementById("ssid").value;
        var password=document.getElementById("password").value;
        document.getElementById("ssid").value=ssid + " XYZ";
        // Und dann das Formular senden
        return true;
      } 
    </script>
  </html>
)=====";

void setup(){
  Serial.begin(115200);

  // Verschlüsselung initiieren
  Cipher * cipher = new Cipher();
  cipher->setKey(key);

  // Kurze Pause nur zu Testzwecken (Serielle Ausgabe soll lesbar sein)
  delay(2000);
  // Zugangsdaten laden
  prefs.begin("zugangsdaten", false);

  // Preferences auslesen und decoden
  String ssid_encoded = cipher->decryptString(prefs.getString("ssid", ""));
  String password_encoded = cipher->decryptString(prefs.getString("password", ""));
  
  // Entschlüsselten Daten in char-arrays schreiben
  ssid_encoded.toCharArray(ssid, sizeof(ssid));
  password_encoded.toCharArray(password, sizeof(password));
  
  prefs.end();
  Serial.println("SSID entschlüsselt beim Starten: " + (String)ssid + " Passwort entschlüsselt beim Starten:" + (String)password);
  
  server.on("/", display_root);
  server.on("/setData", setData);

  WiFi.mode(WIFI_AP);
  WiFi.softAP(APssid, APpassword);
  server.begin();
}

void loop(){
  server.handleClient();
}

void display_root() {
  const char * httpType PROGMEM = "text/html";
  server.send_P(200,  httpType, indexPage);
}

void setData() {

  // Verschlüsselung initiieren
  Cipher * cipher = new Cipher();
  cipher->setKey(key);
  
  prefs.begin("zugangsdaten", false);
  if (server.method() != HTTP_POST) {
    Serial.println("Fehler! Formular konnte nicht gesendet werden.");
    server.send(200, "text/plain", "Fehler");
  } else {
    for (uint8_t i = 0; i < server.args(); i++) {
      if (server.argName(i)=="ssid") {
        prefs.putString("ssid", cipher->encryptString(server.arg(i)));
        Serial.println("Kodierte SSID:" + prefs.getString("ssid", ""));
        server.arg(i).toCharArray(ssid, sizeof(ssid));
      } else if (server.argName(i)=="password") {
        prefs.putString("password", cipher->encryptString(server.arg(i)));
        Serial.println("Kodiertes Passwort:" + prefs.getString("password", ""));
        server.arg(i).toCharArray(password, sizeof(password));
      }
    }
    prefs.end();
    
    Serial.println("SSID nach dem Speichern: " + (String)ssid + " Passwort nach dem Speichern:" + (String)password);
    // Seite neu laden...
    const char * httpType PROGMEM = "text/html";
    server.send_P(200,  httpType, indexPage);
  }
}

Über Anmerkungen / Verbesserungsvorschläge freue ich mich :wink:

Dazu gleich noch ne Frage:
Wieviel Platz ist denn inden Preferences? Wenn ich da ca. 30 key/value Paare speicher, sollte das doch kein Problem sein, gelle?

Viele Grüße

Wofür soll das gut sein?
Wo ist die Prüfung der Daten?

Gruß Tommy

Hier :wink:

In dem Test hab ich die Überprüfung der Daten weggelassen (daher der Kommentar).
Sollte nur ein Beispiel sein, an welcher Stelle man vor dem Senden des Formulars noch ran könnte. Einen Teil der Überprüfung werd ich sicherlich dann genau an dieser Stelle machen, einen Teil auch schon auf HTML5 Ebene (z.B. über die Typisierung von Feldern, min-max-Grenzen. Kommt eben drauf an, welchen Anwendungsfall man hat.

Schutz davor, dass ein böser Onkel die Daten auslesen könnte. Oder ist das übertrieben?
Keine Ahnung, kann man natürlich auch weglassen.

Edit:
Fips macht das zum Beispiel bei seinem Login-Manager auch

Allerdings macht er es auf eine andere Art und Weise, hier ein Auszug:

void handleWifiSave() {
  if (server.hasArg("ssid") && server.hasArg("passwort")) {
    strcpy(ssid, server.arg(0).c_str());
    strcpy(password, server.arg(1).c_str());
    for (auto &c : ssid) c ^= key;                      // Chiffrierung SSID
    for (auto &c : password) c ^= key;                  // Chiffrierung Passwort
    File file = LittleFS.open("/wifi.dat", "w");
    file.write(reinterpret_cast<uint8_t*>(&ssid), sizeof(ssid));
    file.write(reinterpret_cast<uint8_t*>(&password), sizeof(password));
    file.close();
    server.send(200, "application/json", JSON);
    delay(500);
    connectWifi();
  }
}

Mensch Tommy, lob mich halt auch mal :wink:

Grüßle und guten Start in den Tag euch

Für die Zugangsdaten ok, aber Du schriebst von 30 Wertepaaren. Für andere Daten halte ich eine Verschlüsselung nicht unbedingt für notwendig.

Gruß Tommy

Ah ok. Da hatte ich mich falsch ausgedrückt sorry.
Aber mal so in den Raum gefragt: Wieviel Wertepaare sind denn in den Preferences möglich?
Laut meiner Info stehen 20KB zur Verfügung. Das dürfte doch für hunderte reichen, oder?

Das kommt auf die Länge der Keys und der Werte an :wink:
Ansonsten kann ich dazu nichts sagen, ich nutze keine ESP32.

Gruß Tommy

1 Like

Achtung: Die Länge der Keys ist beschränkt. Den genauen Wert weiß nicht auswendig, so max. 16 glaube ich zu erinnern.

1 Like

Ok. Ich kuck mal ob Google was weiß