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 Jun 02 16:19:34 2020 +0000
Revision:
138:44d84506b2f6
Parent:
137:3b6632374855
Child:
139:e189c6669983
Modified AjaxRequest to AjaxSendNameValue in order to be able to encode '=' and '?' correctly

Who changed what in which revision?

UserRevisionLine numberNew contents of line
andrewboyson 96:eb2eb75bad0f 1 "//Ajax class\n"
andrewboyson 95:8c9dda8a0caf 2 "'use strict';\n"
andrewboyson 95:8c9dda8a0caf 3 "\n"
andrewboyson 95:8c9dda8a0caf 4 "//Exposed properties\n"
andrewboyson 95:8c9dda8a0caf 5 "let ajaxResponse_ = '';\n"
andrewboyson 95:8c9dda8a0caf 6 "let ajaxHeaders_ = '';\n"
andrewboyson 96:eb2eb75bad0f 7 "let ajaxDate_ = null;\n"
andrewboyson 95:8c9dda8a0caf 8 "let ajaxMs_ = 0;\n"
andrewboyson 95:8c9dda8a0caf 9 "let ajaxOnResponse_ = null;\n"
andrewboyson 95:8c9dda8a0caf 10 "let ajaxOnTick_ = null;\n"
andrewboyson 95:8c9dda8a0caf 11 "let ajaxServer_ = '';\n"
andrewboyson 95:8c9dda8a0caf 12 "\n"
andrewboyson 95:8c9dda8a0caf 13 "//Private variables\n"
andrewboyson 95:8c9dda8a0caf 14 "let ajaxOverrideBlockUpdateOnFocus_ = false;\n"
andrewboyson 95:8c9dda8a0caf 15 "let ajaxXhr_ = null;\n"
andrewboyson 95:8c9dda8a0caf 16 "let ajaxMsCountAtAjaxSend_ = 0;\n"
andrewboyson 95:8c9dda8a0caf 17 "const ajaxTickMs_ = 100;\n"
andrewboyson 95:8c9dda8a0caf 18 "const ajaxUpdateMs_ = 10000;\n"
andrewboyson 95:8c9dda8a0caf 19 "\n"
andrewboyson 95:8c9dda8a0caf 20 "//Private utilities\n"
andrewboyson 95:8c9dda8a0caf 21 "function ajaxGetElementOrNull_(elementName) //Returns the element if it: exists; block is overidden; does not have focus\n"
andrewboyson 95:8c9dda8a0caf 22 "{\n"
andrewboyson 95:8c9dda8a0caf 23 " let elem = document.getElementById(elementName);\n"
andrewboyson 95:8c9dda8a0caf 24 " if (!elem) return null;\n"
andrewboyson 95:8c9dda8a0caf 25 " if (ajaxOverrideBlockUpdateOnFocus_) return elem;\n"
andrewboyson 95:8c9dda8a0caf 26 " if (elem !== document.activeElement) return elem;\n"
andrewboyson 95:8c9dda8a0caf 27 " return null;\n"
andrewboyson 95:8c9dda8a0caf 28 "}\n"
andrewboyson 95:8c9dda8a0caf 29 "function ajaxHexToBit_(text, iBit)\n"
andrewboyson 95:8c9dda8a0caf 30 "{\n"
andrewboyson 95:8c9dda8a0caf 31 " let value = parseInt(text, 16);\n"
andrewboyson 95:8c9dda8a0caf 32 " value >>= iBit;\n"
andrewboyson 95:8c9dda8a0caf 33 " return value & 1;\n"
andrewboyson 95:8c9dda8a0caf 34 "}\n"
andrewboyson 98:4e099563d5b9 35 "function ajaxHexToSignedInt8_(text)\n"
andrewboyson 98:4e099563d5b9 36 "{\n"
andrewboyson 98:4e099563d5b9 37 " let value = parseInt(text, 16);\n"
andrewboyson 98:4e099563d5b9 38 " if (value < 0x80) return value;\n"
andrewboyson 98:4e099563d5b9 39 " return value - 0x100;\n"
andrewboyson 98:4e099563d5b9 40 "}\n"
andrewboyson 98:4e099563d5b9 41 "function ajaxHexToSignedInt16_(text)\n"
andrewboyson 98:4e099563d5b9 42 "{\n"
andrewboyson 98:4e099563d5b9 43 " let value = parseInt(text, 16);\n"
andrewboyson 98:4e099563d5b9 44 " if (value < 0x8000) return value;\n"
andrewboyson 98:4e099563d5b9 45 " return value - 0x10000;\n"
andrewboyson 98:4e099563d5b9 46 "}\n"
andrewboyson 98:4e099563d5b9 47 "function ajaxHexToSignedInt32_(text)\n"
andrewboyson 98:4e099563d5b9 48 "{\n"
andrewboyson 98:4e099563d5b9 49 " let value = parseInt(text, 16);\n"
andrewboyson 98:4e099563d5b9 50 " if (value < 0x80000000) return value;\n"
andrewboyson 98:4e099563d5b9 51 " return value - 0x100000000;\n"
andrewboyson 98:4e099563d5b9 52 "}\n"
andrewboyson 98:4e099563d5b9 53 "\n"
andrewboyson 95:8c9dda8a0caf 54 "\n"
andrewboyson 95:8c9dda8a0caf 55 "//Private ajax functions\n"
andrewboyson 95:8c9dda8a0caf 56 "function ajaxHandleAjaxResponse_()\n"
andrewboyson 95:8c9dda8a0caf 57 "{\n"
andrewboyson 95:8c9dda8a0caf 58 " if (ajaxXhr_.readyState == 4 && ajaxXhr_.status == 200)\n"
andrewboyson 95:8c9dda8a0caf 59 " {\n"
andrewboyson 95:8c9dda8a0caf 60 " ajaxResponse_ = ajaxXhr_.responseText;\n"
andrewboyson 95:8c9dda8a0caf 61 " ajaxHeaders_ = ajaxXhr_.getAllResponseHeaders();\n"
andrewboyson 96:eb2eb75bad0f 62 " let iDateStart = Ajax.headers.toLowerCase().indexOf('date:');\n"
andrewboyson 96:eb2eb75bad0f 63 " let iDateEnd = Ajax.headers.indexOf('\\r', iDateStart);\n"
andrewboyson 96:eb2eb75bad0f 64 " ajaxDate_ = new Date(Ajax.headers.slice(iDateStart + 5, iDateEnd));\n"
andrewboyson 96:eb2eb75bad0f 65 "\n"
andrewboyson 95:8c9dda8a0caf 66 " let elem;\n"
andrewboyson 98:4e099563d5b9 67 " elem = ajaxGetElementOrNull_('ajax-response' ); if (elem) elem.textContent = ajaxResponse_;\n"
andrewboyson 98:4e099563d5b9 68 " elem = ajaxGetElementOrNull_('ajax-headers' ); if (elem) elem.textContent = ajaxHeaders_;\n"
andrewboyson 98:4e099563d5b9 69 " elem = ajaxGetElementOrNull_('ajax-date-local' );\n"
andrewboyson 98:4e099563d5b9 70 " if (elem)\n"
andrewboyson 98:4e099563d5b9 71 " {\n"
andrewboyson 98:4e099563d5b9 72 " elem.textContent = ajaxDate_.toLocaleString( undefined, { weekday : 'short' ,\n"
andrewboyson 98:4e099563d5b9 73 " day : '2-digit',\n"
andrewboyson 98:4e099563d5b9 74 " month : 'short' ,\n"
andrewboyson 98:4e099563d5b9 75 " year : 'numeric',\n"
andrewboyson 98:4e099563d5b9 76 " hour : '2-digit',\n"
andrewboyson 98:4e099563d5b9 77 " minute : '2-digit',\n"
andrewboyson 98:4e099563d5b9 78 " timeZoneName: 'short'\n"
andrewboyson 98:4e099563d5b9 79 " }\n"
andrewboyson 98:4e099563d5b9 80 " );\n"
andrewboyson 98:4e099563d5b9 81 " }\n"
andrewboyson 95:8c9dda8a0caf 82 " if (ajaxOnResponse_) ajaxOnResponse_();\n"
andrewboyson 95:8c9dda8a0caf 83 " ajaxOverrideBlockUpdateOnFocus_ = false; //Received response so reset override after display\n"
andrewboyson 95:8c9dda8a0caf 84 " }\n"
andrewboyson 95:8c9dda8a0caf 85 "}\n"
andrewboyson 138:44d84506b2f6 86 "function ajaxSendNameValue_(name, value) //Used by this script and from HTML page\n"
andrewboyson 95:8c9dda8a0caf 87 "{\n"
andrewboyson 95:8c9dda8a0caf 88 " ajaxXhr_ = new XMLHttpRequest();\n"
andrewboyson 95:8c9dda8a0caf 89 " ajaxXhr_.onreadystatechange = ajaxHandleAjaxResponse_;\n"
andrewboyson 138:44d84506b2f6 90 " if (name)\n"
andrewboyson 98:4e099563d5b9 91 " {\n"
andrewboyson 138:44d84506b2f6 92 " if (value)\n"
andrewboyson 138:44d84506b2f6 93 " {\n"
andrewboyson 138:44d84506b2f6 94 " value = value.split('%').join('%25'); //Encode '%'s first\n"
andrewboyson 138:44d84506b2f6 95 " value = value.split('+').join('%2B');\n"
andrewboyson 138:44d84506b2f6 96 " value = value.split('=').join('%3D');\n"
andrewboyson 138:44d84506b2f6 97 " value = value.split('?').join('%3F');\n"
andrewboyson 138:44d84506b2f6 98 " value = value.split('#').join('%23');\n"
andrewboyson 138:44d84506b2f6 99 " ajaxXhr_.open('GET', ajaxServer_ + '?' + name + '=' + value, true);\n"
andrewboyson 138:44d84506b2f6 100 " }\n"
andrewboyson 138:44d84506b2f6 101 " else\n"
andrewboyson 138:44d84506b2f6 102 " {\n"
andrewboyson 138:44d84506b2f6 103 " ajaxXhr_.open('GET', ajaxServer_ + '?' + name, true);\n"
andrewboyson 138:44d84506b2f6 104 " }\n"
andrewboyson 98:4e099563d5b9 105 " }\n"
andrewboyson 98:4e099563d5b9 106 " else\n"
andrewboyson 98:4e099563d5b9 107 " {\n"
andrewboyson 138:44d84506b2f6 108 " ajaxXhr_.open('GET', ajaxServer_, true);\n"
andrewboyson 98:4e099563d5b9 109 " }\n"
andrewboyson 95:8c9dda8a0caf 110 " ajaxXhr_.send();\n"
andrewboyson 95:8c9dda8a0caf 111 " ajaxMsCountAtAjaxSend_ = ajaxMs_;\n"
andrewboyson 95:8c9dda8a0caf 112 "}\n"
andrewboyson 138:44d84506b2f6 113 "function AjaxSendNameValue(name, value) //From html\n"
andrewboyson 95:8c9dda8a0caf 114 "{\n"
andrewboyson 95:8c9dda8a0caf 115 " ajaxOverrideBlockUpdateOnFocus_ = true; //Request has come from an update\n"
andrewboyson 138:44d84506b2f6 116 " ajaxSendNameValue_(name, value);\n"
andrewboyson 95:8c9dda8a0caf 117 "}\n"
andrewboyson 95:8c9dda8a0caf 118 "\n"
andrewboyson 95:8c9dda8a0caf 119 "//Private functions\n"
andrewboyson 95:8c9dda8a0caf 120 "function ajaxTick_() //Called about every 100ms\n"
andrewboyson 95:8c9dda8a0caf 121 "{\n"
andrewboyson 95:8c9dda8a0caf 122 " ajaxMs_ += ajaxTickMs_; //Don't use Date.now() as we don't know when the PC's clock will be updated around a leap second\n"
andrewboyson 95:8c9dda8a0caf 123 " if (ajaxMs_ >= ajaxMsCountAtAjaxSend_ + ajaxUpdateMs_) ajaxSendAjaxRequest_('');\n"
andrewboyson 95:8c9dda8a0caf 124 " if (ajaxOnTick_) ajaxOnTick_();\n"
andrewboyson 95:8c9dda8a0caf 125 "}\n"
andrewboyson 95:8c9dda8a0caf 126 "function ajaxInit_()\n"
andrewboyson 95:8c9dda8a0caf 127 "{\n"
andrewboyson 95:8c9dda8a0caf 128 " setInterval(ajaxTick_, ajaxTickMs_);\n"
andrewboyson 95:8c9dda8a0caf 129 " ajaxSendAjaxRequest_('');\n"
andrewboyson 95:8c9dda8a0caf 130 "}\n"
andrewboyson 95:8c9dda8a0caf 131 "\n"
andrewboyson 95:8c9dda8a0caf 132 "//Exposed public\n"
andrewboyson 95:8c9dda8a0caf 133 "class Ajax\n"
andrewboyson 95:8c9dda8a0caf 134 "{\n"
andrewboyson 98:4e099563d5b9 135 " static get ms () { return ajaxMs_ ; }\n"
andrewboyson 98:4e099563d5b9 136 " static get response () { return ajaxResponse_ ; }\n"
andrewboyson 98:4e099563d5b9 137 " static get headers () { return ajaxHeaders_ ; }\n"
andrewboyson 98:4e099563d5b9 138 " static get date () { return ajaxDate_ ; }\n"
andrewboyson 98:4e099563d5b9 139 " \n"
andrewboyson 98:4e099563d5b9 140 " static set tickMs (v) { ajaxTickMs_ = v; }\n"
andrewboyson 98:4e099563d5b9 141 " static set updateMs (v) { ajaxUpdateMs_ = v; }\n"
andrewboyson 98:4e099563d5b9 142 " static set server (v) { ajaxServer_ = v; }\n"
andrewboyson 98:4e099563d5b9 143 " static set onResponse(v) { ajaxOnResponse_ = v; }\n"
andrewboyson 98:4e099563d5b9 144 " static set onTick (v) { ajaxOnTick_ = v; }\n"
andrewboyson 95:8c9dda8a0caf 145 "\n"
andrewboyson 95:8c9dda8a0caf 146 " static getElementOrNull(elementName) { return ajaxGetElementOrNull_(elementName) ; }\n"
andrewboyson 95:8c9dda8a0caf 147 " static hexToBit (text, iBit ) { return ajaxHexToBit_ (text, iBit ) ; }\n"
andrewboyson 98:4e099563d5b9 148 " static hexToSignedInt8 (text ) { return ajaxHexToSignedInt8_ (text ) ; }\n"
andrewboyson 98:4e099563d5b9 149 " static hexToSignedInt16(text ) { return ajaxHexToSignedInt16_(text ) ; }\n"
andrewboyson 98:4e099563d5b9 150 " static hexToSignedInt32(text ) { return ajaxHexToSignedInt32_(text ) ; }\n"
andrewboyson 95:8c9dda8a0caf 151 " \n"
andrewboyson 95:8c9dda8a0caf 152 " static init()\n"
andrewboyson 95:8c9dda8a0caf 153 " {\n"
andrewboyson 95:8c9dda8a0caf 154 " if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', ajaxInit_ ); // Loading hasn't finished yet\n"
andrewboyson 95:8c9dda8a0caf 155 " else ajaxInit_(); //`DOMContentLoaded` has already fired\n"
andrewboyson 95:8c9dda8a0caf 156 " }\n"
andrewboyson 95:8c9dda8a0caf 157 "}"