Skip to content

make HTTP Update Server more secure #2104

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jun 7, 2016
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
To upload through terminal you can use: curl -F "[email protected]" esp8266-webupdate.local/update
*/

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <ESP8266HTTPUpdateServer.h>

const char* host = "esp8266-webupdate";
const char* update_path = "/firmware";
const char* update_username = "admin";
const char* update_password = "admin";
const char* ssid = "........";
const char* password = "........";

ESP8266WebServer httpServer(80);
ESP8266HTTPUpdateServer httpUpdater;

void setup(void){

Serial.begin(115200);
Serial.println();
Serial.println("Booting Sketch...");
WiFi.mode(WIFI_AP_STA);
WiFi.begin(ssid, password);

while(WiFi.waitForConnectResult() != WL_CONNECTED){
WiFi.begin(ssid, password);
Serial.println("WiFi failed, retrying.");
}

MDNS.begin(host);

httpUpdater.setup(&httpServer, update_path, update_username, update_password);
httpServer.begin();

MDNS.addService("http", "tcp", 80);
Serial.printf("HTTPUpdateServer ready! Open http://%s.local%s in your browser and login with username '%s' and password '%s'\n", host, update_path, update_username, update_password);
}

void loop(void){
httpServer.handleClient();
delay(1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delay not needed here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah... was in the other example, so I left it there thinking might be needed. I'll remove it from both

}
37 changes: 25 additions & 12 deletions libraries/ESP8266HTTPUpdateServer/src/ESP8266HTTPUpdateServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@


const char* ESP8266HTTPUpdateServer::_serverIndex =
R"(<html><body><form method='POST' action='/update' enctype='multipart/form-data'>
R"(<html><body><form method='POST' action='' enctype='multipart/form-data'>
<input type='file' name='update'>
<input type='submit' value='Update'>
</form>
Expand All @@ -17,24 +17,29 @@ ESP8266HTTPUpdateServer::ESP8266HTTPUpdateServer(bool serial_debug)
{
_serial_output = serial_debug;
_server = NULL;
_username = NULL;
_password = NULL;
_authenticated = false;
}

void ESP8266HTTPUpdateServer::setup(ESP8266WebServer *server)
void ESP8266HTTPUpdateServer::setup(ESP8266WebServer *server, const char * path, const char * username, const char * password)
{
_server = server;
_username = (char *)username;
_password = (char *)password;

// handler for the /update form page
_server->on("/update", HTTP_GET, [&](){
_server->sendHeader("Connection", "close");
_server->sendHeader("Access-Control-Allow-Origin", "*");
_server->on(path, HTTP_GET, [&](){
if(_username != NULL && _password != NULL && !_server->authenticate(_username, _password))
return _server->requestAuthentication();
_server->send(200, "text/html", _serverIndex);
});

// handler for the /update form POST (once file upload finishes)
_server->on("/update", HTTP_POST, [&](){
_server->sendHeader("Connection", "close");
_server->sendHeader("Access-Control-Allow-Origin", "*");
_server->send(200, "text/html", (Update.hasError())?"FAIL":"<META http-equiv=\"refresh\" content=\"15;URL=/update\">OK");
_server->on(path, HTTP_POST, [&](){
if(!_authenticated)
return _server->requestAuthentication();
_server->send(200, "text/html", (Update.hasError())?"FAIL":"<META http-equiv=\"refresh\" content=\"15;URL=\">OK");
ESP.restart();
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@igrr you think maybe we should not reboot if Update failed?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll merge this as is, let's address this in a separate pull request. I think a good idea would be to add update callbacks similar to ESP8266HTTPUpdate (client). Actually maybe we need to review APIs of HTTP update client and HTTP update server and make them more uniform. Let's discuss further on gitter.

},[&](){
// handler for the file upload, get's the sketch bytes, and writes
Expand All @@ -43,27 +48,35 @@ void ESP8266HTTPUpdateServer::setup(ESP8266WebServer *server)
if(upload.status == UPLOAD_FILE_START){
if (_serial_output)
Serial.setDebugOutput(true);

_authenticated = (_username == NULL || _password == NULL || _server->authenticate(_username, _password));
if(!_authenticated){
if (_serial_output)
Serial.printf("Unauthenticated Update\n");
return;
}

WiFiUDP::stopAll();
if (_serial_output)
Serial.printf("Update: %s\n", upload.filename.c_str());
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
if(!Update.begin(maxSketchSpace)){//start with max available size
if (_serial_output) Update.printError(Serial);
}
} else if(upload.status == UPLOAD_FILE_WRITE){
} else if(_authenticated && upload.status == UPLOAD_FILE_WRITE){
if (_serial_output) Serial.printf(".");
if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){
if (_serial_output) Update.printError(Serial);

}
} else if(upload.status == UPLOAD_FILE_END){
} else if(_authenticated && upload.status == UPLOAD_FILE_END){
if(Update.end(true)){ //true to set the size to the current progress
if (_serial_output) Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
} else {
if (_serial_output) Update.printError(Serial);
}
if (_serial_output) Serial.setDebugOutput(false);
} else if(upload.status == UPLOAD_FILE_ABORTED){
} else if(_authenticated && upload.status == UPLOAD_FILE_ABORTED){
Update.end();
if (_serial_output) Serial.println("Update was aborted");
}
Expand Down
21 changes: 20 additions & 1 deletion libraries/ESP8266HTTPUpdateServer/src/ESP8266HTTPUpdateServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,28 @@ class ESP8266HTTPUpdateServer
bool _serial_output;
ESP8266WebServer *_server;
static const char *_serverIndex;
char * _username;
char * _password;
bool _authenticated;
public:
ESP8266HTTPUpdateServer(bool serial_debug=false);
void setup(ESP8266WebServer *server=NULL);

void setup(ESP8266WebServer *server)
{
setup(server, NULL, NULL);
}

void setup(ESP8266WebServer *server, const char * path)
{
setup(server, path, NULL, NULL);
}

void setup(ESP8266WebServer *server, const char * username, const char * password)
{
setup(server, "/update", username, password);
}

void setup(ESP8266WebServer *server, const char * path, const char * username, const char * password);
};


Expand Down