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.
Related
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.
I'm stuck with PHP 5.4 and a single database userid because of defects in Comcast's business hosting implementation. The userid problem means that the same credentials I use to manage a MySQL database must also be used while handling requests from ordinary users. I have control over who can login as an ordinary user by delegating the creation of ids to a group of trusted users who will communicate to the ultimate end users the URL they will need to login to the app. And, although the server is running Apache, it ignores an Authorization header.
Positing that some hacker might defeat the HostWays Unix server's file system protection and read all of my PHP source code, I need to avoid having the database credentials appear in the MySQL connect calls in my PHP source. I've come up with the following scheme for encrypting the database credentials, and I want to be sure that the scheme will actually be effective.
I will define a global variable named $credentials in a configuration file. I'll generate the value in my development environment with this code:
$cipher = "AES-128-CBC";
$secretkey = …; // it's a secret!
$secret = openssl_encrypt("$dbuser:$dbpass", $cipher, $secretkey);
Remember, I'm stuck with PHP 5.4, and I wasn't able to get past mysterious errors in openssl_decrypt when I used an initial-value in the encrypt/decrypt calls. Anyhow, this much of the code at least works.
My PHP app can be accessed two ways: one is via an XML payload in a POST transaction coming from a C++ desktop app; the other is via an ordinary URL in a GET. Both methods will use HTTPS. The XML schema calls for an authentication parameter that supplies the $secretkey.
The URL for the GET transaction will be used by general users from a browser. It will include a secret= parameter that encodes both the user's login id and the $secretkey. I will generate the secret= parameter during the transaction that adds the user to the list of authorized users:
$cipher = "AES-128-CBC";
$key = "Fortune42Cookies!";
$secret = openssl_encrypt("$username:$secretkey", $cipher, $key);
In this code, the $secretkey is left over from when the trusted user who is adding this end user to the list logged in. The corresponding decryption code used when the end user logs in is as follows:
$cipher = "AES-128-CBC";
$key = "Fortune42Cookies!";
$clearsecret = explode(":", openssl_decrypt($secret, $cipher, $key));
$username = $clearsecret[0];
$secretkey = $clearsecret[1];
$this->db = new mydatabase($secretkey);
. . .
class mydatabase extends mysqli {
public function __construct($secretkey) {
public $credentials; // set by the configuration script
$cipher = "AES-128-CBC";
$cred = explode(":", openssl_decrypt($credentials, $cipher, $secretkey);
parent::__construct(<database path>, $cred[0], $cred[1], <dbname>);
}
}
To summarize, the __construct call uses $credentials that are stored in encrypted form in a configuration file that my putative hacker can read. We decrypt them using the $secretkey that comes to us either in a secure XML packet or in the URL for a secure GET transaction.
The one flaw I see in this scheme is that a hacker who learns the login id of a registered user and who reads the PHP source code to learn the silly Fortune42Cookies! key could construct that user's unique $secret and thereby impersonate the user. Because there's only one set of credentials for the database, the hacker would then have the keys to the city.
Still, the extra layer of protection (hacker needs to defeat the UNIX file system protection AND learn the name of a real end user) seems worth it.
Fifty years of software development experience tells me I've missed something major. What is it?
I am facing a problem that's literally driving me nuts. Following scenario is given:
Application A - Manages the users
pw created using Bcrypt(), cost 14
Application B - fetches the User-Data and stores it inside it's local db
pw verified through bcrypt(), cost 14
Bcrypt implementation via Zend\Crypt\Password\Bcrypt
So, whenever I create a fixed password on App A, synchronize them, authentication on App B works.
However, whenever I create a random password on App A, synchronize them, authentication on App B just won't work.
I pass the password created through a session to a front-end after a redirect.
$password = (string) rand(2938, 9578);
//$password = '12345678';
$bcrypt = new Bcrypt();
$bcrypt->setCost(14);
$entity->setPassword($bcrypt->create($password));
$entityService->save($entity);
$this->flashMessenger()->setNamespace('MpuServerUser')->addSuccessMessage(
"Benutzer-PIN erfolgreich erneuert. PIN: {$password}"
);
return $this->redirect()->toRoute('mpuserveruser');
As you can see that's the part that I'm facing troubles with. Whenever I create a new password for a User with a given string '123456', there are no problems, whatsoever.
But when I use the uncommented part $password = (string) rand(2938, 9578); the password I'm getting on my front-end won't authenticate.
There's no differences between trying with rand() or mt_rand(). Anyone any idea what the heck is going on here? ^^
Update - apparently only passwords that are of 6 chars or longer do work. Even if I set a predefined password of only 4 letters, it won't work.
Here's my piece of code:
(...)
//I USE ZfcUserAdmin MODULE
if ( $this->getOptions()->getCreateUserAutoPassword() ) {
//HERE'S WHERE I'VE TRIED
//\Zend\Math\Rand::getInteger( 2938, 9578 ); --> OK
//rand( 2938, 9578 ); --> OK
$rand = \Zend\Math\Rand::getString( 8 );
$user->setPassword( $rand );
}
$uncrypted_password = $user->getPassword();
$bcrypt = new Bcrypt;
//PASSWORD COST IS SET TO 14, LIKE YOU
$bcrypt->setCost( $userOptions->getPasswordCost() );
$user->setPassword( $bcrypt->create( $uncrypted_password ) );
(...)
The only difference between your solution and mine, is that you redirect and store the password in the session, while I just send an email to the user with the generated password. If I use the new credentials in the email, I can login without problems. Anyway, I think that's not the problem, I suppose it should be something in the BCrypt class.
I'm trying to create a link that when clicked will login a user automatically and take them to a specific page.
I've thought about creating some sort of hashed string that contains the user's ID, username and a few other pieces of info. When clicked these pieces of information are looked up in the DB and if validated I login them in and redirect them to a specific page.
For sites like Twitter and Facebook when I receive an email notification and click the link in my email I'm automatically taken to my inbox on the corresponding site. I'm trying to duplicate that behavior...
Are there any security issues with doing something like this or is there a safer more preferred way?
if you want to offer this feature to your users, you have to take care of two things:
The validity of the created url must be set in time (ex: 24hours, 48hours).
The created url must only work for one specific user.
(optionnal) The created url only work for one page
I propose this kind of solution to create an url which match these criteria (it's only a proof of concept):
<?php
$privateKey = 'somethingVerySecret';
$userName = 'cedric';
$url = 'my/personal/url';
$timeLimit = new DateTime('Tomorow');
function createToken($privateKey, $url, $userName, $timeLimit){
return hash('sha256', $privateKey.$url.$userName.$timeLimit);
}
function createUrl($privateKey, $url, $userName, $timeLimit){
$hash = createToken($privateKey, $url, $userName, $timeLimit->getTimestamp());
$autoLoginUrl = http_build_query(array(
'name' => $userName,
'timeLimit' => $timeLimit,
'token' => $hash
));
return $url.'?'.$autoLoginUrl;
}
function checkUrl($privateKey){
if((int)$_GET['timeLimit'] > time() ){
return false;
}
//check the user credentials (he exists, he have right on this page)
$hash = createToken($privateKey, $_SERVER['PHP_SELF'], $_GET['name'], $_GET['timeLimit']);
return ($_GET['token'] == $hash);
}
The general standard for logging in is when a user creates an account your program should create a string of seemly random letters and numbers with a certain php function in php 5.5, and then store this in a file with some sort of pointer based on the username. Then when a user tries to login you use that same function and compare the two strings. The function being hash_pbkdf2 even though this php function supports sha... encryptions do not use those. I salt the hash code with the username. Here is an article on all website login and password things. The most secure thing you can do with your website to prevent people from brute force cracking your passwords is to limit the connection speed after a couple wrong password attempt to something so slow it would take longer than the life of the universe to crack after a couple password attempts. If you wanted to make a sort of remember me button store the username in cookies But never the password the browser will take care of the remembering password part if you label your form elements correctly.
I'm building a private CMS for my own use and am at the point where I will start building out the username and password storing features. I am considering the possibility of storing all admin username, password, and user details in a multidimensional array within a PHP file, rather than using SQL to store them in a database.
My reason for wanting to use this non-traditional approach of storing user info is the belief that this will make it harder for attackers to gain unauthorized access to user info (usernames, passwords, IP addresses, etc.), because I will not be connecting to a MySQL database.
Rough Outline of Code:
add_user.php
// set the last referrer session variable to the current page
$_SESSION['last_referrer'] = 'add_user.php';
// set raw credential variables and salt
$raw_user = $_POST['user'];
$raw_pass = $_POST['pass'];
$raw_IP = $_SERVER['REMOTE_ADDR'];
$salt = '&^${QqiO%Ur!W0,.#.*';
// set the username if its clean, else its false
$username = (is_clean($raw_user)) ? $raw_user : false; // is_clean() is a function I will build to check if strings are clean, and can be appended to an array without creating a parsing error.
// set the salted, sanitized, and encrypted password if its clean, else its false
$password = (is_clean($raw_pass)) ? $salt . encrypt($raw_pass) : false; // encrypt() is a function I will build to encrypt passwords in a specific way
// if username and password are both valid and not false
if( $username && $password ) {
// set the users IP address
$IP = sanitize($raw_IP);
// create a temporary key
$temp_key = $_SESSION['temp_key'] = random_key();
// random_key() is a function I will build to create a key that I will store in a session only long enough to use for adding user info to the database.php file
// add user details array to main array of all users
$add_user = append_array_to_file('database.php', array($username, $password, $IP));
// append_array_to_file() is a function I will build to add array's to the existing multidimensional array that holds all user credentials.
// The function will load the database.php file using cURL so that database.php can check if the temp_key session is set, the append_array_to_file() function will stop and return false if the database.php file reports back that the temp_key is not set.
// The function will crawl database.php to read the current array of users into the function, will then add the current user's credentials to the array, then will rewrite the database.php file with the new array.
// destroy the temporary session key
unset($_SESSION['temp_key']);
}
else {
return false;
}
database.php
$users_credentials = array(1 => array('username' => 'jack',
'password' => '&^${QqiO%Ur!W0,.#.*HuiUn34D09Qi!d}Yt$s',
'ip'=> '127.0.0.1'),
2 => array('username' => 'chris',
'password' => '&^${QqiO%Ur!W0,.#.*8YiPosl#87&^4#',
'ip'=> '873.02.34.7')
);
I would then create custom functions to mimic SQL queries like SELECT for use in verifying users trying to log in.
My Questions
Is this a bad idea, and if so, why?
Am I correct in thinking that this will reduce the number of possibilities for hackers trying to gain unauthorized access, sniff/steal passwords, etc., since I'm not connecting to a remote database?
I don't see any advantage: Whether you use a text file, a mysql database or a php file ( === text file), they are all "databases" in the sense that they are files where you store your information. The difference is that an sql database is made for that stuff;
I do see disadvantages as there are more potential holes you would have to think about. Some examples (apart from the stuff mentioned in the comments):
You need to take care that the password file is always out of the web-root in case php dies on you;
You need to avoid passing around your password file in for example source control.
These are not things that are hard to solve, but using a normal database you don't even have to worry about them.
Apart from that are misunderstanding the purpose of the salt: If you just prepend it to the encrypted password, there is really no point in using a salt, you need to send it to your encrypt function to hash it with your text-password so that rainbow tables would have to be generated for each password instead of just one for your whole database. And for that reason you should also not use a single salt for all your users, each should have a different, unique salt.
If you plan to store any kind of config data in a text file of any sort, as opposed to a traditional database, consider using an .ini file. If I'm not mistaken, you can also take advantage of storing it outside of your web root, just like the php.ini file.
Here's a great post explaining exactly how to go about this: Using ini files for PHP application settings
PHP Manual: get_cfg_var()