Vulnerability in my system "forgot password"

Asked

Viewed 1,151 times

0

I created a system in case the user forgets the password, but I have doubts about one thing... HTML+PHP System

<html>
<head>
<link rel="icon" href="favicon-16.png" sizes="16x16">
<link rel="icon" href="favicon-32.png" sizes="32x32">
<meta charset="UTF-8">
<title> ::RECUPERAR SENHA:: </title>
</head>
<body>
<link href="css/forget.css" rel="stylesheet">
<script type="text/javascript" src="js/knautiluzPassMathFramework.js"></script>
<script src='https://www.google.com/recaptcha/api.js'></script>
<div id="menu"></div>
<div id="resetSenha">Insira aqui o seu e-mail:</div>
<form name="botaoy" action="" method="post">
<br>
<input type="hidden" name="password" id="password" value="none"></input>
<br>
<input type="email" required placeholder="E-mail" name="emailReset" id="emailReset" pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,3}$">
<br>
<input type="text" required placeholder="USUARIO" name="usernameReset" title="No minimo 3, no máximo 10 letras MAIÚSCULAS" id="usernameReset" pattern="[A-Z]{3,}" maxlength="10"></input>
<br>
<input type="date" required  name="birthdayReset" id="birthdayReset" min="1915-01-01" max="2006-01-01"> 
<div class="g-recaptcha" data-sitekey="6LeSEBwTAAAAAOD2kcTBvz8401DSvI5RTbtG79xK"></div>
<input onClick="knautiluzPassMathFramework();" type="submit" name="botaoy" id="gologin" value="⟳"/>
<br>
</form>
</body>
<footer></footer>
</html> 
 <?php
if(isset($_POST["botaoy"])) {

if (isset($_POST['g-recaptcha-response'])) {
    $captcha_data = $_POST['g-recaptcha-response'];
}

if (!$captcha_data) {
echo "<span id=\"captchaError\">Complete o reCAPTCHA</span>";
    return true;
}
$resposta = file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=meucodigo&response=".$captcha_data."&remoteip=".$_SERVER['REMOTE_ADDR']);
if ($resposta.success) {

require ("includes/connection.php");
require ("includes/start-session.php");

$email          = mysqli_real_escape_string($mysqli, $_POST["emailReset"]); 
$username       = mysqli_real_escape_string($mysqli, $_POST["usernameReset"]);
$birthday       = mysqli_real_escape_string($mysqli, $_POST["birthdayReset"]);
$password       = mysqli_real_escape_string($mysqli, $_POST["password"]); 
$sql = $mysqli->query("SELECT * FROM data WHERE email='$email'");
$get = $sql->fetch_array();
$db_email    = $get['email'];
$db_username = $get['username'];
$db_birthday = $get['birthday'];

if ($email != $db_email || $username != $db_username || $birthday != $db_birthday) {
echo "<span id=\"msgOne\">Dados incorretos.</span>";
return true;
} else {
$sql = $mysqli->query("UPDATE data SET password = '".md5($password)."'  WHERE email =  '$email'");

$sendEmail = $mysqli->query("SELECT * FROM data WHERE email='$emailReset'");
$row = $sendEmail->num_rows;
$get = $sendEmail->fetch_array();
$assunto     = "Sua senha foi alterada!";
$emailz  = $_POST["emailReset"];
$mensagem    = 'Olá! alteramos sua senha temporariamente! Mude ela através do painel de usuário.<br>Sua nova senha é: '.$password.'';


$enviar         = "$mensagem";
require ("includes/PHPMailerAutoload.php");
define('GUSER', '[email protected]'); 
define('GPWD', 'senha@exemplo');        

function smtpmailer($para, $de, $de_nome, $assunto, $corpo) { 
    global $error;
    $mail = new PHPMailer();
    $mail->CharSet = 'UTF-8';
    $mail->IsSMTP();        
    $mail->SMTPDebug =0;        
    $mail->SMTPAuth = true;     
    $mail->SMTPSecure = 'tls';  
    $mail->Host = 'meuhost';    
    $mail->Port = 0;        
    $mail->Username = GUSER;
    $mail->Password = GPWD;
    $mail->SetFrom($de, $de_nome);
    $mail->Subject = $assunto;
    $mail->Body = $corpo;
    $mail->IsHTML(true);
    $mail->AddAddress($para);
    if(!$mail->Send()) {
        $error = 'Mail error: '.$mail->ErrorInfo; 
        return false;
    } else {
        $error = 'Mensagem enviada!';
        return true;
    }
}


 if (smtpmailer($emailz, '[email protected]', 'Knautiluz', $assunto, $enviar)) {
echo "<span id=\"msgTwo\">Senha alterada! Verifique seu e-mail com a nova senha.</span>";
 return true;

} else {
if (!empty($error)) echo $error;}}}
}
?>

My question is: the new password will be generated through a javascript by clicking on the Submit button and will be stored in:

<input type="hidden" name="password" id="password"
value="none"></input>

Instead of "None" a password will be entered with lower-case, upper-case and number letters. This password will be taken from PHP $password = mysqli_real_escape_string($mysqli, $_POST["password"]);

And then it’s sent to the user’s email. Basic system print: inserir a descrição da imagem aqui

The user who wants to reset the password will have to enter the e-mail username and date of birth. Even so I imagined that a malicious user with this information could use an Alert or another command to get the password generated in the password input field. Is it possible? There’s a better way than input for me to store the password generated through js?

  • 2

    Why don’t you generate the password directly in PHP? It’s simpler, safer and avoids problems if the user has disabled the JS in the browser.

  • That’s one of the problems I’ve noticed.

  • "Forgot password" system is basically by email. First, show on the screen a piece of the email that will receive the token, which is for the user not to send the token to the wrong place. Then send a link with a single, complex, limited duration token that allows the user to make a new password.

2 answers

3


The vulnerability is beyond that.

I’m no security expert (not to say I’m not an expert on anything, really!), but I’ll list some errors I noticed in about 3 minutes:

INPUT HIDDEN: (Very High Risk)

If there is an inpu called password, you allow the user to send any password.

In other words, if you want to reset your user I simply put:

<input type="hidden" name="password" id="password" value="123456"></input>

Your date of birth can be easily obtained by social engineering, or on your Facebook, as well as email. I haven’t scanned your complete code to see if all the data is being verified.

So your data I switch to the password you want, in this case to 123456.

You can say: But there is Javascript, which will change the value!

1 - No. Simply rename inputs (name, id), for example. Or in this case simply remove the onClick which apparently he is responsible for generating.

2 - No. Even if you create an "ultra-protected" Javascript, you are deducing that the person is using a browser that supports it. That is, using a CURL or a Webdriver (without dynamic content) will ignore Javascript. It will even do the POST you want, no problem at all!

Alternative:

Generate the password via PHP on the server side.

In an ideal scenario, allow the user to choose the password they want and continue reading this topic. =)

Send "raw" password: (High Risk)

Don’t email me the password, just like that. If the person registered a wrong email now "someone else" has access to account, which could be avoided if they did not send the raw password.

Also if the server’s network is monitored/redirected/intercepted, it will expose the password to an attacker.

Alternative:

Do not send password via e-mail.

False confirmation by e-mail: (High Risk)

If you changed the password, as in method number 1, the user does not need to confirm the password or the change via email. There is no link for the user to confirm the change or claim to be (or not) aware of the modification. So there is no password change confirmation!

Alternative:

1 - Send a link (no password) containing a link to "Click here to confirm your password change, requested in {data} by {IP} using {BROWSER}". This email should expire in a short time (no more than 24 hours, but I believe it should become useless within a maximum of 1 hour after you request it). The link should contain no trace of the password!

Ideally, the link CAN ONLY BE VALIDATED USING THE SAME BROWSER AND/OR IP! That is, if the user used Chrome on 1.2.3.4, only 1.2.3.4 can accept the change. So if someone has access to the link improperly they will not be able to change the password, because they are not using the same computer/network that they requested.

2 - Send an SMS (this is paid for) or use F2A so that the user has to confirm the change also using such feature. This way, if the e-mail is hacked it will not be possible to confirm the access. As well as confirm the email (said in the item above) you will have to confirm the F2A.

Account lock: (Low)

In your current system, if I want to "troll" you, I can keep asking for a password reset at all times, making sure you never know what the password is. This is because your system replaces the old password instantly, it prevents you from logging in with your old password.

Assuming there is a "spam" password reset, your password would be modified at any time, which in the end would lock/block access to your account, by not being able to know what is your current password.

As the password is modified immediately without any confirmation, you end up creating a big problem.

Alternative:

