In an app i am making i need to edit and create some passwords with haste and i think i have found a way but i doubt its correctness.
I dug through ion auth and found this function in the ion_auth_model
/**
* Hashes the password to be stored in the database.
*
* #return void
* #author Mathew
**/
public function hash_password($password, $salt=false, $use_sha1_override=FALSE)
{
if (empty($password))
{
return FALSE;
}
//bcrypt
if ($use_sha1_override === FALSE && $this->hash_method == 'bcrypt')
{
return $this->bcrypt->hash($password);
}
if ($this->store_salt && $salt)
{
return sha1($password . $salt);
}
else
{
$salt = $this->salt();
return $salt . substr(sha1($salt . $password), 0, -$this->salt_length);
}
}
and tested by creating this public function in one of my controllers
public function Qpass_gen(){
$pass = $this->ion_auth_model->hash_password('password',FALSE,FALSE);
echo $pass;
}
and when i replaced the Qpass_gen() string with the one stored defaultly in the database by ion_auth,i managed to log in.
Is my method for quickly generating passwords guaranteed to work always?.
Yes, that's a good way to handle it. As the author of the library, that's what I would recommend.
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
So I'm trying to implement a system where user authentication is based on external sql.
This external sql is on my first website where I have a lot of users and I simply want them to login with the same credentials.
The passwords stored in the database are encrypted so I need to figure out the encryption method. I have full access to the website so I found this php code (site is based on php):
/**
* Generate a password hash
*
* #param string $strPassword The unencrypted password
*
* #return string The encrypted password
*
* #throws \Exception If none of the algorithms is available
*/
public static function hash($strPassword)
{
if (CRYPT_SHA512 == 1)
{
return crypt($strPassword, '$6$' . md5(uniqid(mt_rand(), true)) . '$');
}
elseif (CRYPT_SHA256 == 1)
{
return crypt($strPassword, '$5$' . md5(uniqid(mt_rand(), true)) . '$');
}
elseif (CRYPT_BLOWFISH == 1)
{
return crypt($strPassword, '$2a$07$' . md5(uniqid(mt_rand(), true)) . '$');
}
else
{
throw new \Exception('None of the required crypt() algorithms is available');
}
}
and also this:
/**
* Run the controller and parse the password template
*/
public function run()
{
$this->Template = new BackendTemplate('be_password');
if (Input::post('FORM_SUBMIT') == 'tl_password')
{
$pw = Input::postRaw('password');
$cnf = Input::postRaw('confirm');
// The passwords do not match
if ($pw != $cnf)
{
Message::addError($GLOBALS['TL_LANG']['ERR']['passwordMatch']);
}
// Password too short
elseif (utf8_strlen($pw) < Config::get('minPasswordLength'))
{
Message::addError(sprintf($GLOBALS['TL_LANG']['ERR']['passwordLength'], Config::get('minPasswordLength')));
}
// Password and username are the same
elseif ($pw == $this->User->username)
{
Message::addError($GLOBALS['TL_LANG']['ERR']['passwordName']);
}
// Save the data
else
{
// Make sure the password has been changed
if (crypt($pw, $this->User->password) === $this->User->password)
{
Message::addError($GLOBALS['TL_LANG']['MSC']['pw_change']);
}
else
{
$this->loadDataContainer('tl_user');
// Trigger the save_callback
if (is_array($GLOBALS['TL_DCA']['tl_user']['fields']['password']['save_callback']))
{
foreach ($GLOBALS['TL_DCA']['tl_user']['fields']['password']['save_callback'] as $callback)
{
if (is_array($callback))
{
$this->import($callback[0]);
$pw = $this->$callback[0]->$callback[1]($pw);
}
elseif (is_callable($callback))
{
$pw = $callback($pw);
}
}
}
$objUser = UserModel::findByPk($this->User->id);
$objUser->pwChange = '';
$objUser->password = Encryption::hash($pw);
$objUser->save();
Message::addConfirmation($GLOBALS['TL_LANG']['MSC']['pw_changed']);
$this->redirect('contao/main.php');
}
}
$this->reload();
}
My question is:
how to translate this method to python?
I don't quite understand the randomness and how to repeat it. Cou;ld someone explain?
Thanks,
C
//edit:
I mean I don't understand where
return crypt($strPassword, '$6$' . **md5(uniqid(mt_rand(), true))** . '$');
this middle part is saved and thus how can it ever be compared to crypt(password, the_same_function(but gives totally different value);
succesfully
I feel like I'm missing something obvious here, could someone explain? I know it's basic.
Thanks again
Main magic here is in 2 lines:
the one that you mentioned:
return crypt($strPassword, '$6$' . **md5(uniqid(mt_rand(), true))** . '$');
and the one which checks if password is changed:
if (crypt($pw, $this->User->password) === $this->User->password)
in fist one the salt for the password is generated, in second hashed password from database is used to extract the salt which has to be used in crypt() method
Here you have more info: http://php.net/manual/en/faq.passwords.php#faq.passwords.salt
minus may be because you didn't put enough work at first and ( the thing that made it harder for me ) you didn't put your password verification code and you didn't put your python code.
I think you have enough now to write your own code. Take a look in the python hashlib documentation https://docs.python.org/3/library/hashlib.html and using salts.
I hope it's enough to get you going if not post your python code.
I'm trying to validate password against invalid hash stored in database. Instead of getting false (as I supposed) in this situation, my application dies with Invalid hash exception.
Is there any Yii2-built-in way to validate hash, before feeding it to validatePassword, to handle this kind of situation more gently?
Or the only way I'm left with is to copy code used by validatePassword:
if (!preg_match('/^\$2[axy]\$(\d\d)\$[\.\/0-9A-Za-z]{22}/', $hash, $matches) || $matches[1] < 4 || $matches[1] > 30) {
throw new InvalidParamException('Hash is invalid.');
}
to my own code and simply not call validatePassword, when hash is invalid?
You can always use try - catch block in your password validation:
/**
* Validates password
*
* #param string $password password to validate
* #return boolean if password provided is valid for current user
*/
public function validatePassword($password)
{
try {
$data = Yii::$app->getSecurity()->validatePassword($password, $this->password_hash);
return $data;
} catch(\yii\base\InvalidParamException $e) {
return false;
}
}
Something like that, but why you even want to try to validate hash if it's already malformed? Maybe some criminal will pass something bad there, which can pass your login etc.
I have login code that is supposed to work by attempting to authenticate the user using Laravel's Auth::attempt() method. This code works on another site of mine, I have altered it as instead of the Password in the database, it is stored as passwdEncrypted. I cannot change it as the database is in use by another application as well.
The code is below:
// check if in database
$isInDb = User::where('ReferenceCode', $username)->first();
if($isInDb) {
// is in database
// check if password is encrypted yet
if($isInDb->passwdEncrypted) {
// password is encrypted
if(Auth::attempt(array('ReferenceCode' => $username, 'passwdEncrypted' => $password))) {
// authenticated
$array = array();
$array['verified'] = 'true';
$array['type'] = Auth::user()->UserType;
return Response::json($array);
} else {
// not authenticated
$array = array();
$array['verified'] = 'false';
$array['type'] = $type;
return Response::json($array);
}
} else {
// password is not encrypted
// check if plain text password is correct
if($isInDb->Password == $password) {
// plain text password is correct
$hashed = Hash::make($password);
$arr = array('passwdEncrypted' => $hashed);
$updated = User::where('rsmsOnlineUserID', $isInDb->rsmsOnlineUserID)->update($arr);
if($updated) {
$newUser = User::find($isInDb->rsmsOnlineUserID);
echo $newUser->passwdEncrypted;
if(Auth::attempt(array('ReferenceCode' => $username, 'passwdEncrypted' => $password))) {
echo 'logged in';
} else {
dd(DB::getQueryLog());
echo 'could not log in';
}
} else {
echo 'did not update';
}
} else {
// plain text password is incorrect
$array = array();
$array['verified'] = 'false';
$array['type'] = $type;
return Response::json($array);
}
}
} else {
// not in database
return Respone::json(array('success' => 'false'));
}
What is happening: I can't log in, the username and password in the database is 1234, even if I hard code that, it does not work.
It first checks to see if the user is in the database, if it is, it checks to see if there is an encrypted password, if there is not, it will create one from the password given if it matches the plain text password in the database and then log the user in (I have no choice but to have the plain text password stored in the database, that is how they want it).
But it returns the {"verified":"false","type":"prospective_employee"} from the not authenticated part of the code. So neither of the Auth::attempt() blocks work.
I was logging them in manually but even Auth::login() won't work.
I have the following in my User model (with the main database table):
public function getAuthPassword()
{
return $this->Password;
}
/**
* Get the token value for the "remember me" session.
*
* #return string
*/
public function getRememberToken() {
return $this->remember_token;
}
public function setRememberToken($value) {
$this->remember_token = $value;
}
public function getRememberTokenName() {
return 'remember_token';
}
/**
* Get the e-mail address where password reminders are sent.
*
* #return string
*/
public function getReminderEmail()
{
return $this->email;
}
Please note that there is a field in the table called Password, but that is the plain text password, I need to authenticate against the passwdEncrypted field.
You cannot do this with Laravel, and for good reason, but it is ridiciously unsecure and dangerous.
I have no choice but to have the plain text password stored in the
database, that is how they want it
I dont understand - why are you storing BOTH an "unencrypted" and "encrypted" password? There is no point. You should only ever store encrypted passwords. There is no need for any other way, and you need to educate the people as to why.
This code works on another site of mine, I have altered it as instead
of the Password in the database, it is stored as passwdEncrypted. I
cannot change it as the database is in use by another application as
well.
The Laravel Auth code is hard coded to use the "password" column. You cannot simply change it to another colum. That is why your code is failing.
Since you are not using the password column, and since you are not using encrypted passwords, you might as well just create your own unsecure login system, customised to suit your requirements.
I have a site which runs off an MD5 hashing scheme for passwords. As a way of supporting this legacy system, I've this answer to manually override the login system for now. But this isn't really ideal, as MD5 is pretty much universally known to be awful at encryption. So in the interest of security, what's the best way to migrate users over to the safer CakePHP auth system without causing them undue grief?
Figured it out thanks to this answer (albeit lightly modified). Basically, it updates the user behind the scenes to use the new system if the current system doesn't match up with it.
/**
* Login method
*/
public function login() {
$this->layout = 'homepage';
// If the user is already logged in, redirect to their user page
if($this->Auth->user() != null) {
$this->redirect();
} else {
// If this is being POSTed, check for login information
if($this->request->is('post')) {
if($this->Auth->login($this->loginHelper($this->request->data))) {
// Redirect to origin path, ideally
} else {
$this->Session->setFlash('Invalid username or password, try again');
}
}
}
}
/**
* Update password method
* #param array The user's data array
* #param Returns either a user object if the user is valid or null otherwise
*/
private function loginHelper($data) {
$username = $this->data['User']['username'];
$plainText = $this->data['User']['password'];
$user = current($this->User->findByUsername($username));
$salted = Security::hash($plainText, null, true);
if ($salted === $user['password']) {
return $user; // user exists, password is correct
}
$md5ed = Security::hash($plainText, 'md5', null);
if ($md5ed === $user['password']) {
$this->User->id = $user['id'];
$this->User->saveField('password', $plainText);
return $user; // user exists, password now updated to blowfish
}
return null; // user's password does not exist.
}
So I'm trying to write this script for my site.
It looks quite messed up and broken.
Maybe somebody can help me tidy it up a bit and explain what might be incorrect.
Also, is there a way to make it shorter, looks a bit unsafe to me.
Thank you.
<?php
class Register
{
private $username;
private $password;
private $password2;
private $passmd5;
private $email;
private $email2;
private $errors;
private $rtoken;
public function __construct()
{
$this->errors = array();
$this->username = $this->filter($_POST['ruser']);
$this->password = $this->filter($_POST['rpass']);
$this->password2 = $this->filter($_POST['rpass2']);
$this->email = $this->filter($_POST['remail']);
$this->email2 = $this->filter($_POST['remail2']);
$this->rtoken = $_POST['rtoken'];
$this->passmd5 = md5($this->password);
}
public function process()
{
if ($this->valid_rtoken() && $this->valid_data())
$this->register();
return count($this->errors) ? 0 : 1;
}
public function filter($var)
{
return preg_replace('/[^a-zA-Z0-9#.]/', '', $var);
}
public function register()
{
mysql_query("INSERT INTO users(username,password,email) VALUES ('{$this->username}','{$this->passmd5}','{$this->email}')");
if (mysql_affected_rows() < 1)
$this->errors[] = '<font color="red">Database error</font>';
}
public function user_exists()
{
$data = mysql_query("SELECT ID FROM users WHERE username = '{$this->username}'");
return mysql_num_rows($data) ? 1 : 0;
}
public function email_exists()
{
$data = mysql_query("SELECT ID FROM users WHERE email = '{$this->email}'");
return mysql_num_rows($data) ? 1 : 0;
}
public function show_errors()
{
echo "";
foreach ($this->errors as $key => $value)
echo $value . "<br>";
}
public function valid_data()
{
if ($this->user_exists())
$this->errors[] = '<font color="red">Username Exists</font>';
if ($this->email_exists())
$this->errors[] = '<font color="red">email exists</font>';
if (empty($this->username))
$this->errors[] = '<font color="red">check your username</font>';
if (empty($this->password))
$this->errors[] = '<font color="red">check your password</font>';
if ($this->password != $this->password2)
$this->errors[] = '<font color="red">Passwords do not match</font>';
if (empty($this->email) || !eregi('^[a-zA-Z0-9._-]+#[a-zA-Z0-9._-]+\.[a-zA-Z]{2,4}$', $this->email))
$this->errors[] = '<font color="red">Check your email</font>';
if ($this->email != $this->email2)
$this->errors[] = '<font color="red">Emails do not match</font>';
return count($this->errors) ? 0 : 1;
}
public function valid_rtoken()
{
if (!isset($_SESSION['rtoken']) || $this->rtoken != $_SESSION['rtoken'])
$this->errors[] = '<font color="red">Check</font>';
return count($this->errors) ? 0 : 1;
}
}
?>
Swoosh, there are always better ways to write code. I hope to challenge you to rethink why your code is long, and why it might be unsafe, rather than rewrite your code for you. Hopefully you will learn more this way.
To begin with, hashing the password with MD5 is completely insecure. Please forget that MD5 ever existed, and please don't use even SHA1 for anything secure. Instead, you should use bcrypt or PBKDF2 (with SHA2 or Whirlpool and ~2000+ rounds). I suggest you refer to my answer to Secure Hash and Salt for PHP Passwords for tips and links to articles and libraries to help implement better security.
Second, mysql_* functions are deprecated as of PHP 5.5. Using MySQLi will get you by, but you should use a consistent interface such as PDO to handle the connections, and query escaping/filtering. While you might not be building your software in PHP 5.5, you will not always have control over if a host decides to upgrade your version of PHP. Optimize for future compatibility as much as you can now. Plus, PDO has some nifty features that are explained in its documentation.
Third, you should not use a regular expression to "filter" your query variables. The safest thing you can do is use intval/floatval on any numbers, and escape the rest through the DB library you use such as mysql_escape_string (or mysqli_real_escape_string) OR use prepared statements (which will sanitize variables for you).
Fourth, you are putting display logic into your object. Think about it: what purpose/role does this object fulfill? Does it handle the registration logic? Does it store the registration data? It's a good idea to use the Single Responsibility Principle here. It looks like the object is supposed to act like a hybrid model-controller, with presentational information in it. You could expand this to RegistrationModel and RegistrationController classes to handle storing the data temporarily, or even decide to do something else. But remember, the more responsibilities that your class has the more ways it'll have to break.
Also, by making all the attributes of Register private, you cannot have more than one way to register. What if you wanted a short-cut to the process, such as log in via OAuth (such as Twitter or Facebook), but needed to reuse some of the logic in Register? These attributes should at least be protected so that you can inherit from them, or even public so another object can interface with them (such as an object that notifies the user that their registration is successful).