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:
Tue Apr 30 12:45:08 2019 +0000
Revision:
110:8ab752842d25
Parent:
109:3e82f62c7e1f
Child:
111:aaa858678e34
Tidied. About to rename 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 110:8ab752842d25 3 #include "web-server-derived.h"
andrewboyson 110:8ab752842d25 4 #include "web.h"
andrewboyson 110:8ab752842d25 5 #include "web-pages-base.h"
andrewboyson 109:3e82f62c7e1f 6 #include "mstimer.h"
andrewboyson 109:3e82f62c7e1f 7
andrewboyson 109:3e82f62c7e1f 8 #define LOGIN_DELAY_MS 200
andrewboyson 109:3e82f62c7e1f 9
andrewboyson 110:8ab752842d25 10 #define DO_LOGIN DO_SERVER + 0
andrewboyson 110:8ab752842d25 11
andrewboyson 110:8ab752842d25 12 static int decideWhatToDo(char *pPath, char* pLastModified)
andrewboyson 110:8ab752842d25 13 {
andrewboyson 110:8ab752842d25 14 if (HttpSameStr(pPath, "/login")) return DO_LOGIN;
andrewboyson 110:8ab752842d25 15
andrewboyson 110:8ab752842d25 16 int todo;
andrewboyson 110:8ab752842d25 17 todo = WebServerBaseDecideWhatToDo (pPath, pLastModified); if (todo != DO_NOT_FOUND) return todo;
andrewboyson 110:8ab752842d25 18 todo = WebServerDerivedDecideWhatToDo(pPath, pLastModified); if (todo != DO_NOT_FOUND) return todo;
andrewboyson 110:8ab752842d25 19 return DO_NOT_FOUND;
andrewboyson 110:8ab752842d25 20 }
andrewboyson 110:8ab752842d25 21 static void handleQuery(int todo, char* pQuery)
andrewboyson 110:8ab752842d25 22 {
andrewboyson 110:8ab752842d25 23 switch (todo)
andrewboyson 110:8ab752842d25 24 {
andrewboyson 110:8ab752842d25 25 case DO_LOGIN: WebLoginQuery (pQuery); return;
andrewboyson 110:8ab752842d25 26 }
andrewboyson 110:8ab752842d25 27 if (WebServerBaseHandleQuery (todo, pQuery)) return;
andrewboyson 110:8ab752842d25 28 if (WebServerDerivedHandleQuery(todo, pQuery)) return;
andrewboyson 110:8ab752842d25 29 }
andrewboyson 110:8ab752842d25 30 static void handlePost(int todo, int contentLength, int contentStart, int size, char* pRequestStream, uint32_t positionInRequestStream, bool* pComplete)
andrewboyson 110:8ab752842d25 31 {
andrewboyson 110:8ab752842d25 32 if (WebServerBasePost (todo, contentLength, contentStart, size, pRequestStream, positionInRequestStream, pComplete)) return;
andrewboyson 110:8ab752842d25 33 if (WebServerDerivedPost(todo, contentLength, contentStart, size, pRequestStream, positionInRequestStream, pComplete)) return;
andrewboyson 110:8ab752842d25 34 }
andrewboyson 110:8ab752842d25 35
andrewboyson 110:8ab752842d25 36 static void reply(int todo)
andrewboyson 110:8ab752842d25 37 {
andrewboyson 110:8ab752842d25 38 //Try all the base modules
andrewboyson 110:8ab752842d25 39 switch (todo)
andrewboyson 110:8ab752842d25 40 {
andrewboyson 110:8ab752842d25 41 case DO_LOGIN: WebLoginHtml (); return;
andrewboyson 110:8ab752842d25 42 case DO_NOT_FOUND: HttpNotFound (); return;
andrewboyson 110:8ab752842d25 43 case DO_NOT_MODIFIED: HttpNotModified (); return;
andrewboyson 110:8ab752842d25 44 }
andrewboyson 110:8ab752842d25 45
andrewboyson 110:8ab752842d25 46 //If not called then call the derived (child) module
andrewboyson 110:8ab752842d25 47 if (WebServerBaseReply (todo)) return;
andrewboyson 110:8ab752842d25 48 if (WebServerDerivedReply(todo)) return;
andrewboyson 110:8ab752842d25 49 }
andrewboyson 110:8ab752842d25 50
andrewboyson 109:3e82f62c7e1f 51 static void handleRequest(int size, char* pRequestStream, uint32_t positionInRequestStream, int* pToDo, bool* pPostComplete, uint32_t* pDelayUntil)
andrewboyson 109:3e82f62c7e1f 52 {
andrewboyson 109:3e82f62c7e1f 53 //Handle request for the first packet of data received but leave todo the same after that.
andrewboyson 109:3e82f62c7e1f 54 int contentLength = 0;
andrewboyson 109:3e82f62c7e1f 55 int contentStart = 0;
andrewboyson 109:3e82f62c7e1f 56 if (size && positionInRequestStream == 0)
andrewboyson 109:3e82f62c7e1f 57 {
andrewboyson 109:3e82f62c7e1f 58 //Read the headers
andrewboyson 109:3e82f62c7e1f 59 char* pMethod;
andrewboyson 109:3e82f62c7e1f 60 char* pPath;
andrewboyson 109:3e82f62c7e1f 61 char* pQuery;
andrewboyson 109:3e82f62c7e1f 62 char* pLastModified;
andrewboyson 109:3e82f62c7e1f 63 char* pCookies;
andrewboyson 109:3e82f62c7e1f 64 contentStart = HttpRequestRead(pRequestStream, size, &pMethod, &pPath, &pQuery, &pLastModified, &pCookies, &contentLength);
andrewboyson 109:3e82f62c7e1f 65
andrewboyson 109:3e82f62c7e1f 66 //Ask the web server what to do
andrewboyson 110:8ab752842d25 67 *pToDo = decideWhatToDo(pPath, pLastModified);
andrewboyson 109:3e82f62c7e1f 68
andrewboyson 109:3e82f62c7e1f 69 //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 70 if (*pToDo < DO_LOGIN) { *pPostComplete = true; return; }
andrewboyson 109:3e82f62c7e1f 71
andrewboyson 109:3e82f62c7e1f 72 //If what to do is LOGIN then the user has just returned the login form
andrewboyson 109:3e82f62c7e1f 73 if (*pToDo == DO_LOGIN)
andrewboyson 109:3e82f62c7e1f 74 {
andrewboyson 110:8ab752842d25 75 handleQuery(*pToDo, pQuery); //Read the password and the original location
andrewboyson 109:3e82f62c7e1f 76 if (WebLoginQueryPasswordOk)
andrewboyson 109:3e82f62c7e1f 77 {
andrewboyson 109:3e82f62c7e1f 78 if (!WebLoginSessionIdIsSet()) //If there isn't a session id already
andrewboyson 109:3e82f62c7e1f 79 {
andrewboyson 109:3e82f62c7e1f 80 WebLoginSessionIdNew(); //Create a new session id
andrewboyson 109:3e82f62c7e1f 81 }
andrewboyson 109:3e82f62c7e1f 82 *pToDo = WebLoginOriginalToDo; //Load the original todo and SEND_SESSION_ID
andrewboyson 109:3e82f62c7e1f 83 *pToDo += DO_SEND_SESSION_ID;
andrewboyson 109:3e82f62c7e1f 84 }
andrewboyson 109:3e82f62c7e1f 85 *pDelayUntil = MsTimerCount + LOGIN_DELAY_MS; //To prevent brute forcing the hash delay the reply to the login
andrewboyson 109:3e82f62c7e1f 86 *pPostComplete = true;
andrewboyson 109:3e82f62c7e1f 87 return; //Either way no query or post will be valid
andrewboyson 109:3e82f62c7e1f 88 }
andrewboyson 109:3e82f62c7e1f 89
andrewboyson 109:3e82f62c7e1f 90 //Have a normal request so authenticate
andrewboyson 109:3e82f62c7e1f 91 if (!WebLoginCookiesContainValidSessionId(pCookies))
andrewboyson 109:3e82f62c7e1f 92 {
andrewboyson 109:3e82f62c7e1f 93 WebLoginOriginalToDo = *pToDo; //Record the original destination for redirection
andrewboyson 109:3e82f62c7e1f 94 *pToDo = DO_LOGIN;
andrewboyson 109:3e82f62c7e1f 95 *pPostComplete = true;
andrewboyson 109:3e82f62c7e1f 96 return; //Ignore any query or post as the user is not authenticated
andrewboyson 109:3e82f62c7e1f 97 }
andrewboyson 109:3e82f62c7e1f 98
andrewboyson 109:3e82f62c7e1f 99 //Handle the query
andrewboyson 110:8ab752842d25 100 handleQuery(*pToDo, pQuery);
andrewboyson 109:3e82f62c7e1f 101 }
andrewboyson 109:3e82f62c7e1f 102
andrewboyson 109:3e82f62c7e1f 103 //Do the upload of anything that needs it. Todos it doesn't understand are ignored.
andrewboyson 110:8ab752842d25 104 if (!*pPostComplete) handlePost(*pToDo, contentLength, contentStart, size, pRequestStream, positionInRequestStream, pPostComplete);
andrewboyson 109:3e82f62c7e1f 105 }
andrewboyson 109:3e82f62c7e1f 106
andrewboyson 109:3e82f62c7e1f 107 static void sendReply(int todo)
andrewboyson 109:3e82f62c7e1f 108 {
andrewboyson 109:3e82f62c7e1f 109 //Check if todo includes the need to send a cookie
andrewboyson 109:3e82f62c7e1f 110 if (todo >= DO_SEND_SESSION_ID)
andrewboyson 109:3e82f62c7e1f 111 {
andrewboyson 109:3e82f62c7e1f 112 HttpOkCookieName = WebLoginSessionNameGet();
andrewboyson 109:3e82f62c7e1f 113 HttpOkCookieValue = WebLoginSessionIdGet();
andrewboyson 109:3e82f62c7e1f 114 HttpOkCookieMaxAge = WebLoginSessionNameLife();
andrewboyson 109:3e82f62c7e1f 115 todo -= DO_SEND_SESSION_ID;
andrewboyson 109:3e82f62c7e1f 116 }
andrewboyson 109:3e82f62c7e1f 117 else
andrewboyson 109:3e82f62c7e1f 118 {
andrewboyson 109:3e82f62c7e1f 119 HttpOkCookieName = NULL;
andrewboyson 109:3e82f62c7e1f 120 HttpOkCookieValue = NULL;
andrewboyson 109:3e82f62c7e1f 121 HttpOkCookieMaxAge = -1;
andrewboyson 109:3e82f62c7e1f 122 }
andrewboyson 109:3e82f62c7e1f 123
andrewboyson 110:8ab752842d25 124 reply(todo);
andrewboyson 109:3e82f62c7e1f 125 }
andrewboyson 109:3e82f62c7e1f 126
andrewboyson 109:3e82f62c7e1f 127 int WebInit()
andrewboyson 109:3e82f62c7e1f 128 {
andrewboyson 109:3e82f62c7e1f 129 HttpRequestFunction = handleRequest;
andrewboyson 109:3e82f62c7e1f 130 HttpReplyFunction = sendReply;
andrewboyson 109:3e82f62c7e1f 131
andrewboyson 109:3e82f62c7e1f 132 WebLoginInit();
andrewboyson 109:3e82f62c7e1f 133
andrewboyson 109:3e82f62c7e1f 134 return 0;
andrewboyson 109:3e82f62c7e1f 135 }