Laravel - DecryptException: 'The MAC is invalid' - php

In laravel for registration I'm using encrypt algorithm for password instead of inbuilt bcrypt function in Laravel because to get password and send it to mail when password is forgot.
But decrypt it is showing a error like
DecryptException The MAC is invalid in Encrypter.php (line 184)
This , when I run this code it is working on local but server itself it is not working below i have mentioned the code , can anyone please help
public function forgotpassword(Request $request)
{
$email=$request->email;
$selectemail = User::select('email','password','name')
->where('email',$email)
->first();
if($selectemail)
{
$password=decrypt($selectemail->password);
$data = array( 'email' => $selectemail->email,'password' => $password , 'name' => $selectemail->name);
Mail::send('email.resetpassword',$data,function($message) use ($email)
{
$message->to([$email])->subject('Forgot Password Letgo');
});
echo "Mail has sent successfully";
} else {
echo "This email is not yet registered";
}
}

The problem is you generated a new APP_KEY, then if you try to decrypt the old encrypted data it will show the DecryptException: The MAC is invalid.
If you want to decrypt the old data you need to restore your old APP_KEY.
After realizing that, now, adding a new problem there, if you stored new data with another APP_KEY or another encryption method you have a problem on the data because they are mixed on the table.
In case you don't know when do you started with the new encrypt method or differentiate the new encrypted entries, the fastest solution would be reset all the passwords with the new encrypt method.
You can learn more about how Laravel encryption works on the official Laravel docs.

I copied the APP_KEY from the environment it was working dev to the production and the issue was solved. you may want to try it.

In case none of the above helped you, as it was in my case, well, some people mention clearing the cookies, sadly that is ambiguous to say the least.
I tried everything from the above, clear cache in laravel and the browser, hard reload and all..With no success!
SOLUTION: just CLOSE the browser entirely, and reopen it. In my case, I was using both Chrome and Opera, and they were both messing up. I had to close them BOTH, then reopen them for the MAC problem to disappear.

To avoid this, use a custom key instead. The default key is APP_KEY, but you can provide one so your decrypt is not linked with new or old APP_KEY.
I use the following code to resolve it, and it worked in different APP_KEYs.
function customCrypt($vWord){
$customKey = "blabla_key_with_correct_length";
$newEncrypter = new \Illuminate\Encryption\Encrypter( $customKey, Config::get( 'app.cipher' ) );
return $newEncrypter->encrypt( $vWord );
}
function customDecrypt($vWord){
$customKey = "blabla_key_with_correct_length";
$newEncrypter = new \Illuminate\Encryption\Encrypter( $customKey, Config::get( 'app.cipher' ) );
return $newEncrypter->decrypt( $vWord );
}
Important for key length : if $cipher == 'AES-128-CBC' use $length === 16, if $cipher == 'AES-256-CBC' use $length === 32). Check in config/app.cipher which cipher your app uses.

If you run multiple project and passes one encryption key to another project, Just make sure you have made same APP_KEY to your both project.

App key matters in encryption and decryption. I was having 2 sub
domains with different projects in which I was encrypting value on sub
domain and 1 and trying to decrypt on sub domain 2. Issue was resolved
when both projects were having same appkey. Note: No projects should
have same appkey!!!
If you have imported DB form one environment to another, most likely you will face this error. Its recommended to have same APP_KEY as data source application in order to fix bug.

If you have run 2 different Laravel applications locally in the same browser, cookies can be saved in your browser. Please clear the cache and cookies before loading the 2n app.

Related

Can a default user's password be set in SQL using PHP's password_hash?

