Skip to content

Commit a87ee5f

Browse files
committed
fix(csrf): Prevent CSRF on other OTA examples
1 parent 450c640 commit a87ee5f

File tree

3 files changed

+51
-2
lines changed

3 files changed

+51
-2
lines changed

libraries/HTTPUpdateServer/src/HTTPUpdateServer.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ static const char serverIndex[] PROGMEM =
2727
</body>
2828
</html>)";
2929
static const char successResponse[] PROGMEM = "<META http-equiv=\"refresh\" content=\"15;URL=/\">Update Success! Rebooting...";
30+
static const char * csrfHeaders[2] = {"Origin", "Host"};
3031

3132
class HTTPUpdateServer {
3233
public:
@@ -56,6 +57,9 @@ class HTTPUpdateServer {
5657
_username = username;
5758
_password = password;
5859

60+
// collect headers for CSRF verification
61+
_server->collectHeaders(csrfHeaders, 2);
62+
5963
// handler for the /update form page
6064
_server->on(path.c_str(), HTTP_GET, [&]() {
6165
if (_username != emptyString && _password != emptyString && !_server->authenticate(_username.c_str(), _password.c_str())) {
@@ -69,6 +73,10 @@ class HTTPUpdateServer {
6973
path.c_str(), HTTP_POST,
7074
[&]() {
7175
if (!_authenticated) {
76+
if (_username == emptyString || _password == emptyString) {
77+
_server->send(200, F("text/html"), String(F("Update error: Wrong origin received!")));
78+
return;
79+
}
7280
return _server->requestAuthentication();
7381
}
7482
if (Update.hasError()) {
@@ -100,6 +108,17 @@ class HTTPUpdateServer {
100108
return;
101109
}
102110

111+
String origin = _server->header(String(csrfHeaders[0]));
112+
String host = _server->header(String(csrfHeaders[1]));
113+
String expectedOrigin = String("http://") + host;
114+
if (origin != expectedOrigin) {
115+
if (_serial_output) {
116+
Serial.printf("Wrong origin received! Expected: %s, Received: %s\n", expectedOrigin.c_str(), origin.c_str());
117+
}
118+
_authenticated = false;
119+
return;
120+
}
121+
103122
if (_serial_output) {
104123
Serial.printf("Update: %s\n", upload.filename.c_str());
105124
}

libraries/Update/examples/OTAWebUpdater/OTAWebUpdater.ino

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,16 @@
77

88
#define SSID_FORMAT "ESP32-%06lX" // 12 chars total
99
//#define PASSWORD "test123456" // generate if remarked
10+
const char * authUser = "admin";
11+
const char * authPass = "admin";
1012

1113
WebServer server(80);
1214
Ticker tkSecond;
1315
uint8_t otaDone = 0;
1416

17+
const char * csrfHeaders[2] = {"Origin", "Host"};
18+
static bool authenticated = false;
19+
1520
const char *alphanum = "0123456789!@#$%^&*abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1621
String generatePass(uint8_t str_len) {
1722
String buff;
@@ -38,13 +43,17 @@ void apMode() {
3843
}
3944

4045
void handleUpdateEnd() {
46+
if (!authenticated) {
47+
return server.requestAuthentication();
48+
}
4149
server.sendHeader("Connection", "close");
4250
if (Update.hasError()) {
4351
server.send(502, "text/plain", Update.errorString());
4452
} else {
4553
server.sendHeader("Refresh", "10");
4654
server.sendHeader("Location", "/");
4755
server.send(307);
56+
delay(500);
4857
ESP.restart();
4958
}
5059
}
@@ -56,18 +65,34 @@ void handleUpdate() {
5665
}
5766
HTTPUpload &upload = server.upload();
5867
if (upload.status == UPLOAD_FILE_START) {
68+
authenticated = server.authenticate(authUser, authPass);
69+
if (!authenticated) {
70+
Serial.println("Authentication fail!");
71+
otaDone = 0;
72+
return;
73+
}
74+
String origin = server.header(String(csrfHeaders[0]));
75+
String host = server.header(String(csrfHeaders[1]));
76+
String expectedOrigin = String("http://") + host;
77+
if (origin != expectedOrigin) {
78+
Serial.printf("Wrong origin received! Expected: %s, Received: %s\n", expectedOrigin.c_str(), origin.c_str());
79+
authenticated = false;
80+
otaDone = 0;
81+
return;
82+
}
83+
5984
Serial.printf("Receiving Update: %s, Size: %d\n", upload.filename.c_str(), fsize);
6085
if (!Update.begin(fsize)) {
6186
otaDone = 0;
6287
Update.printError(Serial);
6388
}
64-
} else if (upload.status == UPLOAD_FILE_WRITE) {
89+
} else if (authenticated && upload.status == UPLOAD_FILE_WRITE) {
6590
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
6691
Update.printError(Serial);
6792
} else {
6893
otaDone = 100 * Update.progress() / Update.size();
6994
}
70-
} else if (upload.status == UPLOAD_FILE_END) {
95+
} else if (authenticated && upload.status == UPLOAD_FILE_END) {
7196
if (Update.end(true)) {
7297
Serial.printf("Update Success: %u bytes\nRebooting...\n", upload.totalSize);
7398
} else {
@@ -78,6 +103,7 @@ void handleUpdate() {
78103
}
79104

80105
void webServerInit() {
106+
server.collectHeaders(csrfHeaders, 2);
81107
server.on(
82108
"/update", HTTP_POST,
83109
[]() {
@@ -92,6 +118,9 @@ void webServerInit() {
92118
server.send_P(200, "image/x-icon", favicon_ico_gz, favicon_ico_gz_len);
93119
});
94120
server.onNotFound([]() {
121+
if (!server.authenticate(authUser, authPass)) {
122+
return server.requestAuthentication();
123+
}
95124
server.send(200, "text/html", indexHtml);
96125
});
97126
server.begin();

libraries/WebServer/examples/WebUpdate/WebUpdate.ino

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ void setup(void) {
6767
if (origin != expectedOrigin) {
6868
Serial.printf("Wrong origin received! Expected: %s, Received: %s\n", expectedOrigin.c_str(), origin.c_str());
6969
authenticated = false;
70+
return;
7071
}
7172

7273
Serial.printf("Update: %s\n", upload.filename.c_str());

0 commit comments

Comments
 (0)