Reorganizing login sessions, cookie support for CGIservlet
[CGIscriptor.git] / Private / Login.html
blobb121d3a13675781d8c74650034ac5f0407bb44c4
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 localStorage.setItem("CGIscriptorPRIVATE", hash)
51 return hash;
54 function HashSessionSeed(sessionseed) {
55 var hash1 = "";
56 var hash2 = "";
57 var passwordvalue = document.getElementById('PASSWORD');
58 var saltvalue = document.getElementById('SALT');
59 hash1 = hex_sha1(saltvalue.value+passwordvalue.value);
60 if(sessionseed != "")
61 hash2 = hex_sha1(sessionseed+hash1);
62 else
63 hash2 = hash1;
64 return hash2;
67 function remove_cgiparam(elem, attr, parameter) {
68 var elems = document.getElementsByTagName(elem);
69 for (var i = 0; i < elems.length; i++)
71 var n=elems[i][attr].indexOf("&"+parameter);
72 if(n>0)
73 elems[i][attr] = elems[i][attr].replace("&"+param, "");
74 var n=elems[i][attr].indexOf(parameter);
75 if(n>0)
76 elems[i][attr] = elems[i][attr].replace(param, "");
80 function check_username_password ( ) {
81 var username = document.getElementById('USERNAME');
82 var password = document.getElementById('PASSWORD');
83 if(username.value.match(/[a-zA-Z0-9]/) && password.value.match(/[a-zA-Z0-9]/))
84 return true;
85 alert("Please enter a user name and password");
86 return false;
89 // Remove EVERYTHING from Login window
90 window.onload = function() {
91 if(window.location.search)window.location.search = "";
93 </SCRIPT>
95 <script type="text/javascript">
97 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
98 * in FIPS 180-1
99 * Version 2.2 Copyright Paul Johnston 2000 - 2009.
100 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
101 * Distributed under the BSD License
102 * See http://pajhome.org.uk/crypt/md5 for details.
106 * Configurable variables. You may need to tweak these to be compatible with
107 * the server-side, but the defaults work in most cases.
109 var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
110 var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
113 * These are the functions you'll usually want to call
114 * They take string arguments and return either hex or base-64 encoded strings
116 function hex_sha1(s) { return rstr2hex(rstr_sha1(str2rstr_utf8(s))); }
117 function b64_sha1(s) { return rstr2b64(rstr_sha1(str2rstr_utf8(s))); }
118 function any_sha1(s, e) { return rstr2any(rstr_sha1(str2rstr_utf8(s)), e); }
119 function hex_hmac_sha1(k, d)
120 { return rstr2hex(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); }
121 function b64_hmac_sha1(k, d)
122 { return rstr2b64(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); }
123 function any_hmac_sha1(k, d, e)
124 { return rstr2any(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d)), e); }
127 * Perform a simple self-test to see if the VM is working
129 function sha1_vm_test()
131 return hex_sha1("abc").toLowerCase() == "a9993e364706816aba3e25717850c26c9cd0d89d";
135 * Calculate the SHA1 of a raw string
137 function rstr_sha1(s)
139 return binb2rstr(binb_sha1(rstr2binb(s), s.length * 8));
143 * Calculate the HMAC-SHA1 of a key and some data (raw strings)
145 function rstr_hmac_sha1(key, data)
147 var bkey = rstr2binb(key);
148 if(bkey.length > 16) bkey = binb_sha1(bkey, key.length * 8);
150 var ipad = Array(16), opad = Array(16);
151 for(var i = 0; i < 16; i++)
153 ipad[i] = bkey[i] ^ 0x36363636;
154 opad[i] = bkey[i] ^ 0x5C5C5C5C;
157 var hash = binb_sha1(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
158 return binb2rstr(binb_sha1(opad.concat(hash), 512 + 160));
162 * Convert a raw string to a hex string
164 function rstr2hex(input)
166 try { hexcase } catch(e) { hexcase=0; }
167 var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
168 var output = "";
169 var x;
170 for(var i = 0; i < input.length; i++)
172 x = input.charCodeAt(i);
173 output += hex_tab.charAt((x >>> 4) & 0x0F)
174 + hex_tab.charAt( x & 0x0F);
176 return output;
180 * Convert a raw string to a base-64 string
182 function rstr2b64(input)
184 try { b64pad } catch(e) { b64pad=''; }
185 var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
186 var output = "";
187 var len = input.length;
188 for(var i = 0; i < len; i += 3)
190 var triplet = (input.charCodeAt(i) << 16)
191 | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
192 | (i + 2 < len ? input.charCodeAt(i+2) : 0);
193 for(var j = 0; j < 4; j++)
195 if(i * 8 + j * 6 > input.length * 8) output += b64pad;
196 else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
199 return output;
203 * Convert a raw string to an arbitrary string encoding
205 function rstr2any(input, encoding)
207 var divisor = encoding.length;
208 var remainders = Array();
209 var i, q, x, quotient;
211 /* Convert to an array of 16-bit big-endian values, forming the dividend */
212 var dividend = Array(Math.ceil(input.length / 2));
213 for(i = 0; i < dividend.length; i++)
215 dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
219 * Repeatedly perform a long division. The binary array forms the dividend,
220 * the length of the encoding is the divisor. Once computed, the quotient
221 * forms the dividend for the next step. We stop when the dividend is zero.
222 * All remainders are stored for later use.
224 while(dividend.length > 0)
226 quotient = Array();
227 x = 0;
228 for(i = 0; i < dividend.length; i++)
230 x = (x << 16) + dividend[i];
231 q = Math.floor(x / divisor);
232 x -= q * divisor;
233 if(quotient.length > 0 || q > 0)
234 quotient[quotient.length] = q;
236 remainders[remainders.length] = x;
237 dividend = quotient;
240 /* Convert the remainders to the output string */
241 var output = "";
242 for(i = remainders.length - 1; i >= 0; i--)
243 output += encoding.charAt(remainders[i]);
245 /* Append leading zero equivalents */
246 var full_length = Math.ceil(input.length * 8 /
247 (Math.log(encoding.length) / Math.log(2)))
248 for(i = output.length; i < full_length; i++)
249 output = encoding[0] + output;
251 return output;
255 * Encode a string as utf-8.
256 * For efficiency, this assumes the input is valid utf-16.
258 function str2rstr_utf8(input)
260 var output = "";
261 var i = -1;
262 var x, y;
264 while(++i < input.length)
266 /* Decode utf-16 surrogate pairs */
267 x = input.charCodeAt(i);
268 y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
269 if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
271 x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
272 i++;
275 /* Encode output as utf-8 */
276 if(x <= 0x7F)
277 output += String.fromCharCode(x);
278 else if(x <= 0x7FF)
279 output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
280 0x80 | ( x & 0x3F));
281 else if(x <= 0xFFFF)
282 output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
283 0x80 | ((x >>> 6 ) & 0x3F),
284 0x80 | ( x & 0x3F));
285 else if(x <= 0x1FFFFF)
286 output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
287 0x80 | ((x >>> 12) & 0x3F),
288 0x80 | ((x >>> 6 ) & 0x3F),
289 0x80 | ( x & 0x3F));
291 return output;
295 * Encode a string as utf-16
297 function str2rstr_utf16le(input)
299 var output = "";
300 for(var i = 0; i < input.length; i++)
301 output += String.fromCharCode( input.charCodeAt(i) & 0xFF,
302 (input.charCodeAt(i) >>> 8) & 0xFF);
303 return output;
306 function str2rstr_utf16be(input)
308 var output = "";
309 for(var i = 0; i < input.length; i++)
310 output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
311 input.charCodeAt(i) & 0xFF);
312 return output;
316 * Convert a raw string to an array of big-endian words
317 * Characters >255 have their high-byte silently ignored.
319 function rstr2binb(input)
321 var output = Array(input.length >> 2);
322 for(var i = 0; i < output.length; i++)
323 output[i] = 0;
324 for(var i = 0; i < input.length * 8; i += 8)
325 output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
326 return output;
330 * Convert an array of big-endian words to a string
332 function binb2rstr(input)
334 var output = "";
335 for(var i = 0; i < input.length * 32; i += 8)
336 output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
337 return output;
341 * Calculate the SHA-1 of an array of big-endian words, and a bit length
343 function binb_sha1(x, len)
345 /* append padding */
346 x[len >> 5] |= 0x80 << (24 - len % 32);
347 x[((len + 64 >> 9) << 4) + 15] = len;
349 var w = Array(80);
350 var a = 1732584193;
351 var b = -271733879;
352 var c = -1732584194;
353 var d = 271733878;
354 var e = -1009589776;
356 for(var i = 0; i < x.length; i += 16)
358 var olda = a;
359 var oldb = b;
360 var oldc = c;
361 var oldd = d;
362 var olde = e;
364 for(var j = 0; j < 80; j++)
366 if(j < 16) w[j] = x[i + j];
367 else w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
368 var t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
369 safe_add(safe_add(e, w[j]), sha1_kt(j)));
370 e = d;
371 d = c;
372 c = bit_rol(b, 30);
373 b = a;
374 a = t;
377 a = safe_add(a, olda);
378 b = safe_add(b, oldb);
379 c = safe_add(c, oldc);
380 d = safe_add(d, oldd);
381 e = safe_add(e, olde);
383 return Array(a, b, c, d, e);
388 * Perform the appropriate triplet combination function for the current
389 * iteration
391 function sha1_ft(t, b, c, d)
393 if(t < 20) return (b & c) | ((~b) & d);
394 if(t < 40) return b ^ c ^ d;
395 if(t < 60) return (b & c) | (b & d) | (c & d);
396 return b ^ c ^ d;
400 * Determine the appropriate additive constant for the current iteration
402 function sha1_kt(t)
404 return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
405 (t < 60) ? -1894007588 : -899497514;
409 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
410 * to work around bugs in some JS interpreters.
412 function safe_add(x, y)
414 var lsw = (x & 0xFFFF) + (y & 0xFFFF);
415 var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
416 return (msw << 16) | (lsw & 0xFFFF);
420 * Bitwise rotate a 32-bit number to the left.
422 function bit_rol(num, cnt)
424 return (num << cnt) | (num >>> (32 - cnt));
426 </script>
428 </head>
429 <body>
430 <h1 align=CENTER>Example of Login procedure</h1>
432 Simple and very unsafe example login page for CGIscriptor.pl. The password is first hashed with the
433 site specific salt (as it is used to store the password on-site). Then it is hashed with a random,
434 one-time salt. Effectively, creating a one-time password. Only the last value is send to the server.
435 The server has both salt values stored. It will ignore anything except the username, hashed password, and
436 loginticket.
437 </p>
439 The Session Ticket is stored in a cookie with the name <em>CGIscriptor</em>.
440 </p>
442 <form method="POST" action="" id="LoginForm"
443 onSubmit='var success=check_username_password();SetSessionCookie();HashPassword("<SCRIPT TYPE="text/ssperl">
444 $RANDOMSALT</SCRIPT>");success'>
445 <div style="margin-left: 30%; margin-right: 30%; text-align: left">
446 Username: <input type="text" name="USERNAME" id="USERNAME" size="20" /><br />
447 Password: <input type="PASSWORD" name="PASSWORD" id="PASSWORD" size="20" /><br />
448 <input type="hidden" name="SALT" id="SALT" value="<SCRIPT TYPE="text/ssperl">$SERVERSALT</SCRIPT>" /><br />
449 <input type="hidden" name="LOGINTICKET" value="<SCRIPT TYPE="text/ssperl">$LOGINTICKET</SCRIPT>" /><br />
450 </div>
451 <div align="center">
452 <p><input type="submit" value="Login" /></p>
453 </div>
454 </form>
456 The Salt and Ticket values are all created using SHA1 on 512 Byte of output from <em>/dev/urandom</em> in HEX.
457 </p>
458 <FONT STYLE="font-size:small">
459 <p> Example Login page for CGIscriptor.pl<br />
460 Copyright &copy; 2012 R.J.J.H. van Son<br />
461 This program is free software: you can redistribute it and/or modify
462 it under the terms of the GNU General Public License as published by
463 the Free Software Foundation, either version 3 of the License, or
464 (at your option) any later version.
465 This program is distributed in the hope that it will be useful,
466 but WITHOUT ANY WARRANTY; without even the implied warranty of
467 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
468 GNU General Public License for more details.<br />
469 You should have received a copy of the GNU General Public License
470 along with this program. If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>.</p>
471 <p> JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1<br />
472 Copyright &copy; 2000 - 2009 Paul Johnston, Version 2.2<br />
473 Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet<br />
474 Distributed under the BSD License<br />
475 See <a href="http://pajhome.org.uk/crypt/md5">http://pajhome.org.uk/crypt/md5</a> for details.
476 </FONT>
478 </body>
479 </html>