Here is what the reset link looks like that goes in an email to the user:
www.webdev702.com/forgotPassword.php?empl_id=121&[email protected]&request_time=1432183745918&resetCode=MG4L0aBqim27wK9fAWcJ2XCBF
There are many parts to the link and they are:
empl_id
: the id of the person matching the email address enteredemail
: email address of the userrequest_time
: a UTC time stamp of time the reset password was requestedreset_code
: a random 25 alpha numeric code
The process works by someone entering their email in the forgot password box and hitting send, the system then looks for the user with a matching email to the one that was entered and if found it grabs the employee id (empl_id
). It then creates a random 25 alpha numeric code and gets the current time in a UTC time stamp. It creates a document in a collection call forgot_pass
with the empl_id
, email
, reset_code
and request_time
.
Once the document is created the system sends the user a email with the generated link.
When a user clicks the link in the email they are taken to the site and on page load it grabs the items from the URL. It then looks in the forgot_pass
collection for a document that has a matching empl_id
, email
and reset_code
. It needs all 3 to successfully find the document. If it finds it, it then makes sure that no more then 24 hours have passed since the request was made. If it's been over 24 hours they will get an error message and won't be able to reset password. Also if it can't find the document an error message is displayed and they can't reset.
If all the conditions are correct then the user will be able to reset their password. Upon success of resetting the password the users is taken to the login page and the forgot_pass
document is deleted.
Bad Reset Code Link: www.webdev702.com/forgotPassword.php?empl_id=121&[email protected]&request_time=1432183745918&resetCode=MG4L0aBqim27wK9fbWcJ2XCBF
Past 24 Hours Link: www.webdev702. com/forgotPassword.php?empl_id=121&[email protected]&request_time=1431183745918&resetCode=MG4L0aBqim27wK9fAWcJ2XCBF
The reason I send all the data in the link is because I want to find the document that matches all 3 things. I don't want to just look for a matching code, I think that the chances of someone guessing it is unlikely, but being this will be used by enterprise companies I want it to be as secure as possible.
Would you say that my method of generating a reset link is secure? If not, please tell me what you would change and why.
function sendResetLink(){
empl_email = $('#resetEmail').val();
resetCode = createEncryptCode();
empl_id = getUsersID(empl_email);
requestTime = getRequestTime();
link = "http://www.webdev702.com/forgotPassword.php?empl_id="+empl_id+"&email="+empl_email+"&request_time="+requestTime+"&resetCode="+resetCode;
$.ajax({
url: 'https://api.mongolab.com/api/1/databases/hiperhipo/collections/forgot_pass/?apiKey=Mgh1pWtPjeImQeh7CjsbixpkXG-Xniib',
async: false,
data: JSON.stringify({
"empl_id": empl_id,
"empl_email": empl_email,
"reset_code": resetCode,
"request_time": requestTime
}),
type: "POST",
contentType: "application/json"
}).success(function(returnedSkData) {
sendLinktoUser(empl_email, empl_id, requestTime, resetCode, link);
});
}
function getUsersID(empl_email){
urlUser = 'https://api.mongolab.com/api/1/databases/hiperhipo/collections/users/?apiKey=Mgh1pWtPjeImQeh7CjsbixpkXG-Xniib&q={"email":"'+ empl_email +'"}';
var theID
$.ajax(
{
url: urlUser,
type: "GET",
async: false,
contentType: "application/json"
}
).success(function(returnedUserData) {
theID = returnedUserData[0]["empl_id"];
});
return theID;
}
function createEncryptCode(){
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for( var i=0; i < 25; i++ )
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text;
}
function getRequestTime(){
var x = new Date();
var n = x.getTime();
return n;
}
function getUrlParameter(sParam){
var sPageURL = window.location.search.substring(1);
var sURLVariables = sPageURL.split('&');
for (var i = 0; i < sURLVariables.length; i++)
{
var sParameterName = sURLVariables[i].split('=');
if (sParameterName[0] == sParam)
{
return sParameterName[1];
}
}
}
function checkResetCodeandTime(empl_id, requestTime, resetCode){
urlForgot = 'https://api.mongolab.com/api/1/databases/hiperhipo/collections/forgot_pass/?apiKey=Mgh1pWtPjeImQeh7CjsbixpkXG-Xniib&q={"empl_id":"'+ empl_id +'", "reset_code":"'+resetCode+'"}';
$.ajax(
{
url: urlForgot,
type: "GET",
async: false,
contentType: "application/json"
}
).success(function(returnedUserData) {
curTime = getRequestTime();
timeDif = parseInt(curTime) - parseInt(requestTime);
//console.log(timeDif);
if(timeDif < 86000000){
if(returnedUserData != ""){
}else{
$('#resetForm').hide();
$('.login-form').append('<div class="alert alert-danger" role="alert">The link you clicked is no longer valid.</div>')
}
}else{
$('#resetForm').hide();
$('.login-form').append('<div class="alert alert-danger" role="alert">The link you clicked is no longer valid. The 24 hour reset window has passed.</div>')
}
});
}
function sendLinktoUser(emplemail, emplid, request_time, reset_code, the_link){
var data = {
empl_email: emplemail,
theLink: the_link
};
$.ajax({
type: "POST",
url: "sendForgotPassLink.php",
data: data,
success: function(){
console.log("THE POST WAS SUCCESSFUL")
}
});
}
function newPassword(){
event.preventDefault();
newpass1 = $('#pass1').val();
newpass2 = $('#pass2').val();
empl_id = $('#emplId').val();
urlPass = 'https://api.mongolab.com/api/1/databases/hiperhipo/collections/users/?apiKey=Mgh1pWtPjeImQeh7CjsbixpkXG-Xniib&q={"empl_id":"' + empl_id + '"}';
urlForgot = 'https://api.mongolab.com/api/1/databases/hiperhipo/collections/forgot_pass/?apiKey=Mgh1pWtPjeImQeh7CjsbixpkXG-Xniib&q={"empl_id":"'+ empl_id +'"}';
$.ajax(
{ url: urlPass,
type: "GET",
async: false,
contentType: "application/json"
}
).success(function(returnedPassData) {
if(newpass1 == newpass2){
var newHashPW = $.md5(newpass1);
$.ajax(
{ url: urlPass,
async: false,
data: JSON.stringify( { "$set" : { "password" : newHashPW } } ),
type: "PUT",
contentType: "application/json"
}
).success(function(returnedHData) {
$('#successPWD').show();
$('#resetForm').hide();
$.ajax(
{ url: urlForgot,
async: false,
data: JSON.stringify( { "$set" : { "reset_code" : "DELETED", "empl_id": "DELETED", "empl_email": "DELETED" } } ),
type: "PUT",
contentType: "application/json"
}
).success(function(returnedHData) {
window.location = "/index.php";
})
// window.location = "/index.php";
});
}else{
errormisspassHTML = '<div class="alert alert-danger" role="alert">Please verify your new passwords, their was a mismatch.</div>';
$('#login-form').prepend(errormisspassHTML);
}
});
}
<link rel="stylesheet" href="http://www.webdev702.com/assets/css/plugins/pace.css">
<script src="http://www.webdev702.com/assets/js/pace.min.js"></script>
<link href="http://www.webdev702.com/assets/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="http://www.webdev702.com/assets/css/plugins/bootstrap-switch.min.css">
<link rel="stylesheet" href="http://www.webdev702.com/assets/css/plugins/ladda-themeless.min.css">
<link href="http://www.webdev702.com/assets/css/plugins/humane_themes/bigbox.css" rel="stylesheet">
<link href="http://www.webdev702.com/assets/css/plugins/humane_themes/libnotify.css" rel="stylesheet">
<link href="http://www.webdev702.com/assets/css/plugins/humane_themes/jackedup.css" rel="stylesheet">
<link href="http://www.webdev702.com/assets/css/style.css" rel="stylesheet">
<link href="http://www.webdev702.com/assets/css/responsive.css" rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://www.webdev702.com/assets/js/resetPassword.js"></script>
<script src="http://www.webdev702.com/assets/js/pages/login.js"></script>
<script src="http://www.webdev702.com/assets/js/lib/jquery.easing.js"></script>
<script src="http://www.webdev702.com/assets/js/bootstrap-switch.min.js"></script>
<script src="http://www.webdev702.com/assets/js/bootstrap-switch.min.js"></script>
<body class="login-screen blue-color">
<div class="login-bg"> </div>
<section>
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<div class="login-box">
<div class="login-content">
<h3>Identify Yourself</h3>
</div>
<div class="login-form">
<form action="index.php" method="POST" class="form-horizontal ls_form">
<div class="input-group ls-group-input">
<input class="form-control" type="text" placeholder="Username" name="login">
<span class="input-group-addon"><i class="fa fa-user"></i></span>
</div>
<div class="input-group ls-group-input">
<input type="password" placeholder="Password" name="password"
class="form-control" value="">
<span class="input-group-addon"><i class="fa fa-lock"></i></span>
</div>
<div class="input-group ls-group-input login-btn-box">
<input type="submit" class="btn ls-dark-btn ladda-button col-md-12 col-sm-12 col-xs-12" value="Submit" data-style="slide-down" />
<a class="forgot-password" href="javascript:void(0)">Forgot password</a>
</div>
</form>
</div>
<div class="forgot-pass-box">
<form action="#" class="form-horizontal ls_form">
<div class="input-group ls-group-input">
<input class="form-control" id="resetEmail" type="text" placeholder="[email protected]">
<span class="input-group-addon"><i class="fa fa-envelope"></i></span>
</div>
<div class="input-group ls-group-input login-btn-box">
<button onclick="sendResetLink()" class="btn ls-dark-btn col-md-12 col-sm-12 col-xs-12">
<i class="fa fa-rocket"></i> Send
</button>
<a class="login-view" href="javascript:void(0)">Login</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<p class="copy-right big-screen hidden-xs hidden-sm">
<span>©</span> Hiper Hipo <span class="footer-year">2015</span>
</p>
</section>
</body>
N
tries have passed? When in security land, don't re-invent the wheel! \$\endgroup\$