mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-05-04 14:19:13 -04:00
Modified the authentication stuff to grant access not based on plain password authentication but on a kind of 'timed cookies' so the password is not stored in plain text in some browser cookie.
Also modded some random string generation funtions for elegance.
This commit is contained in:
parent
4fc4a35381
commit
082c732429
3 changed files with 39 additions and 14 deletions
|
@ -489,9 +489,13 @@ Class('Pad', {
|
|||
this.passwordHash = password == null ? null : hash(password, generateSalt());
|
||||
db.setSub("pad:"+this.id, ["passwordHash"], this.passwordHash);
|
||||
},
|
||||
getPasswordSalt: function()
|
||||
{
|
||||
return this.passwordHash.split("$")[1];
|
||||
},
|
||||
isCorrectPassword: function(password)
|
||||
{
|
||||
return compare(this.passwordHash, password)
|
||||
return timeSensitiveCompare(this.passwordHash, password)
|
||||
},
|
||||
isPasswordProtected: function()
|
||||
{
|
||||
|
@ -512,17 +516,21 @@ function hash(password, salt)
|
|||
function generateSalt()
|
||||
{
|
||||
var len = 86;
|
||||
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./";
|
||||
var charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./";
|
||||
var randomstring = '';
|
||||
for (var i = 0; i < len; i++)
|
||||
{
|
||||
var rnum = Math.floor(Math.random() * chars.length);
|
||||
randomstring += chars.substring(rnum, rnum + 1);
|
||||
var rnum = Math.floor(Math.random() * charset.length);
|
||||
randomstring += charset[rnum];
|
||||
}
|
||||
return randomstring;
|
||||
}
|
||||
|
||||
function compare(hashStr, password)
|
||||
/* Compare the timed password hash with the saved value.
|
||||
* If the hash was generated too far in the past, it is rejected. */
|
||||
function timeSensitiveCompare(hashStr, password)
|
||||
{
|
||||
return hash(password, hashStr.split("$")[1]) === hashStr;
|
||||
var timestamp = password.split("$")[1];
|
||||
return password === hash(hashStr, timestamp)
|
||||
&& timestamp - new Date().getTime() > 0;
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
|
|||
var groupID = padID.split("$")[0];
|
||||
var padExists = false;
|
||||
var validSession = false;
|
||||
var pwsalt;
|
||||
var sessionAuthor;
|
||||
var tokenAuthor;
|
||||
var isPublic;
|
||||
|
@ -131,6 +132,9 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
|
|||
|
||||
//is it password protected?
|
||||
isPasswordProtected = pad.isPasswordProtected();
|
||||
|
||||
//get the password salt used by the hash function
|
||||
pwsalt = pad.getPasswordSalt();
|
||||
|
||||
//is password correct?
|
||||
if(isPasswordProtected && password && pad.isCorrectPassword(password))
|
||||
|
@ -162,13 +166,14 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
|
|||
else if(isPasswordProtected && passwordStatus == "wrong")
|
||||
{
|
||||
//--> deny access, ask for new password and tell them that the password is wrong
|
||||
statusObject = {accessStatus: "wrongPassword"};
|
||||
//The salt can be safely shared since it is not secret. It does its job (improving resistence against rainbow table attacks) even when public.
|
||||
statusObject = {accessStatus: "wrongPassword", passwordSalt: pwsalt};
|
||||
}
|
||||
//- the pad is password protected but no password given
|
||||
else if(isPasswordProtected && passwordStatus == "notGiven")
|
||||
{
|
||||
//--> ask for password
|
||||
statusObject = {accessStatus: "needPassword"};
|
||||
statusObject = {accessStatus: "needPassword", passwordSalt: pwsalt};
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -72,7 +72,7 @@ function randomString()
|
|||
for (var i = 0; i < string_length; i++)
|
||||
{
|
||||
var rnum = Math.floor(Math.random() * chars.length);
|
||||
randomstring += chars.substring(rnum, rnum + 1);
|
||||
randomstring += chars[rnum];
|
||||
}
|
||||
return "t." + randomstring;
|
||||
}
|
||||
|
@ -147,10 +147,22 @@ function getUrlVars()
|
|||
return vars;
|
||||
}
|
||||
|
||||
function savePassword()
|
||||
function hash(password, salt)
|
||||
{
|
||||
return sha512(password + salt) + "$" + salt;
|
||||
}
|
||||
|
||||
/* Generate the "timed hash" used to get access.
|
||||
* The password is hashed with the database's salt, afterwards it is hashed again with a timestamp a few days in the future as "salt".
|
||||
* The server checks the two hashe's equality as usual, but also checks whether this timestamp is still in the future (grant access)
|
||||
* or if it has passed (deny access). This provides an saved-password expiry mechanism which is a) independent of the browser's cookie
|
||||
* retention and b) provides some level of security against "cookie stealing" (be it by xss or otherwise): If Eve steals a cookie, she
|
||||
* does "only" get a timed-hash lifetime access to the pad, but *not* the actual password.
|
||||
*/
|
||||
function savePassword(pwsalt)
|
||||
{
|
||||
//set the password cookie
|
||||
createCookie("password",$("#passwordinput").val(),null,document.location.pathname);
|
||||
createCookie("password",hash(hash($("#passwordinput").val(), pwsalt), new Date().getTime() + 14 * 24 * 3600 * 1000),null,document.location.pathname); //FIXME some means of configuring this threshold would be really great
|
||||
//reload
|
||||
document.location=document.location;
|
||||
}
|
||||
|
@ -214,13 +226,13 @@ function handshake()
|
|||
{
|
||||
$("#editorloadingbox").html("<b>You need a password to access this pad</b><br>" +
|
||||
"<input id='passwordinput' type='password' name='password'>"+
|
||||
"<button type='button' onclick='savePassword()'>ok</button>");
|
||||
"<button type='button' onclick='savePassword("+obj.passwordSalt+")'>ok</button>");
|
||||
}
|
||||
else if(obj.accessStatus == "wrongPassword")
|
||||
{
|
||||
$("#editorloadingbox").html("<b>You're password was wrong</b><br>" +
|
||||
$("#editorloadingbox").html("<b>Your password was wrong</b><br>" +
|
||||
"<input id='passwordinput' type='password' name='password'>"+
|
||||
"<button type='button' onclick='savePassword()'>ok</button>");
|
||||
"<button type='button' onclick='savePassword("+obj.passwordSalt+")'>ok</button>");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue