New password different from 3 previous ones - php

So here's the deal: I'd like to force my clients to change their password every 3 months. That's easy.
But I also want them to choose a different password which is different from the previous 3 password.
And the difficult thing is that I'm storing the 4 information (3 last passwords and date of the last password change) in the same row of the db.
So I'm setting row-->old_passwords = date()::psw_1::psw_2::psw_3
And checking if($POST['password']== psw_1 || $POST['password']== psw_2 || $POST['password']== psw_3)
But how do I store the new password as being psw_3, the old one becoming psw_2 and that one psw_1 - ultimately erasing the original psw_1 ?
Don't worry about the CWE-257 - passwords are hashed of course !

You should do an "UPDATE" query, updating the fields with the new passwords ?
Something like this should work, but be sure to protect against mysql injection.
ALSO as said by #Fred -ii- , $POST['password'] is never gonna work, you should read about superglobals. $_POST['password'] will give you a better chance of achieving what you are trying to do ;)
$mysql->query("UPDATE tableStoringPwd pwd SET pwd.psw1 = pwd.psw2 , pwd.psw2 = pwd.psw3, pwd.psw3 = '" . $newPwd . "' WHERE userId = ". $userId );
EDIT :

Related

How to make Mysql Queries not case-sensitive?

I have table which contain [Username, Email] and I'm checking them for non repeating any of them,
but It's case-sensitive , so if there is user that's Username is "SelvsterTP", if other user typed it, he won't be able to register, but if he type "selvstertp" for example, no errors face him! ,
I think of making extra column called 'UsernameCheck' and upload to it Username 'lowercase' , then check on that column (same with Email) ,
but it seems to me not the best code for that situation, so any ideas or suggestions?
Original code
$CheckusernameRow =
RowCountDB("Id","users","Username",$Username); //function
to get rows
My Idea
$CheckusernameRow =
RowCountDB("Id","users","UsernameCheck",strtolower($Username));
Did you try the LOWER() selector?
$lowerUsername = strtolower($Username);
$query = "SELECT id FROM users WHERE LOWER(Username) LIKE '$lowerUsername'";

Security breached via a mysql query which I don't quite understand

