Updated documentation
[CGIscriptor.git] / Private / Login.html
bloba9e0ba9e83fd69ec4b238f746683075866550a99
1 <html>
2 <head>
3 <title>Login</title>
4 <META CONTENT="text/ssperl; CGI='$SERVERSALT $LOGINTICKET $RANDOMSALT $REMOTE_ADDR'">
5 <SCRIPT type="text/javascript" LANGUAGE="JavaScript">
6 function createCookie(name,value,days,path) {
7 var expires = "";
8 if (days) {
9 var date = new Date();
10 date.setTime(date.getTime()+(days*24*60*60*1000));
11 expires = ';expires='+date.toGMTString();
13 document.cookie = name+'='+value+expires+';path=/'+path+'';
16 function readCookie(name) {
17 var nameEQ = name + "=";
18 var ca = document.cookie.split(';');
19 for(var i=0;i < ca.length;i++) {
20 var c = ca[i];
21 while (c.charAt(0)==' ') c = c.substring(1,c.length);
22 if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
24 return null;
27 function eraseCookie(name) {
28 createCookie(name,"",-1);
30 </SCRIPT>
31 <script type="text/javascript">
32 // Combine the PASSWORD with the site SALT and hash it
33 // Combine this Hash with the extra SALT, and hash them
34 function HashPassword(extsalt) {
35 var hash = HashSessionSeed(extsalt);
36 var password = document.getElementById('PASSWORD');
37 if(password){
38 password.value = hash;
39 } else {
40 alert("NO PASSWORD IN FORM");
41 return 0;
43 return hash;
46 // REMEMBER: Set the session cookie BEFORE you hash the password!!!
47 function SetSessionCookie() {
48 var seed = '<SCRIPT TYPE="text/ssperl">$LOGINTICKET</SCRIPT>';
49 var hash = HashSessionSeed(seed);
50 // Dom.storage.enabled must be set!
51 if (!sessionStorage || typeof(sessionStorage) == 'undefined' ) {
52 alert('Your browser does not support HTML5 sessionStorage. Set Dom.storage.enabled or try upgrading.');
54 else sessionStorage.setItem("CGIscriptorPRIVATE", hash);
55 return hash;
58 function HashSessionSeed(sessionseed) {
59 var hash1 = "";
60 var hash2 = "";
61 var passwordvalue = document.getElementById('PASSWORD');
62 var saltvalue = document.getElementById('SALT');
63 var username = document.getElementById('USERNAME');
64 hash1 = hex_sha1(saltvalue.value+passwordvalue.value+username.value);
65 if(sessionseed != "")
66 hash2 = hex_sha1(sessionseed+hash1);
67 else
68 hash2 = hash1;
69 return hash2;
72 function remove_cgiparam(elem, attr, parameter) {
73 var elems = document.getElementsByTagName(elem);
74 for (var i = 0; i < elems.length; i++)
76 var n=elems[i][attr].indexOf("&"+parameter);
77 if(n>0)
78 elems[i][attr] = elems[i][attr].replace("&"+param, "");
79 var n=elems[i][attr].indexOf(parameter);
80 if(n>0)
81 elems[i][attr] = elems[i][attr].replace(param, "");
85 function check_username_password ( ) {
86 var username = document.getElementById('USERNAME');
87 var password = document.getElementById('PASSWORD');
88 if(username.value.match(/[a-zA-Z0-9]/) && password.value.match(/[a-zA-Z0-9]/))
89 return true;
90 alert("Please enter a user name and password");
91 return false;
94 // Remove EVERYTHING from Login window
95 window.onload = function() {
96 if(window.location.search)window.location.search = "";
97 var warning = document.getElementById('WARNING');
98 if(sessionStorage == null) warning.innerHTML = "Storage not supported by the browser: Upgrade your browser or set dom.storage.enabled";
99 else {
100 warning.style.color = "Black";
101 warning.innerHTML = "";
104 </SCRIPT>
106 <script type="text/javascript">
108 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
109 * in FIPS 180-1
110 * Version 2.2 Copyright Paul Johnston 2000 - 2009.
111 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
112 * Distributed under the BSD License
113 * See http://pajhome.org.uk/crypt/md5 for details.
117 * Configurable variables. You may need to tweak these to be compatible with
118 * the server-side, but the defaults work in most cases.
120 var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
121 var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
124 * These are the functions you'll usually want to call
125 * They take string arguments and return either hex or base-64 encoded strings
127 function hex_sha1(s) { return rstr2hex(rstr_sha1(str2rstr_utf8(s))); }
128 function b64_sha1(s) { return rstr2b64(rstr_sha1(str2rstr_utf8(s))); }
129 function any_sha1(s, e) { return rstr2any(rstr_sha1(str2rstr_utf8(s)), e); }
130 function hex_hmac_sha1(k, d)
131 { return rstr2hex(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); }
132 function b64_hmac_sha1(k, d)
133 { return rstr2b64(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); }
134 function any_hmac_sha1(k, d, e)
135 { return rstr2any(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d)), e); }
138 * Perform a simple self-test to see if the VM is working
140 function sha1_vm_test()
142 return hex_sha1("abc").toLowerCase() == "a9993e364706816aba3e25717850c26c9cd0d89d";
146 * Calculate the SHA1 of a raw string
148 function rstr_sha1(s)
150 return binb2rstr(binb_sha1(rstr2binb(s), s.length * 8));
154 * Calculate the HMAC-SHA1 of a key and some data (raw strings)
156 function rstr_hmac_sha1(key, data)
158 var bkey = rstr2binb(key);
159 if(bkey.length > 16) bkey = binb_sha1(bkey, key.length * 8);
161 var ipad = Array(16), opad = Array(16);
162 for(var i = 0; i < 16; i++)
164 ipad[i] = bkey[i] ^ 0x36363636;
165 opad[i] = bkey[i] ^ 0x5C5C5C5C;
168 var hash = binb_sha1(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
169 return binb2rstr(binb_sha1(opad.concat(hash), 512 + 160));
173 * Convert a raw string to a hex string
175 function rstr2hex(input)
177 try { hexcase } catch(e) { hexcase=0; }
178 var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
179 var output = "";
180 var x;
181 for(var i = 0; i < input.length; i++)
183 x = input.charCodeAt(i);
184 output += hex_tab.charAt((x >>> 4) & 0x0F)
185 + hex_tab.charAt( x & 0x0F);
187 return output;
191 * Convert a raw string to a base-64 string
193 function rstr2b64(input)
195 try { b64pad } catch(e) { b64pad=''; }
196 var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
197 var output = "";
198 var len = input.length;
199 for(var i = 0; i < len; i += 3)
201 var triplet = (input.charCodeAt(i) << 16)
202 | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
203 | (i + 2 < len ? input.charCodeAt(i+2) : 0);
204 for(var j = 0; j < 4; j++)
206 if(i * 8 + j * 6 > input.length * 8) output += b64pad;
207 else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
210 return output;
214 * Convert a raw string to an arbitrary string encoding
216 function rstr2any(input, encoding)
218 var divisor = encoding.length;
219 var remainders = Array();
220 var i, q, x, quotient;
222 /* Convert to an array of 16-bit big-endian values, forming the dividend */
223 var dividend = Array(Math.ceil(input.length / 2));
224 for(i = 0; i < dividend.length; i++)
226 dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
230 * Repeatedly perform a long division. The binary array forms the dividend,
231 * the length of the encoding is the divisor. Once computed, the quotient
232 * forms the dividend for the next step. We stop when the dividend is zero.
233 * All remainders are stored for later use.
235 while(dividend.length > 0)
237 quotient = Array();
238 x = 0;
239 for(i = 0; i < dividend.length; i++)
241 x = (x << 16) + dividend[i];
242 q = Math.floor(x / divisor);
243 x -= q * divisor;
244 if(quotient.length > 0 || q > 0)
245 quotient[quotient.length] = q;
247 remainders[remainders.length] = x;
248 dividend = quotient;
251 /* Convert the remainders to the output string */
252 var output = "";
253 for(i = remainders.length - 1; i >= 0; i--)
254 output += encoding.charAt(remainders[i]);
256 /* Append leading zero equivalents */
257 var full_length = Math.ceil(input.length * 8 /
258 (Math.log(encoding.length) / Math.log(2)))
259 for(i = output.length; i < full_length; i++)
260 output = encoding[0] + output;
262 return output;
266 * Encode a string as utf-8.
267 * For efficiency, this assumes the input is valid utf-16.
269 function str2rstr_utf8(input)
271 var output = "";
272 var i = -1;
273 var x, y;
275 while(++i < input.length)
277 /* Decode utf-16 surrogate pairs */
278 x = input.charCodeAt(i);
279 y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
280 if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
282 x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
283 i++;
286 /* Encode output as utf-8 */
287 if(x <= 0x7F)
288 output += String.fromCharCode(x);
289 else if(x <= 0x7FF)
290 output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
291 0x80 | ( x & 0x3F));
292 else if(x <= 0xFFFF)
293 output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
294 0x80 | ((x >>> 6 ) & 0x3F),
295 0x80 | ( x & 0x3F));
296 else if(x <= 0x1FFFFF)
297 output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
298 0x80 | ((x >>> 12) & 0x3F),
299 0x80 | ((x >>> 6 ) & 0x3F),
300 0x80 | ( x & 0x3F));
302 return output;
306 * Encode a string as utf-16
308 function str2rstr_utf16le(input)
310 var output = "";
311 for(var i = 0; i < input.length; i++)
312 output += String.fromCharCode( input.charCodeAt(i) & 0xFF,
313 (input.charCodeAt(i) >>> 8) & 0xFF);
314 return output;
317 function str2rstr_utf16be(input)
319 var output = "";
320 for(var i = 0; i < input.length; i++)
321 output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
322 input.charCodeAt(i) & 0xFF);
323 return output;
327 * Convert a raw string to an array of big-endian words
328 * Characters >255 have their high-byte silently ignored.
330 function rstr2binb(input)
332 var output = Array(input.length >> 2);
333 for(var i = 0; i < output.length; i++)
334 output[i] = 0;
335 for(var i = 0; i < input.length * 8; i += 8)
336 output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
337 return output;
341 * Convert an array of big-endian words to a string
343 function binb2rstr(input)
345 var output = "";
346 for(var i = 0; i < input.length * 32; i += 8)
347 output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
348 return output;
352 * Calculate the SHA-1 of an array of big-endian words, and a bit length
354 function binb_sha1(x, len)
356 /* append padding */
357 x[len >> 5] |= 0x80 << (24 - len % 32);
358 x[((len + 64 >> 9) << 4) + 15] = len;
360 var w = Array(80);
361 var a = 1732584193;
362 var b = -271733879;
363 var c = -1732584194;
364 var d = 271733878;
365 var e = -1009589776;
367 for(var i = 0; i < x.length; i += 16)
369 var olda = a;
370 var oldb = b;
371 var oldc = c;
372 var oldd = d;
373 var olde = e;
375 for(var j = 0; j < 80; j++)
377 if(j < 16) w[j] = x[i + j];
378 else w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
379 var t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
380 safe_add(safe_add(e, w[j]), sha1_kt(j)));
381 e = d;
382 d = c;
383 c = bit_rol(b, 30);
384 b = a;
385 a = t;
388 a = safe_add(a, olda);
389 b = safe_add(b, oldb);
390 c = safe_add(c, oldc);
391 d = safe_add(d, oldd);
392 e = safe_add(e, olde);
394 return Array(a, b, c, d, e);
399 * Perform the appropriate triplet combination function for the current
400 * iteration
402 function sha1_ft(t, b, c, d)
404 if(t < 20) return (b & c) | ((~b) & d);
405 if(t < 40) return b ^ c ^ d;
406 if(t < 60) return (b & c) | (b & d) | (c & d);
407 return b ^ c ^ d;
411 * Determine the appropriate additive constant for the current iteration
413 function sha1_kt(t)
415 return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
416 (t < 60) ? -1894007588 : -899497514;
420 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
421 * to work around bugs in some JS interpreters.
423 function safe_add(x, y)
425 var lsw = (x & 0xFFFF) + (y & 0xFFFF);
426 var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
427 return (msw << 16) | (lsw & 0xFFFF);
431 * Bitwise rotate a 32-bit number to the left.
433 function bit_rol(num, cnt)
435 return (num << cnt) | (num >>> (32 - cnt));
437 </script>
439 </head>
440 <body>
441 <h1 align=CENTER>Example of Login procedure</h1>
442 <h2 align=CENTER><div id="WARNING" style="color: Red">You need to have JavaScript and cookies enabled to use the login system</div></h2>
444 Simple and very unsafe example login page for CGIscriptor.pl. The password is first hashed with the
445 site specific salt (as it is used to store the password on-site). Then it is hashed with a random,
446 one-time salt. Effectively, creating a one-time password. Only the last value is send to the server.
447 The server has both salt values stored. It will ignore anything except the username, hashed password, and
448 loginticket.
449 </p>
451 The Session Ticket information is stored in in <a href="http://www.xul.fr/en/html5/sessionstorage.php">
452 <em>sessionStorage</em></a> with key <em>CGIscriptorPRIVATE</em>. Older browsers might not implement
453 <a href="http://www.xul.fr/en/html5/sessionstorage.php"><em>sessionStorage</em></a>, or it might be
454 turned off in the <a href="http://kb.mozillazine.org/Dom.storage.enabled"><em>dom.storage.enabled</em></a>
455 parameter.
456 </p>
457 <form method="POST" action="" id="LoginForm"
458 onSubmit='var success=check_username_password();SetSessionCookie();HashPassword("<SCRIPT TYPE="text/ssperl">
459 $RANDOMSALT</SCRIPT>");success'>
460 <div style="margin-left: 30%; margin-right: 30%; text-align: left">
461 Username: <input type="text" name="USERNAME" id="USERNAME" size="20" /><br />
462 Password: <input type="PASSWORD" name="PASSWORD" id="PASSWORD" size="20" /><br />
463 <input type="hidden" name="SALT" id="SALT" value="<SCRIPT TYPE="text/ssperl">$SERVERSALT</SCRIPT>" /><br />
464 <input type="hidden" name="LOGINTICKET" value="<SCRIPT TYPE="text/ssperl">$LOGINTICKET</SCRIPT>" /><br />
465 </div>
466 <div align="center">
467 <p><input type="submit" value="Login" /></p>
468 </div>
469 </form>
471 There are three default test accounts, all three have password <em>test</em>:
472 <ul>
473 <li>test: A SESSION ticket account</li>
474 <li>testip: An IPADDRESS ticket account</li>
475 <li>testchallenge: A CHALLENGE ticket account</li>
476 </ul>
477 </p>
480 The Salt and Ticket values are all created using SHA1 on 32 Byte of output from <em>/dev/urandom</em> in HEX.
481 </p>
482 <FONT STYLE="font-size:small">
483 <p> Example Login page for CGIscriptor.pl<br />
484 Copyright &copy; 2012 R.J.J.H. van Son<br />
485 This program is free software: you can redistribute it and/or modify
486 it under the terms of the GNU General Public License as published by
487 the Free Software Foundation, either version 3 of the License, or
488 (at your option) any later version.
489 This program is distributed in the hope that it will be useful,
490 but WITHOUT ANY WARRANTY; without even the implied warranty of
491 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
492 GNU General Public License for more details.<br />
493 You should have received a copy of the GNU General Public License
494 along with this program. If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>.</p>
495 <p> JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1<br />
496 Copyright &copy; 2000 - 2009 Paul Johnston, Version 2.2<br />
497 Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet<br />
498 Distributed under the BSD License<br />
499 See <a href="http://pajhome.org.uk/crypt/md5">http://pajhome.org.uk/crypt/md5</a> for details.
500 </FONT>
502 </body>
503 </html>