Common stuff for all my devices' web server pages: css, login, log, ipv4, ipv6, firmware update, clock, reset info etc.

Dependents:   oldheating gps motorhome heating

Security

A password has to be set whenever there has been a software reset. Resets following faults or power on do not require a new password as the hash is restored from the RTC GPREG register.

The password is not saved on the device; instead a 32 bit hash of the password is saved. It would take 2^31 attempts to brute force the password: this could be done in under a month if an attempt were possible every millisecond. To prevent this a 200 ms delay is introduced in the reply to the login form, that gives a more reasonable 13 years to brute force the password.

Once the password is accepted a random session id is created. This is 36 bit to give six base 64 characters but without an extra delay. If an attempt could be made every ms then this would still take over a year to brute force.

The most likely attack would to use a dictionary with, say, 10 million entries against the password which would still take 20 days to do.

Committer:
andrewboyson
Date:
Mon Apr 29 14:45:30 2019 +0000
Revision:
109:3e82f62c7e1f
Child:
110:8ab752842d25
Tidied names from http to web

Who changed what in which revision?

UserRevisionLine numberNew contents of line
andrewboyson 109:3e82f62c7e1f 1 #include "http.h"
andrewboyson 109:3e82f62c7e1f 2 #include "web-server-base.h"
andrewboyson 109:3e82f62c7e1f 3 #include "web-base.h"
andrewboyson 109:3e82f62c7e1f 4 #include "mstimer.h"
andrewboyson 109:3e82f62c7e1f 5
andrewboyson 109:3e82f62c7e1f 6 #define LOGIN_DELAY_MS 200
andrewboyson 109:3e82f62c7e1f 7
andrewboyson 109:3e82f62c7e1f 8 static void handleRequest(int size, char* pRequestStream, uint32_t positionInRequestStream, int* pToDo, bool* pPostComplete, uint32_t* pDelayUntil)
andrewboyson 109:3e82f62c7e1f 9 {
andrewboyson 109:3e82f62c7e1f 10 //Handle request for the first packet of data received but leave todo the same after that.
andrewboyson 109:3e82f62c7e1f 11 int contentLength = 0;
andrewboyson 109:3e82f62c7e1f 12 int contentStart = 0;
andrewboyson 109:3e82f62c7e1f 13 if (size && positionInRequestStream == 0)
andrewboyson 109:3e82f62c7e1f 14 {
andrewboyson 109:3e82f62c7e1f 15 //Read the headers
andrewboyson 109:3e82f62c7e1f 16 char* pMethod;
andrewboyson 109:3e82f62c7e1f 17 char* pPath;
andrewboyson 109:3e82f62c7e1f 18 char* pQuery;
andrewboyson 109:3e82f62c7e1f 19 char* pLastModified;
andrewboyson 109:3e82f62c7e1f 20 char* pCookies;
andrewboyson 109:3e82f62c7e1f 21 contentStart = HttpRequestRead(pRequestStream, size, &pMethod, &pPath, &pQuery, &pLastModified, &pCookies, &contentLength);
andrewboyson 109:3e82f62c7e1f 22
andrewboyson 109:3e82f62c7e1f 23 //Ask the web server what to do
andrewboyson 109:3e82f62c7e1f 24 *pToDo = WebServerBaseDecideWhatToDo(pPath, pLastModified);
andrewboyson 109:3e82f62c7e1f 25
andrewboyson 109:3e82f62c7e1f 26 //If what to do is NOTHING, NOT_FOUND or NOT_MODIFIED then no query or post will be valid so stop now
andrewboyson 109:3e82f62c7e1f 27 if (*pToDo < DO_LOGIN) { *pPostComplete = true; return; }
andrewboyson 109:3e82f62c7e1f 28
andrewboyson 109:3e82f62c7e1f 29 //If what to do is LOGIN then the user has just returned the login form
andrewboyson 109:3e82f62c7e1f 30 if (*pToDo == DO_LOGIN)
andrewboyson 109:3e82f62c7e1f 31 {
andrewboyson 109:3e82f62c7e1f 32 WebServerBaseHandleQuery(*pToDo, pQuery); //Read the password and the original location
andrewboyson 109:3e82f62c7e1f 33 if (WebLoginQueryPasswordOk)
andrewboyson 109:3e82f62c7e1f 34 {
andrewboyson 109:3e82f62c7e1f 35 if (!WebLoginSessionIdIsSet()) //If there isn't a session id already
andrewboyson 109:3e82f62c7e1f 36 {
andrewboyson 109:3e82f62c7e1f 37 WebLoginSessionIdNew(); //Create a new session id
andrewboyson 109:3e82f62c7e1f 38 }
andrewboyson 109:3e82f62c7e1f 39 *pToDo = WebLoginOriginalToDo; //Load the original todo and SEND_SESSION_ID
andrewboyson 109:3e82f62c7e1f 40 *pToDo += DO_SEND_SESSION_ID;
andrewboyson 109:3e82f62c7e1f 41 }
andrewboyson 109:3e82f62c7e1f 42 *pDelayUntil = MsTimerCount + LOGIN_DELAY_MS; //To prevent brute forcing the hash delay the reply to the login
andrewboyson 109:3e82f62c7e1f 43 *pPostComplete = true;
andrewboyson 109:3e82f62c7e1f 44 return; //Either way no query or post will be valid
andrewboyson 109:3e82f62c7e1f 45 }
andrewboyson 109:3e82f62c7e1f 46
andrewboyson 109:3e82f62c7e1f 47 //Have a normal request so authenticate
andrewboyson 109:3e82f62c7e1f 48 if (!WebLoginCookiesContainValidSessionId(pCookies))
andrewboyson 109:3e82f62c7e1f 49 {
andrewboyson 109:3e82f62c7e1f 50 WebLoginOriginalToDo = *pToDo; //Record the original destination for redirection
andrewboyson 109:3e82f62c7e1f 51 *pToDo = DO_LOGIN;
andrewboyson 109:3e82f62c7e1f 52 *pPostComplete = true;
andrewboyson 109:3e82f62c7e1f 53 return; //Ignore any query or post as the user is not authenticated
andrewboyson 109:3e82f62c7e1f 54 }
andrewboyson 109:3e82f62c7e1f 55
andrewboyson 109:3e82f62c7e1f 56 //Handle the query
andrewboyson 109:3e82f62c7e1f 57 WebServerBaseHandleQuery(*pToDo, pQuery);
andrewboyson 109:3e82f62c7e1f 58 }
andrewboyson 109:3e82f62c7e1f 59
andrewboyson 109:3e82f62c7e1f 60 //Do the upload of anything that needs it. Todos it doesn't understand are ignored.
andrewboyson 109:3e82f62c7e1f 61 if (!*pPostComplete) *pPostComplete = WebServerBaseHandlePost(*pToDo, contentLength, contentStart, size, pRequestStream, positionInRequestStream);
andrewboyson 109:3e82f62c7e1f 62 }
andrewboyson 109:3e82f62c7e1f 63
andrewboyson 109:3e82f62c7e1f 64 static void sendReply(int todo)
andrewboyson 109:3e82f62c7e1f 65 {
andrewboyson 109:3e82f62c7e1f 66 //Check if todo includes the need to send a cookie
andrewboyson 109:3e82f62c7e1f 67 if (todo >= DO_SEND_SESSION_ID)
andrewboyson 109:3e82f62c7e1f 68 {
andrewboyson 109:3e82f62c7e1f 69 HttpOkCookieName = WebLoginSessionNameGet();
andrewboyson 109:3e82f62c7e1f 70 HttpOkCookieValue = WebLoginSessionIdGet();
andrewboyson 109:3e82f62c7e1f 71 HttpOkCookieMaxAge = WebLoginSessionNameLife();
andrewboyson 109:3e82f62c7e1f 72 todo -= DO_SEND_SESSION_ID;
andrewboyson 109:3e82f62c7e1f 73 }
andrewboyson 109:3e82f62c7e1f 74 else
andrewboyson 109:3e82f62c7e1f 75 {
andrewboyson 109:3e82f62c7e1f 76 HttpOkCookieName = NULL;
andrewboyson 109:3e82f62c7e1f 77 HttpOkCookieValue = NULL;
andrewboyson 109:3e82f62c7e1f 78 HttpOkCookieMaxAge = -1;
andrewboyson 109:3e82f62c7e1f 79 }
andrewboyson 109:3e82f62c7e1f 80
andrewboyson 109:3e82f62c7e1f 81 WebServerBaseReply(todo);
andrewboyson 109:3e82f62c7e1f 82 }
andrewboyson 109:3e82f62c7e1f 83
andrewboyson 109:3e82f62c7e1f 84 int WebInit()
andrewboyson 109:3e82f62c7e1f 85 {
andrewboyson 109:3e82f62c7e1f 86 HttpRequestFunction = handleRequest;
andrewboyson 109:3e82f62c7e1f 87 HttpReplyFunction = sendReply;
andrewboyson 109:3e82f62c7e1f 88
andrewboyson 109:3e82f62c7e1f 89 WebLoginInit();
andrewboyson 109:3e82f62c7e1f 90
andrewboyson 109:3e82f62c7e1f 91 return 0;
andrewboyson 109:3e82f62c7e1f 92 }