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

Dependents:   gps 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:
Sun Sep 01 18:12:48 2019 +0000
Revision:
129:6d9bffc72676
Parent:
128:fc9708e1d17c
Child:
130:9a5b8fe308f1
Tidied up connection checks

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 112:f29bb9b99059 3 #include "web-server-this.h"
andrewboyson 110:8ab752842d25 4 #include "web.h"
andrewboyson 110:8ab752842d25 5 #include "web-pages-base.h"
andrewboyson 128:fc9708e1d17c 6 #include "web-connection.h"
andrewboyson 109:3e82f62c7e1f 7 #include "mstimer.h"
andrewboyson 128:fc9708e1d17c 8 #include "log.h"
andrewboyson 109:3e82f62c7e1f 9
andrewboyson 109:3e82f62c7e1f 10 #define LOGIN_DELAY_MS 200
andrewboyson 109:3e82f62c7e1f 11
andrewboyson 110:8ab752842d25 12 #define DO_LOGIN DO_SERVER + 0
andrewboyson 110:8ab752842d25 13
andrewboyson 110:8ab752842d25 14 static int decideWhatToDo(char *pPath, char* pLastModified)
andrewboyson 110:8ab752842d25 15 {
andrewboyson 110:8ab752842d25 16 if (HttpSameStr(pPath, "/login")) return DO_LOGIN;
andrewboyson 110:8ab752842d25 17
andrewboyson 110:8ab752842d25 18 int todo;
andrewboyson 112:f29bb9b99059 19 todo = WebServerBaseDecideWhatToDo(pPath, pLastModified); if (todo != DO_NOT_FOUND) return todo;
andrewboyson 112:f29bb9b99059 20 todo = WebServerThisDecideWhatToDo(pPath, pLastModified); if (todo != DO_NOT_FOUND) return todo;
andrewboyson 110:8ab752842d25 21 return DO_NOT_FOUND;
andrewboyson 110:8ab752842d25 22 }
andrewboyson 110:8ab752842d25 23 static void handleQuery(int todo, char* pQuery)
andrewboyson 110:8ab752842d25 24 {
andrewboyson 110:8ab752842d25 25 switch (todo)
andrewboyson 110:8ab752842d25 26 {
andrewboyson 110:8ab752842d25 27 case DO_LOGIN: WebLoginQuery (pQuery); return;
andrewboyson 110:8ab752842d25 28 }
andrewboyson 112:f29bb9b99059 29 if (WebServerBaseHandleQuery(todo, pQuery)) return;
andrewboyson 112:f29bb9b99059 30 if (WebServerThisHandleQuery(todo, pQuery)) return;
andrewboyson 110:8ab752842d25 31 }
andrewboyson 110:8ab752842d25 32 static void handlePost(int todo, int contentLength, int contentStart, int size, char* pRequestStream, uint32_t positionInRequestStream, bool* pComplete)
andrewboyson 110:8ab752842d25 33 {
andrewboyson 112:f29bb9b99059 34 if (WebServerBasePost(todo, contentLength, contentStart, size, pRequestStream, positionInRequestStream, pComplete)) return;
andrewboyson 112:f29bb9b99059 35 if (WebServerThisPost(todo, contentLength, contentStart, size, pRequestStream, positionInRequestStream, pComplete)) return;
andrewboyson 111:aaa858678e34 36 *pComplete = true;
andrewboyson 110:8ab752842d25 37 }
andrewboyson 110:8ab752842d25 38
andrewboyson 110:8ab752842d25 39 static void reply(int todo)
andrewboyson 110:8ab752842d25 40 {
andrewboyson 110:8ab752842d25 41 //Try all the base modules
andrewboyson 110:8ab752842d25 42 switch (todo)
andrewboyson 110:8ab752842d25 43 {
andrewboyson 110:8ab752842d25 44 case DO_LOGIN: WebLoginHtml (); return;
andrewboyson 110:8ab752842d25 45 case DO_NOT_FOUND: HttpNotFound (); return;
andrewboyson 110:8ab752842d25 46 case DO_NOT_MODIFIED: HttpNotModified (); return;
andrewboyson 110:8ab752842d25 47 }
andrewboyson 110:8ab752842d25 48
andrewboyson 110:8ab752842d25 49 //If not called then call the derived (child) module
andrewboyson 112:f29bb9b99059 50 if (WebServerBaseReply(todo)) return;
andrewboyson 112:f29bb9b99059 51 if (WebServerThisReply(todo)) return;
andrewboyson 110:8ab752842d25 52 }
andrewboyson 128:fc9708e1d17c 53 static void handleRequest(int connectionId, int size, char* pRequestStream, uint32_t positionInRequestStream)
andrewboyson 109:3e82f62c7e1f 54 {
andrewboyson 129:6d9bffc72676 55 struct WebConnection* pConnection;
andrewboyson 129:6d9bffc72676 56 if (!positionInRequestStream)
andrewboyson 129:6d9bffc72676 57 {
andrewboyson 129:6d9bffc72676 58 pConnection = WebConnectionNew(connectionId);
andrewboyson 129:6d9bffc72676 59 }
andrewboyson 129:6d9bffc72676 60 else
andrewboyson 128:fc9708e1d17c 61 {
andrewboyson 129:6d9bffc72676 62 pConnection = WebConnectionOrNull(connectionId);
andrewboyson 129:6d9bffc72676 63 if (!pConnection)
andrewboyson 129:6d9bffc72676 64 {
andrewboyson 129:6d9bffc72676 65 LogTimeF("WebRequest - no connection corresponds to id %d\r\n", connectionId);
andrewboyson 129:6d9bffc72676 66 return;
andrewboyson 129:6d9bffc72676 67 }
andrewboyson 128:fc9708e1d17c 68 }
andrewboyson 122:cd3f391ac8aa 69
andrewboyson 128:fc9708e1d17c 70 pConnection->delayUntil = MsTimerCount; //Default to no delay unless modified;
andrewboyson 122:cd3f391ac8aa 71
andrewboyson 109:3e82f62c7e1f 72 //Handle request for the first packet of data received but leave todo the same after that.
andrewboyson 109:3e82f62c7e1f 73 int contentLength = 0;
andrewboyson 109:3e82f62c7e1f 74 int contentStart = 0;
andrewboyson 109:3e82f62c7e1f 75 if (size && positionInRequestStream == 0)
andrewboyson 109:3e82f62c7e1f 76 {
andrewboyson 109:3e82f62c7e1f 77 //Read the headers
andrewboyson 109:3e82f62c7e1f 78 char* pMethod;
andrewboyson 109:3e82f62c7e1f 79 char* pPath;
andrewboyson 109:3e82f62c7e1f 80 char* pQuery;
andrewboyson 109:3e82f62c7e1f 81 char* pLastModified;
andrewboyson 109:3e82f62c7e1f 82 char* pCookies;
andrewboyson 109:3e82f62c7e1f 83 contentStart = HttpRequestRead(pRequestStream, size, &pMethod, &pPath, &pQuery, &pLastModified, &pCookies, &contentLength);
andrewboyson 109:3e82f62c7e1f 84
andrewboyson 109:3e82f62c7e1f 85 //Ask the web server what to do
andrewboyson 128:fc9708e1d17c 86 pConnection->toDo = decideWhatToDo(pPath, pLastModified);
andrewboyson 109:3e82f62c7e1f 87
andrewboyson 109:3e82f62c7e1f 88 //If what to do is NOTHING, NOT_FOUND or NOT_MODIFIED then no query or post will be valid so stop now
andrewboyson 128:fc9708e1d17c 89 if (pConnection->toDo < DO_LOGIN) { pConnection->postComplete = true; return; }
andrewboyson 109:3e82f62c7e1f 90
andrewboyson 109:3e82f62c7e1f 91 //If what to do is LOGIN then the user has just returned the login form
andrewboyson 128:fc9708e1d17c 92 if (pConnection->toDo == DO_LOGIN)
andrewboyson 109:3e82f62c7e1f 93 {
andrewboyson 128:fc9708e1d17c 94 handleQuery(pConnection->toDo, pQuery); //Read the password and the original location
andrewboyson 109:3e82f62c7e1f 95 if (WebLoginQueryPasswordOk)
andrewboyson 109:3e82f62c7e1f 96 {
andrewboyson 109:3e82f62c7e1f 97 if (!WebLoginSessionIdIsSet()) //If there isn't a session id already
andrewboyson 109:3e82f62c7e1f 98 {
andrewboyson 109:3e82f62c7e1f 99 WebLoginSessionIdNew(); //Create a new session id
andrewboyson 109:3e82f62c7e1f 100 }
andrewboyson 128:fc9708e1d17c 101 pConnection->toDo = WebLoginOriginalToDo; //Load the original todo and SEND_SESSION_ID
andrewboyson 128:fc9708e1d17c 102 pConnection->toDo += DO_SEND_SESSION_ID;
andrewboyson 109:3e82f62c7e1f 103 }
andrewboyson 128:fc9708e1d17c 104 pConnection->delayUntil = MsTimerCount + LOGIN_DELAY_MS; //To prevent brute forcing the hash delay the reply to the login
andrewboyson 128:fc9708e1d17c 105 pConnection->postComplete = true;
andrewboyson 109:3e82f62c7e1f 106 return; //Either way no query or post will be valid
andrewboyson 109:3e82f62c7e1f 107 }
andrewboyson 109:3e82f62c7e1f 108
andrewboyson 109:3e82f62c7e1f 109 //Have a normal request so authenticate
andrewboyson 109:3e82f62c7e1f 110 if (!WebLoginCookiesContainValidSessionId(pCookies))
andrewboyson 109:3e82f62c7e1f 111 {
andrewboyson 128:fc9708e1d17c 112 WebLoginOriginalToDo = pConnection->toDo; //Record the original destination for redirection
andrewboyson 128:fc9708e1d17c 113 pConnection->toDo = DO_LOGIN;
andrewboyson 128:fc9708e1d17c 114 pConnection->postComplete = true;
andrewboyson 109:3e82f62c7e1f 115 return; //Ignore any query or post as the user is not authenticated
andrewboyson 109:3e82f62c7e1f 116 }
andrewboyson 109:3e82f62c7e1f 117
andrewboyson 109:3e82f62c7e1f 118 //Handle the query
andrewboyson 128:fc9708e1d17c 119 handleQuery(pConnection->toDo, pQuery);
andrewboyson 109:3e82f62c7e1f 120 }
andrewboyson 109:3e82f62c7e1f 121
andrewboyson 109:3e82f62c7e1f 122 //Do the upload of anything that needs it. Todos it doesn't understand are ignored.
andrewboyson 128:fc9708e1d17c 123 if (!pConnection->postComplete) handlePost(pConnection->toDo, contentLength, contentStart, size, pRequestStream, positionInRequestStream, &pConnection->postComplete);
andrewboyson 109:3e82f62c7e1f 124 }
andrewboyson 109:3e82f62c7e1f 125
andrewboyson 128:fc9708e1d17c 126 static bool pollSomethingToSend(int connectionId, bool clientFinished) //returns true if finished; false if not;
andrewboyson 109:3e82f62c7e1f 127 {
andrewboyson 128:fc9708e1d17c 128 struct WebConnection* pConnection = WebConnectionOrNull(connectionId);
andrewboyson 129:6d9bffc72676 129 if (!pConnection) return false; //Protects the gap between the connection being established and the request being started
andrewboyson 122:cd3f391ac8aa 130
andrewboyson 128:fc9708e1d17c 131 if (!pConnection->toDo)
andrewboyson 125:772948168e4f 132 {
andrewboyson 127:bd6dd135009d 133 if (clientFinished) return true; //The client hasn't requested anything and never will so finish
andrewboyson 127:bd6dd135009d 134 else return false; //The client hasn't requested anything yet but still could
andrewboyson 125:772948168e4f 135 }
andrewboyson 128:fc9708e1d17c 136 if (!pConnection->postComplete ) return false; //Wait for the request (usually a POST) to finish
andrewboyson 128:fc9708e1d17c 137 if (!MsTimerAbsolute(pConnection->delayUntil)) return false; //Wait a while (usually after a LOGIN attempt)
andrewboyson 122:cd3f391ac8aa 138
andrewboyson 128:fc9708e1d17c 139 int todo = pConnection->toDo; //Make a copy so that we don't modify todo in the state
andrewboyson 122:cd3f391ac8aa 140
andrewboyson 109:3e82f62c7e1f 141 //Check if todo includes the need to send a cookie
andrewboyson 109:3e82f62c7e1f 142 if (todo >= DO_SEND_SESSION_ID)
andrewboyson 109:3e82f62c7e1f 143 {
andrewboyson 109:3e82f62c7e1f 144 HttpOkCookieName = WebLoginSessionNameGet();
andrewboyson 109:3e82f62c7e1f 145 HttpOkCookieValue = WebLoginSessionIdGet();
andrewboyson 109:3e82f62c7e1f 146 HttpOkCookieMaxAge = WebLoginSessionNameLife();
andrewboyson 109:3e82f62c7e1f 147 todo -= DO_SEND_SESSION_ID;
andrewboyson 109:3e82f62c7e1f 148 }
andrewboyson 109:3e82f62c7e1f 149 else
andrewboyson 109:3e82f62c7e1f 150 {
andrewboyson 109:3e82f62c7e1f 151 HttpOkCookieName = NULL;
andrewboyson 109:3e82f62c7e1f 152 HttpOkCookieValue = NULL;
andrewboyson 109:3e82f62c7e1f 153 HttpOkCookieMaxAge = -1;
andrewboyson 109:3e82f62c7e1f 154 }
andrewboyson 109:3e82f62c7e1f 155
andrewboyson 110:8ab752842d25 156 reply(todo);
andrewboyson 122:cd3f391ac8aa 157
andrewboyson 123:06de83222fda 158 return !HttpBufFilled(); //If we haven't used a full buffer then we have finished
andrewboyson 109:3e82f62c7e1f 159 }
andrewboyson 109:3e82f62c7e1f 160
andrewboyson 109:3e82f62c7e1f 161 int WebInit()
andrewboyson 109:3e82f62c7e1f 162 {
andrewboyson 128:fc9708e1d17c 163 HttpFunctionReset = WebConnectionReset;
andrewboyson 128:fc9708e1d17c 164 HttpFunctionRequest = handleRequest;
andrewboyson 128:fc9708e1d17c 165 HttpFunctionPoll = pollSomethingToSend;
andrewboyson 109:3e82f62c7e1f 166
andrewboyson 109:3e82f62c7e1f 167 WebLoginInit();
andrewboyson 109:3e82f62c7e1f 168
andrewboyson 109:3e82f62c7e1f 169 return 0;
andrewboyson 109:3e82f62c7e1f 170 }