I've been working on a legacy website. Recently a user has informed us about a potential security breach.
Long story short, when trying to login and using '=' 'or' as password or a username the following query will get executed.
SELECT * FROM `table-goes-here` WHERE `username` = ''=' 'or'' AND `password` = 'some-hash-goes-here'
This query will select everything in that table and will allow login without any actual valid credentials.
I just maintain the site and I have talked to the owner before about such security leaks, he won't listen.
What I want to know is how exactly is this a valid query and what exactly does it do(preferably step by step, explain it to me like I'm 5 version). My MySQL knowledge isn't the best there could be.
I'm very aware that this is an SQL injection. I know how to prepare statements, but they're not in the budget apparently. I just want to know what it does exactly. Namely this part. I have never seen this syntax before and googling doesn't really help as I don't know what I'm looking for exactly.
`username` = ''=' 'or''
Thing is you are actually running
SELECT * FROM table WHERE (username= '' = '') or ('' AND password="");
and (user_username = '' = '') evaluates to true... try
SELECT (username= '' = '') FROM table;
and ('' AND password="") also evaluates to true... try
Select ('' AND password="") FROM table;
also evaluates to true so... everything is shown
I think it's working like this, first of all evals the
username = '' this returns false or 0 then the other parts come
0 = ' '
as you can see from mysql : https://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#operator_equal
if you compare string with 0 (zero), it returns 1 (true)
AS a result:
SELECT 'blabla' = ''; // returns 0
SELECT 0 = ' '; // returns 1
The query would basically evaluate to true and would return all records.
So, the thing which would happen is that the hacker would be logged in as the user whose ID is stored first in your database.
Of course, it wouldn't show the database to the hacker, because you aren't echoing any of the database content anywhere, but with a little modification the hacker can manage to login as any user, not just the first one.
Read following to prevent such attacks How can I prevent SQL injection in PHP?
More info- http://www.w3schools.com/sql/sql_injection.asp
EDIT- The hacker would also have to use a similar trick in password field because else it would become false.

Crypt function doesn't work when comparing string to hash

I'm using a pretty standard way of cookie login - I give the user two cookies, one with his username and the other with a randomly generated string plus a user-specific salt.
This is what happens at login:
$_SESSION['username']=$row[username];
$_SESSION['user_id']=$row['id'];
$loginhash=generateRandomBase64String()."_".$row['salt'];
$number_of_days = 14;
$date_of_expiry = time() + 60 * 60 * 24 * $number_of_days ;
setcookie( "userlogin", $row['username'], $date_of_expiry, "/" ) ;
setcookie( "loginhash", $loginhash, $date_of_expiry, "/" ) ;
$cryptedhash=crypt($loginhash);
$today=date("Y-m-d");
mysql_query("update members set last_login='$today',loginhash='$cryptedhash' where id='$row[id]' ") or die(mysql_error());
So the $loginhash value is something like Pe0vFou8qe++CqhcJgFtRmoAldpuIs+d_g5oijF76 and the crypted version of that is stored in the database. The salt is already in the database, as it's generated for each user when they sign up.
I use session variables ($_SESSION[username]) to keep users logged in. Then, when a user visits the site, I check for two things: if $_SESSION[username] is not set but $_COOKIE[userlogin] is, I check if the hash is correct so I could log the user in. The problem is, the hash is never correct.
if($_COOKIE['userlogin'] && !isset($_SESSION[user_id])){
$username=mysql_real_escape_string($_COOKIE['userlogin']);
$loginhash=mysql_real_escape_string($_COOKIE['loginhash']);
$salt=substr($loginhash,-8);
$result=mysql_query("select * from members where (username='$username' || email='$username') && salt='$salt' limit 1 ") or die (mysql_error());
$row=mysql_fetch_assoc($result);
$cryptedhash=$row['loginhash'];
if (crypt($loginhash, $cryptedhash) == $cryptedhash){
$_SESSION['username']=$row[username];
$_SESSION['user_id']=$row['id'];
}
}
$_COOKIE[userlogin] is the correct value. When I check for the username/salt combination in the database, the correct result is found (echo $row[username] gives correct value). However, the if condition below that is never met. I would think there's something weird about my PHP configuration, but I use the same crypting mechanism to store passwords and there it works properly.
So can anyone see what's going wrong here?
PS I'm not looking to start a discussion about cookie safety or the variety of available hashing functions here.
Here's the problem:
In your first call to crypt(), you do not specify a salt.
In your second call to crypt(), you pass the $cryptedhash as the salt.
crypt() is documented to generate a random salt if you do not provide one, and then prepend that salt to the returned hash. That has the side effect that if you pass a returned salt+hash as the hash for a subsequent call, crypt() will still pull the correct salt out of it.
Unfortunately the algorithm used and the length/format of the salt+hash is based on a combination of your operating system, PHP version, and whether or not you specified the salt parameter. When you used your code previously, you had the happy accident that DES was chosen in both calls to crypt(). Now, your environment is using a different algorithm for the 2 calls to crypt() since you only supplied the hash in one of them.
The solution is to just pass a consistent salt to both calls to crypt(). You can stop appending the salt to the string you want to hash, and actually pass your user salt as the salt parameter and everything will be fine.
This line is wrong $loginhash=mysql_real_escape_string($_COOKIE['loginhash']); You don't need to escape it (you are not using it with the database), you must use it unaltered when pass it to crypt(), so the line can be writen as $loginhash=$_COOKIE['loginhash']; (at least for this part of code)
If you are using PHP Version 5.5.0 or higher you should take a look at PHPDoc - Password Hashing!
I know you said you don't want to discuss different hashing functions, but I thought this one makes it easyer for you since it should get rid of your problem and is (in my oppinion) way easier to use!
Here would be your code with the new functions: (not tested, if broke pleases comment)
$_SESSION['username']=$row[username];
$_SESSION['user_id']=$row['id'];
$loginhash=generateRandomBase64String()."_".$row['salt'];
$number_of_days = 14;
$date_of_expiry = time() + 60 * 60 * 24 * $number_of_days ;
setcookie( "userlogin", $row['username'], $date_of_expiry, "/" ) ;
setcookie( "loginhash", $loginhash, $date_of_expiry, "/" ) ;
$cryptedhash=password_hash($loginhash, PASSWORD_DEFAULT, array("cost" => 10));
// a cost of 10 is standard, you may want to adjust it according to your hardware (lower/higher cost means faster/slower)
$today=date("Y-m-d");
mysql_query("update members set last_login='$today',loginhash='$cryptedhash' where id='$row[id]' ") or die(mysql_error());
Using PASSWORD_DEFAULT there will make shure that even in future versions the strongest algorithm will always be used.
and
if($_COOKIE['userlogin'] && !isset($_SESSION[user_id])){
$username=mysql_real_escape_string($_COOKIE['userlogin']);
$loginhash=$_COOKIE['loginhash']; // i guess you should not use mysql_real_escape_string
$salt=mysql_real_escape_string(substr($loginhash,-8)); // here would be the place to use it
$result=mysql_query("select * from members where (username='$username' || email='$username') && salt='$salt' limit 1 ") or die (mysql_error());
$row=mysql_fetch_assoc($result);
$cryptedhash=$row['loginhash'];
if (password_verify($loginhash, $cryptedhash)){
$_SESSION['username']=$row[username];
$_SESSION['user_id']=$row['id'];
}
}

What is the best way to store user BIRTHDATES in MySQL/PHP?

What is the best way to store user BIRTHDATES in MySQL/PHP and why?
I'm currently storing it as a VARCHAR(10) in the following format:
MM-DD-YYYY
...but I have a feeling this isn't the best way, especially since I sometimes see invalid years in that field.
Please don't just say what the best method is, also explain why it's the best method so that I can be convinced to change the entire way birthdates are handled on my site, which is a huge undertaking should that be needed.
Thanks.
mySQL has a native field type for dates: DATE.
Please don't just say what the best method is, also explain why it's the best method so that I can be convinced to change the entire way birthdates are handled on my site, which is a huge undertaking should that be needed.
Not sure what further explanation to give :) If you want to store dates in a mySQL table, DATE simply is the one correct choice. It comes with optimized indexing for queries like >, < and BETWEEN, sorts fast, and can deal with any date from the years 1001-9999, so this really is the way to go.
Store it as a DATE type, since it's very efficient for both storage and operations involving the field (filtering by date, etc.).
As others have already answered: MySQL has a DATE format for just dates, but if you need a time and date there's also DATETIME and TIMESTAMP.
DATE requires 3 bytes of storage, DATETIME require 8 bytes, and TIMESTAMP requires 4.
Incidentally, INT also requires 4 bytes. If you're just using a DATE without a corresponding time - like for your birthdays - then it's not really an issue, however there is an article which presents some arguments as to why you might want to avoid using DATETIME, and I think it's worth a read. It's also worth reading over the comments as some people have noted that the suggestions in the article aren't always practical: e.g. query optimiser sometimes has trouble with date functions, and the difficulty storing dates as unix timestamps.
This Is the php part with verification do declare the Err variable to null before.
<?php
if (! isset ( $_POST ['yearOfBirth'] ) || empty ( $_POST ['yearOfBirth'] )) {
$isFormValid = FALSE;
$yearErr = "Please select year";
if (! isset ( $_POST ['monthOfBirth'] ) || empty ( $_POST ['monthOfBirth'] )) {
$isFormValid = FALSE;
$monthErr = "Please select month";
if (! isset ( $_POST ['dayOfBirth'] ) || empty ( $_POST ['dayOfBirth'] )) {
$isFormValid = FALSE;
$dateErr = "Please complete the dob";
$dob = $_POST ['yearOfBirth'] . "-" . $_POST ['monthOfBirth'] . "-" . $_POST ['dayOfBirth'];
// exit(print_r($_POST));
}
}
}
---------------------------
Here is the SQL part.
----------------------------------
$dob = $_POST["yearOfBirth"] . "-" . $_POST["monthOfBirth"] . "-" . $_POST["dayOfBirth"];
$register = "INSERT INTO tablename( dob, )
VALUES('$dob',)";
Hope this helps

prevent duplicated usernames with non-case-sensitive behavior in php/mysql

In my website usernames are saved like this robert and funny thing is that one can registers with the name Robert (Capital R).
How would I prevent these somehow duplicated usernames?
My project is in mysql/php
if (mysql_num_rows(mysql_query("SELECT username FROM user_table WHERE username='$username'")) > 0){
die("duplicated usernames can't be saved , this username exists.");
}
Even with this code Robert can be registered.
You should add a UNIQUE index on the username column.
Also, you may find some useful info here: https://stackoverflow.com/search?q=mysql+case+sensitive
Convert them both to the same case and then compare them.
As such:
SELECT username FROM table WHERE LOWER(username) = LOWER('$username')
Hopefully you've sanitized the user input for the username to prevent SQL injections, after that, use PHP to LC your usernames before checking for duplicates or inserting the name in the database:
$username = strtolower($username);
if (mysql_num_rows(mysql_query("SELECT username FROM user_table WHERE username='$username'")) > 0){
die("duplicated usernames can't be saved , this username exists.");
}
php has very important function but people ignore it in start strtolower , use it at the time of
registering and query hence
$username = strtolower($username);
you will never face such problem again .

Categories