My old PHP app has a default admin user and md5 encrypted password created by the SQL that creates the database: insert into users values ( 1, 'admin', MD5('changeMe'), 2 );
Is there a simple way to include a default user and encrypted password using PHP's passowrd_hash function on creating the tables? I ask because I understand that password_hash is a native PHP function and I assume it won't be understood in SQL.
The solution to my problem came in three parts. My OP sought a simple way to create a hashed password for the admin user for insertion in the MySQL database on the installation of the application, using the native PHP password_hash() function.
(1) Based on a suggestion by #Nick and #Tadman, I decided to incorporate setting the hash in an installer script that would set not only the hash but other defined site/application variables.
Rather than inserting user values when the database table is created, it was deferred until immediately after, with the admin user entering their credentials in the form that inserts the hash and writes other definitions to a file:
$userpass = $_POST['userpass'];
echo password_hash($userpass, PASSWORD_BCRYPT);
(2) The second part of my problem was replacing all instances of md5()`` withpassword_hash()` and I achieved that by using a neat PHP script I found online to recursively search and replace the occurrences on the server.
Having replaced the md5() occurrences, I needed to change the hash comparison method and again by searching the relevant files I was able to replace instances of:
if ($p != $theUser->pwd ) {
return( false ); }
with:
if(password_verify($p, $theUser->pwd)) {
// Success!
}
else {
// Invalid credentials
echo "Uh oh!";
}
(3) The third step in resolving the problem was discovering that adding $1$ to the opening of the md5 hash could make it readable by password_hash(); so I just needed to make a couple of adjustments in the installed database to the admin user's old password.
Thanks to those who helped shine the light so I could find my way. I'm off now to invent the wheel and sliced bread.
you can do something like this in php:
$hash = password_hash('changeMe');
//echo $hash;
then use this hash in the Database.

Php session data is returning wrong value

I have an application that i built in php 7 with the code-igniter framework and my problem is with the session data , storing and retrieving session data works fine , but occasionally when two people log in at close intervals , the session data for the first user is also retrieved for the second user, searched through the site , saw something similar here (wrong data in PHP session) that suggested that it might be a caching issue (my site uses nginx for caching) , but no concrete solutions were suggested. Any suggestions or Ideas will be appreciated.
Edit : Here is the section of my login library for authentication
public function login_account($email,$password)
{
$db = "db";
$data = array("login_mail" => sha1($email));
$query_result = $this->CI->m_eauth->get_login_password($data,$db);
$hash_password ="";
foreach($query_result->result_array() as $value)
{
$hash_password = $value['hash_password'];
$site_name = $value['hash_name'];
$account_type = $value['account_type'];
$site_match_id = $value['site_match_id'];
$site_levels = $value['levels'];
$site_roles = $value['roles'];
}
if(password_verify($password, $hash_password)){
// Success!
$session_data = array(
"site_id"=>$site_match_id,
"site_email"=>$email,
"site_name"=>$site_name,
"site_avatar"=>md5($email).".jpg",
"site_type"=>$account_type,
"site_levels"=>$site_levels,
"site_roles"=>$site_roles
);
$this->CI->session->set_userdata($session_data);
return "successful";
}
else{
// Invalid credentials
return "unsuccessful";
}
}
Let me add that the login works fine and individual sessions work just fine. But every now and then the problem i described happens , and i'ts quite confusing as i don't know where to look.
There's no real way to sugar coat this, sessions aren't some magical part of PHP that you enable you to just call session_start() and go about your day. If your application is leaking sessions then you haven't secured it properly and you need to fix it.
Session security is a pretty big deal, given that a hijacked session basically gives an attacker total access to someone else's account.
I would recommend you read the official PHP session docs and also consider implementing the Nginx userid module as an additional measure for identifying users.

implementing user has password in hook menu

we are implementing a web service for a Drupal 7 site (the web service code is not part of drupal installation folder).
one of the web services needs to sign up the user on the site.
the main hurdle was getting a hashed password that Drupal will also recognize.
for that , am following a suggestion made on stack overflow to Implement a REST service inside drupal and call that from the outside service code . (that part also seems possible and achieveable).
have implemented a password hashing service with following code:
function GetHashedPassword($string)
{
require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
$hashedpw = user_hash_password($string);
$data = array(
'password' => $hashedpw
);
header("Access-Control-Allow-Origin: *");
drupal_json_output($data);
drupal_exit();
}
the main issue now is that whenever this service is called even with same string , it returns a new hashed value each time..
kindly assist if what we need is actually even possible and if so, then what could be fixed in the above code
any help appreciated
The Drupal user_hash_password function generates a new salt each time it calculates the hash. This will cause a new hash to be generated since the salt is likely to be different to the last one.

forge domain for firefox password autocomplete?

When running two different websites, say free.webhost.com/app1 and free.webhost.com/app2, it seems that Firefox has trouble storing different login credentials for both, especially when the same username is used with different passwords. If a user's credentials on the /app1 site are Name and pass1 and on the other site are Name and pass2, then Firefox can only store one of these and will ask to change the password when hopping between them.
I investigated this problem and to my astonishment this seems to be a WONTFIX in the firefox bug repository: https://bugzilla.mozilla.org/show_bug.cgi?id=263387
Is there any way I can workaround this when designing my apps? Like by setting a certain cookie property in PHP or html, or even specify a (fake) different domain name, so that firefox no longer considers free.webhost.com/app1 and free.webhost.com/app2 as the same website for password storage (and can thus store a different password with the same username for both sites)?
No, there is no workaround or trick for this. Deploy your apps to different domains - even different subdomains (e.g. app1.example.com and app2.example.com) will do.
We need a custom saver. I'm going to try be short and concise.
This is very useful if you don't want the browser saver. I think it can have some applications.
THE BASIC
I suggest in the PHP we use different cookies to save the session with session_name or session.name directive. For each site we must set a session name.
In the HTML we should use different inputs name, so the app1 input will be <input type='email' name='email_app1' /> and app2 email_app2. We also can disable the autocomplete.
The data is saved locally encrypted with AES. For this we can get CryptoJS.
We also want to have salt hash in the client.
THE CONCEPT (summarized):
Save locally the password encrypted. It is returned by the login
controller. Of course if the user wants.
Save a salt which changes in each login. Of course if the user wants.
When the user return to the login page, the JavaScript checks if there
is a salt and it sends it to server. The PHP returns the passphrase and JavaScript decrypts the local password.
the sample code:
In the controller:
// I use functions in the controller like example
public function getLoginPage(){
// it prints the html and js to login using the basics how i have said
}
// salt is sended by the JavaScript
public function getPassphrase( $salt, $username ){
$passPhrase = get_passphrase_from_salt( $salt, $username, Request::IP() );
return $passPhrase;
}
// It is to get the salt
public function getSalt( $username, $password ){
$user = get_user( $username, $password );
// if valid user...
$passphrase = random_string();
$salt = random_string();
$encrypted = encrypt( $password, md5($passphrase) );
save_in_table_salt( $salt, $passphrase, $username, Request::IP() );
// it prints a JSON like example
return json_encode( array( 'salt' => $salt, 'encrypted' => $encrypted) );
}
// ... Normal login etc you could change the salt and reset in the client
In the view we put the JavaScript logic. I used localstorage but I think it's not important.
// in login page
window.onload = function(){
if( localStorage.getItem('salt') !== null ) { // the data is saved
// Get the passphrase
ajax_call('post', 'getPassphrase', {
salt: localStorage.getItem('salt'),
username: localStorage.getItem('username')
}, function( passphrase ){
// It sets the inputs values!
document.getElementById('username_app1').value = localStorage.getItem('username');
document.getElementById('password_app1').value = decrypt( localStorage.getItem('password'), CryptoJS.MD5(passphrase) );
});
}
};
// it captures the submit action
document.getElementById('login_form').onsubmit = function(){
// it asks to user if he wants save locally the credentials
if( localStorage.getItem('salt') === null
&& confirm('Do you want save credentials?') ){
var form = this;
// get salt
ajax_call('post', 'getSalt', {
user: document.getElementById('username_app1').value,
password: document.getElementById('password_app1').value
}, function( object ){
localStorage.setItem('salt', object.salt);
localStorage.setItem('password', object.encrypted);
localStorage.setItem('username', document.getElementById('username_app1').value );
form.submit(); // now yes
});
return false; // it prevents submit
}
};
You must know that the code is a sample. Some functions don't exists and it's only to be understood. We need more conditions and logic to do it works.
UPDATED: Now works with multiple computers and IP security and more!
There is no workaround for this as internal credential storage in Firefox is organized per domain, not per URL.
Even changing Name or ID for input HTML controls or Form tag will not affect this.
Only solution is to host your application on different (sub)domains.
Probably the best solution here would be to create 2 different virtual host for your app.
Like one for webhost.com and one for free.webhost.com.
How To Set Up Apache Virtual Hosts on Ubuntu 12.04 LTS
Hope this help!!
If your are having problem on setting up your virtual host in your server let me know.
Note:You need to create your DNS entry to access the new host you created or you need to add a record to the host file of your system from where you are browsing the site.

Why is the CakePHP password field is empty when trying to access it using $this->data?

I am implementing an authentication component,
This is my registration page
create('User',array('action' => 'login'));
echo $form->input('primary_email',array('size'=> 32));
echo $form->input('password',array('label' => 'Password'));
echo $form->input('remember_me',array('label' => 'Remember Me','type'=>'checkbox','checked' => 'false'));
echo $html->link('Forgot Password','/users/forgot/');
echo $form->end('Login');
// Javascripts
echo $javascript->link('jquery',false);
//echo $javascript->link('jquery.validate.js',false);
//echo $javascript->codeBlock($code, array('inline' => false));
?>
When I print the contents of $this->data the password field turns up empty.
How can I resolve this?
When I rename password to password2 or something else it works !!! Strange
this is because the Auth component removes the password from the data array (for security purposes). why would you want it to contain the password anyway? the remember me logic (which I assume you are using from the form fields) will handle logging someone in without the password.
I had the same issue and mrlanrat's suggestion worked for me.
Instead of trying to access $this->data['password'], you will need to access $this->Auth->data['password']
What basically happens is that the Auth component hashes the password and moves the hashed version into $this->Auth->data. The non-hashed version is deleted for security purposes.
So whenever you need to deal with data related to Auth component, make sure you're using $this->Auth->data and not $this->data.
I would like to see your login() function as well but indeed this problem seems weird. You can work around this by renaming your password field and then inside your login function do something like:
$this->data['password']=$this->Auth->password($this->data['User']['pwd']);
//now you can call $this->Auth->login and it will work
One thing you may be able to do is have the user confirm their password, and use it for the login function. I may be completely mis-reading what you're trying to accomplish, but if you have the user Confirm their password w/ a password 2 field, you can compare the two, and also use the starting state of the confirmation password for whatever it is that you're wanting to do.
So:
echo $form->input('password', array('label' => 'password'));
echo $form->input('password2', array('label' => 'Confirm Password'));
Inside of your logic for whatever you're wanting to accomplish, you can just put:
$morePermanentDataStorage = $this->data['User']['password2'];
if($this->data['User']['password'] == $this->Auth->password($this->data['User']['password2']) {
//function logic here
}
I am also assuming that your form logic from above actually starts with:
echo $form->create('User');
I had the same problem, Using $this->Auth->data['User']['user'] and $this->Auth->data['User']['password'] fixed it.
It looks like the auth controller removed the password from the data when it does its automagic.
I had the same problem and it was related to using the email field for login thru this method
$this->Auth->fields = array('username' => 'email', 'password' => 'password');
I couldn't resolve it, so I opted to include a username field on my database and now it works perfectly.
I had the same problem with some ExtJS authentication I was working on some weeks back.
A weird workaround is to go to /config/core.php and change the debug level temporarily e.g. change from Configure::write('debug', 0); to Configure::write('debug', 1); and then run your code - not necessarily the part with the Auth component - (and afterwards change the debug level back if you wish). $this->data['User']['password'] will now be populated with the hash value as intended.
What causes this in the first place still beats me :)
First of all, you need to comment the code Configure::write('Cache.disable', true); in the file core.php

Categories