Added REMOTE_HOST to saved login file procedure to protect against MitM attacks
[CGIscriptor.git] / JavaScript / CGIscriptorSession.js
blobfbc3fd4552bb8fa26cfd8bcef9f8de769d525ccd
1 /*
2  *  LICENSE
3  *  
4  *  This program is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU General Public License
6  *  as published by the Free Software Foundation; either version 2
7  *  of the License, or (at your option) any later version.
8  *  
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *  
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330,
17  *  Boston, MA  02111-1307, USA.
18  * 
19  *  Copyright 2012-2013 Rob van Son 
20  *  email: R.J.J.H.vanSon@gmail.com 
21  *  NKI-AVL Amsterdam
22  */
23 // Global variables, set by the server as CGI parameter values
24 var CGIscriptorSessionType="<SCRIPT TYPE="text/ssperl" CGI='$SESSIONTYPE=""'>
25         $SESSIONTYPE;</SCRIPT>";
26 var CGIscriptorChallengeTicket="<SCRIPT TYPE="text/ssperl" CGI='$CHALLENGETICKET=""'>
27         $CHALLENGETICKET;</SCRIPT>";
29 var CGIscriptorLoginticket="<SCRIPT TYPE='text/ssperl' CGI='$LOGINTICKET=""'>
30         $LOGINTICKET</SCRIPT>";
31 var CGIscriptorServerSalt="<SCRIPT TYPE='text/ssperl' CGI='$SERVERSALT=""'>
32         $SERVERSALT</SCRIPT>";
33 var CGIscriptorRandomSalt="<SCRIPT TYPE='text/ssperl' CGI='$RANDOMSALT=""'>
34         $RANDOMSALT</SCRIPT>";
36 // OnSubmit functions
37 function LoginSubmit () {
38         var success=check_username_password();
39         // Set the LOGINTICKET value in FORM
40         var formID = document.getElementById("LOGINTICKET");
41         var ipaddress = document.getElementById("CLIENTIPADDRESS").value;
42         if(formID) {
43                 formID.value = CGIscriptorLoginticket;
44         };
45         
46         SetSessionCookie();
47         HashPassword(ipaddress+CGIscriptorRandomSalt);
48         hidePasswords();
49         return success;
52 function ChangePasswordSubmit () {
53         if(! check_password_fields())return false;
54         // Set the LOGINTICKET value in FORM
55         var formID = document.getElementById("LOGINTICKET");
56         if(formID) {
57                 formID.value = CGIscriptorLoginticket;
58         };
59         
60         EncryptNewPassword("CGIUSERNAME");
61         HashPassword(CGIscriptorRandomSalt);
62         hidePasswords();
63         return true;
66 function CreateUserSubmit () {
67         if(! check_password_fields())return false;
68         // Set the LOGINTICKET value in FORM
69         var formID = document.getElementById("LOGINTICKET");
70         if(formID) {
71                 formID.value = CGIscriptorLoginticket;
72         };
73         
74         EncryptNewPassword("NEWUSERNAME");
75         HashPassword(CGIscriptorRandomSalt);
76         hidePasswords();
77         return true;
80 // Function definitions
81 function hex_sha1 (plaintext) {
82         var shaObj = new jsSHA(plaintext, "ASCII");
83         return shaObj.getHash("SHA-1", "HEX");
85 function hex_sha256 (plaintext) {
86         var shaObj = new jsSHA(plaintext, "ASCII");
87         return shaObj.getHash("SHA-256", "HEX");
89 function hex_sha512 (plaintext) {
90         var shaObj = new jsSHA(plaintext, "ASCII");
91         return shaObj.getHash("SHA-256", "HEX");
93 function chained_sha (plaintext) {
94         return hex_sha256( hex_sha256( hex_sha512(plaintext) ) );
97 function loadSessionData (SessionType, ChallengeTicket) {
98         if(SessionType == 'CHALLENGE') 
99                 setChallengeParameters(ChallengeTicket);
100         else if(SessionType == 'SESSION')
101                 setSessionParameters();
102         return SessionType;
105 function createCookie(name,value,days,path) {
106         if (days) {
107                 var date = new Date();
108                 date.setTime(date.getTime()+(days*24*60*60*1000));
109                 var expires = "; expires="+date.toGMTString();
110         }
111         else var expires = "";
112         var match = document.cookie.match('/('+name+'\=[^\;]*\);/');
113         if(match){
114                 while(match) {
115                         document.cookie = document.cookie.replace(match[1], name+"="+value);
116                         match = document.cookie.match('/('+name+'\=[^\;]*\);/');
117                 };
118         } else {
119                 document.cookie = name+"=-";
120                 document.cookie = name+"="+value+expires+"; path=/"+path;
121         };
125 function readCookie(name) {
126         var nameEQ = name + "=";
127         var ca = document.cookie.split(';');
128         for(var i=0;i < ca.length;i++) {
129                 var c = ca[i];
130                 while (c.charAt(0)==' ') c = c.substring(1,c.length);
131                 if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
132         }
133         return null;
136 function eraseCookie(name) {
137         createCookie(name,"",-1);
140 // Combine the PASSWORD with the site SERVERSALT and hash it
141 // Combine this Hash iwth the extra SERVERSALT, and hash them
142 function HashPassword(extsalt) {
143         var hash = HashSessionSeed(extsalt);
144         var password = document.getElementById('PASSWORD');
145         if(password){
146                 password.value = hash;
147         } else {
148                 alert("NO PASSWORD IN FORM");
149                 return 0;
150         };
151         return hash;
154 // REMEMBER: Set the session cookie BEFORE you hash the password!!!
155 function SetSessionCookie() {
156         var loginticket = CGIscriptorLoginticket;
157         var randomsalt = CGIscriptorRandomSalt;
158         var hash = HashSessionSeed(loginticket);
159         // Dom.storage.enabled must be set!
160         if (!sessionStorage || typeof(sessionStorage) == 'undefined' ) {
161                 alert('Your browser does not support HTML5 sessionStorage. Set Dom.storage.enabled or try upgrading.');
162                 return 0;
163         } 
164         else sessionStorage.setItem("CGIscriptorPRIVATE", hash);
165         
166         // Store a secret key, if one is given
167         SetSecretKey();
168         
169         return hash;
172 function SetSecretKey() {
173         var loginticket = CGIscriptorLoginticket;
174         var randomsalt = CGIscriptorRandomSalt;
175         var secretkey = ""; 
176         if (!sessionStorage || typeof(sessionStorage) == 'undefined' ) {
177                 alert('Your browser does not support HTML5 sessionStorage. Set Dom.storage.enabled or try upgrading.');
178                 return "";
179         } 
180         else if (loginticket && randomsalt) {
181                 secretkey = HashSessionSeed(loginticket+randomsalt);
182                 sessionStorage.setItem("CGIscriptorSECRET", secretkey);
183         };
184         
185         return secretkey;
188 // Hash(sessionseed+hash(password+username.toLowerCase()+salt))
189 function HashSessionSeed(sessionseed) {
190         var hash1 = "";
191         var hash2 = "";
192         var passwordvalue = document.getElementById('PASSWORD');
193         var saltvalue = CGIscriptorServerSalt;
194         var username = document.getElementById('CGIUSERNAME');
195         hash1 = hex_sha256(passwordvalue.value+username.value.toLowerCase()+saltvalue);
196         if(sessionseed != "")
197                 hash2 = hex_sha256(hash1+sessionseed);
198         else
199                 hash2 = hash1;
200         return hash2;
203 // Remember to hash the repeat too! Or else it will be send in the clear
204 function HashNewPassword(userid) {
205         var hash1 = "";
206         var newpassword = document.getElementById('NEWPASSWORD');
207         var newpasswordrep = document.getElementById('NEWPASSWORDREP');
208         var username = document.getElementById(userid);
209         if(newpassword.value == "" ) {
210                 newpassword.value = "";
211                 return 0;
212         };
213         if(newpasswordrep && (newpasswordrep.value == ""|| newpassword.value != newpasswordrep.value)) {
214                 newpassword.value = "";
215                 newpasswordrep.value = "";
216                 return 0;
217         };
218         var saltvalue = CGIscriptorServerSalt;
219         hash1 = hex_sha256(newpassword.value+username.value.toLowerCase()+saltvalue);
220         newpassword.value = hash1;
221         newpasswordrep.value = hash1;
222         return hash1;
225 function XOR_hex_strings(hex1, hex2) {
226         var resultHex = "";
227         var maxlength = Math.max(hex1.length, hex2.length);
229         for(var i=0; i < maxlength; ++i) {
230                 var h1 = hex1.charAt(i);
231                 if(! h1) h1='0';
232                 var h2 = hex2.charAt(i);
233                 if(! h2) h2 ='0';
234                 var d1 = parseInt(h1,16);
235                 var d2 = parseInt(h2,16);
236                 var resultD = d1^d2;
237                 resultHex = resultHex+resultD.toString(16);
238         };
239         return resultHex;
242 function EncryptNewPassword(userid) {
243         var newpassword = document.getElementById('NEWPASSWORD');
244         var newpasswordrep = document.getElementById('NEWPASSWORDREP');
245         var secretkey = SetSecretKey();
246         
247         // This hashes the newpassword field!
248         HashNewPassword(userid);
249         var encrypted = XOR_hex_strings(secretkey, newpassword.value);
250         newpassword.value = encrypted;
251         newpasswordrep.value = encrypted;
252         return encrypted;
255 function DecryptNewPassword(key, encrypted) {
256         decrypted = XOR_hex_strings(key, encrypted);
257         
258         return decrypted;
261 function add_cgiparam(elem, attr, param) {
262     var elems = document.getElementsByTagName(elem);
263     for (var i = 0; i < elems.length; i++)
264     {
265                 var n=elems[i][attr].indexOf("?");
266                 if(n<0)
267                         elems[i][attr] = elems[i][attr] + "?" + param;
268                 else
269                         elems[i][attr] = elems[i][attr] + "&" + param;
270     };
273 function setSessionParameters() {
274         var cgiScriptorPRIVATE = sessionStorage.getItem("CGIscriptorPRIVATE");
275         // Use existing cookie
276         if(! cgiScriptorPRIVATE) return true;
277         
278         var sessionset = readCookie("CGIscriptorSESSION");
279         if(!(sessionset && sessionset.match(/[\S]/)))return false;
281         var sessionticket = "";
282         sessionticket = hex_sha256(cgiScriptorPRIVATE);
283         sessionticket = hex_sha256(sessionticket+cgiScriptorPRIVATE);
284         if(!sessionticket) return false;
285         createCookie("CGIscriptorSESSION",sessionticket, 0, "");
286         
287         // Without cookies, use this
288         // var sessionparm = document.getElementById('SESSIONTICKET');
289         // if(sessionparm) sessionparm.value = sessionticket;
290     // add_cgiparam('a', 'href', "SESSIONTICKET="+sessionticket);
291     // add_cgiparam('form', 'action', "SESSIONTICKET="+sessionticket);
292     
293     // UNCOMMENT for use in a local version of the Private/Login.html web page.
294     // add_cgiparam('form', 'action', "SETCGISESSIONCOOKIE="+sessionticket);
295     
296         return true;
298 function setChallengeParameters(sessionset) {
299         if(!(sessionset && sessionset.match(/[\S]/)))return false;
300         
301         var sessionticket = "";
302         var sessionkey = sessionStorage.getItem("CGIscriptorPRIVATE");
303         if(!sessionkey) return false;
304         sessionticket = hex_sha256(sessionkey+sessionset);
305         createCookie("CGIscriptorCHALLENGE",sessionticket, 0, "");
307         // Without cookies, use this
308         // var sessionparm = document.getElementById('CHALLENGETICKET');
309         // if(sessionparm) sessionparm.value = sessionticket;
310         
311         // add_cgiparam('a', 'href', "CHALLENGETICKET="+sessionticket);
312         // add_cgiparam('form', 'action', "CHALLENGETICKET="+sessionticket);
313         return true;
316 function clear_persistent_data () {
317         createCookie("CGIscriptorSESSION","", 0, "");
318         createCookie("CGIscriptorCHALLENGE","", 0, "");
319         sessionStorage.setItem("CGIscriptorPRIVATE", "");
320         return true;
323 function check_password_fields ( ) {
324         var newpassword = document.getElementById('NEWPASSWORD');
325         var newpasswordrep = document.getElementById('NEWPASSWORDREP');
326         if(newpassword.value == "" || newpasswordrep.value == "") {
327                 alert("No passwords");
328                 return false;
329         };
330         if(newpassword.value == newpasswordrep.value) {
331                 var submitbutton = document.getElementById('SUBMIT');
332                 submitbutton.style.color = "Black";
333                 return true;
334         };
335         alert("Passwords differ");
336         return false;
339 function check_username_password ( ) {
340         var username = document.getElementById('CGIUSERNAME');
341         var password = document.getElementById('PASSWORD');
342         if(username.value.match(/[a-zA-Z0-9]/) && password.value.match(/[a-zA-Z0-9]/))
343                 return true;
344         alert("Please enter a user name and password");
345         return false;
348 function revealPasswords () {
349         var inputs = document.getElementsByTagName("input");
350         for (i=(inputs.length-1); i>=0; i--) {
351                 var curr = inputs[i];
352                 if (curr.type.toLowerCase()=="password") {
353                         curr.type = "TEXT";
354                 };
355         };
356         
359 function hidePasswords () {
360         var inputs = document.getElementsByTagName("input");
361         for (i=(inputs.length-1); i>=0; i--) {
362                 var curr = inputs[i];
363                 if (curr.type.toLowerCase()=="text") {
364                         curr.type = "PASSWORD";
365                 };
366         };
367         
370 function togglePasswords (hide, show, value) {
371         if(value.match(hide)) {
372                 hidePasswords ();
373                 return value.replace(hide, show);
374         } else {
375                 revealPasswords ();
376                 return value.replace(show, hide);
377         };
380 // Get a loginticket, salt, and random salt from a hidden loginFrame
381 // For use in a local version of the Private/Login.html web page.
382 // UNFINISHED WORK only useful with IPADDRESS sessions
383 // Put the following line in your local version of the HTML page to activate
384 // Replace http://localhost:8080/Private/index.html with the correct URL of the login page
385 // <iFrame id="loginFrame" src="http://localhost:8080/Private/index.html" hidden>Login frame</iFrame>
387 function getLoginData(){
388         var frameID = document.getElementById("loginFrame");
389         var iFrameHeader;
390         var iFrameBody;
391         if ( frameID.contentDocument ) 
392         { // FF
393           iFrameHeader = frameID.contentDocument.getElementsByTagName('head')[0];
394           iFrameBody = frameID.contentDocument.getElementsByTagName('body')[0];
395           iFrameHTML = frameID.contentDocument.getElementsByTagName('html')[0];
396         }
397         else if ( frameID.contentWindow ) 
398         { // IE
399           iFrameHeader = frameID.contentWindow.document.getElementsByTagName('head')[0];
400           iFrameBody   = frameID.contentWindow.document.getElementsByTagName('body')[0];
401           iFrameHTML   = frameID.contentWindow.document.getElementsByTagName('html')[0];
402         }
403         
404         // Login ticket
405         var myLoginexp = /CGIscriptorLoginticket="([a-f0-9]{64})"/;
406         var myLogin = iFrameHeader.innerHTML.match(myLoginexp);
407         if(myLogin) {
408                 CGIscriptorLoginticket = myLogin[1];
409         } else {
410                 alert("Login not possible: Are you already logged in?\nLogin ticket missing");
411         };      
412         
413         // Server Salt
414         var mySaltexp = /CGIscriptorServerSalt="([a-f0-9]{64})"/;
415         var mySalt = iFrameHeader.innerHTML.match(mySaltexp);
416         if(mySalt){ 
417                 CGIscriptorServerSalt = mySalt[1];
418         } else if(myLogin) {
419                 alert("Login not possible: Are you already logged in?\nServer salt missing");
420         };      
422         // Random Salt
423         var myRandomexp = /CGIscriptorRandomSalt="([a-f0-9]{64})"/;
424         var myRandom = iFrameHeader.innerHTML.match(myRandomexp);
425         if(myRandom){ 
426                 CGIscriptorRandomSalt = myRandom[1];
427         } else if(myLogin && mySalt) {
428                 alert("Login not possible: Are you already logged in?\nRandom salt missing");
429         };      
430         
431         // REMOTE_HOST IP ADDRESS
432         var myRemoteHostExp = /id=['"]*CLIENTIPADDRESS['"]* value=['"]([a-f0-9\.\:]+)['"]/;
433         var myRemoteHost = iFrameBody.innerHTML.match(myRemoteHostExp);
434         if(myRemoteHost){
435                 var ipaddress = document.getElementById("CLIENTIPADDRESS");
436                 ipaddress.value = myRemoteHost[1];
437         };      
438         
439         // Clean out frame
440         iFrameHTML.innerHTML = "<html><head><title>EMPTY</title></head><body><h1>EMPTY</h1></body></html>";