Create a table of passwords changed and not "approved" via email, SMS or F2A. This way the user will still have access with the old password.

Ideally, if the user requests the reset and then enters the old password automatically overrides the change, as it has access to the old password. That wouldn’t be possible in your code.

In short: this way the user will still have access using the old password, until it accepts the reset.

Also block if there are many reset requests by a specific user per IP zone.

  • Man, I really like what you said. But still doing all this for me is complicated, I’m beginner in php, js... I have no idea how to create these tokens in php for example, get the browser version and the user ip. But I found it all very interesting. How would I remove the onclick function that generates js side client? I tried but the browser says that it is not possible to save the change to html. When I change without saving and run the operation it follows the saved html procedure.

  • Assuming that the user uses a browser that does not support js will give error in reCAPTCHA, and as php will not validate reCAPTCHA will give error and approach php, preventing password change. Spam passwords: HAHAHAHA! Really, if someone who gets this data wants to control and keep changing the password all the time it would be complicated! But then it would just be me increasing the security of validation with a secret question by!

  • Well, Recaptcha has Noscript support. This displays a token and a form for the user to type, so it supports no javascript. By the way, reCaptcha is already scammed with 'human bots' or really 'software bots', there are several, such as "bypasscaptcha" and "deathbycaptcha.com". So in fact reCaptcha is not a problem to perform automation or action without Javascript. Secret question I don’t think is ideal, because it’s easier to lose than password. If you ask questions like "your father’s middle name", "first school" (...), if you answer correctly (what I don’t do) you fall on the first item.

  • You can disable the "compatibility" of Captcha when there is no Javascript, but either remove the "onClick" in the F12 browser, or monitor through the console, will allow you to choose the password you want or get the generated password. Another possibility is what you said, you can inject a .val() next to Onclick to get the password that is in the field. There is another however, which I have not listed, the dates. If I think you were born between 1990 and 2000, there are 4017 days. So I can create a bot, using captcha automation features, not easy but not so impossible.

0

I would advise you to use in the form only email request and user. When the user submits the form the system checks that user and e-mail in the database create a base64_encode of the email and user and still create a new field in the table to store a microtime() and send as token to the own e-user mail with a link to change password.

The person accessing the email will have to click on this link to confirm that the data is his or her own and then be redirected back to your website to another form where he or she will provide the new password. Then you will change the password from the microtime() data, e-mail and user and then delete the microtime() from the field so that this action can be executed only one view.

  • I believe that is insufficient. The first person with minimum knowledge will be able to see the "base64_encode" and then make a "Decode", so he already has one of the necessary parameters, to predict the link. If you send only the microtime still has the risk, because it can be predictable, if I request MY password now I will receive a microtime(). Therefore, I just need to see an average difference between the time I requested, the connection delay and the time returned to me via email. With this I can request your password and go in trial and error with microtime, easy is not, difficult also not

  • I see no sense in what you say because you will not ask for any password, you will have to reset your password. Understand that the system should not send password but reset password. If the system can send password means that the password is not encrypted in the bank.

  • You can predict the reset link, supposedly sent to the email.

  • "Then you will change the password from the microtime()"data, at first you can see that you use microtime and then just make several attempts, besides you already know " more or less" which was the microtime.

  • You won’t change anything just with microtime() and if you delete microtime() from the field you won’t be able to edit again. It would serve only for a temporary token along with the user. UPDATE user set password = 'new password', microtime = '' WHERE usuario = 'user' AND microtime = 'temporary token';

  • From what I understood: the microtime would be used as a token and this would be sent to the email, correct

  • yes, to the email of anyone who wants to change their own password. you will not be able to change someone else’s data if the token is for your user.

  • So. A malicious person could "predict" what the token would be, since they would be using microtime. The token, for me, must be impossible to predict, using the largest number of letters and numbers "random" and having no link with any username, date, email or anything of the kind. An example: I access at this time is asked to change your password. Until then the email would be sent to YOUR email, I would not have access. However, as I know you use microtime() I can make "several" (because it is limited and is not "so infinite") attempts to find the link sent to your email.

  • I don’t think it’s easy, or fast. But there is a way to forge a link, "guess" which token was sent.

  • I won’t waste any more time arguing since you still don’t understand.

  • Well, maybe you can’t explain it. But I think other people found the same problem: http://stackoverflow.com/questions/15527706/is-using-microtime-to-generate-password-reset-tokens-bad-practice

Show 6 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.