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

Dependents:   heating gps

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:
Wed May 01 07:15:11 2019 +0000
Revision:
111:aaa858678e34
Parent:
110:8ab752842d25
Child:
112:f29bb9b99059
Corrected bug where postComplete was not set true in the event of there not being a post.

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