Digest authentication with encrypted stored password in mysql - php

I would like to implement digest authentication on my website. However, i have a user table with encrypted user table. I know that i can encrypt $_SERVER['PHP_AUTH_PW'] if i use basic authentication. How can we use the same method for digest authentication?

Are you looking for something like this?
define('MY_REALM', 'THIS IS A SECURE FACILITY, AND I MEAN THAT UNIRONICALLY');
$valid = false;
if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW']) {
// Use your DB here; prepared statements are your friend:
$userData = $user->getByUsername($_SERVER['PHP_AUTH_USER']);
if (!empty($userData)) {
$valid = password_verify($_SERVER['PHP_AUTH_PW'], $userData['password_hash']);
}
}
if (!$valid) {
header('WWW-Authenticate: Basic realm="'.MY_REALM.'"');
header('HTTP/1.0 401 Unauthorized');
echo 'Text to send if user hits Cancel button';
exit;
}
There's little benefit to the HTTP digest authentication method. Just use HTTPS on your website and store passwords with password_hash() and use password_verify() to check them.

Related

validation is not working Laravel custom authentication for bcrypted password

I have created a registration system where password are storing in bycript form. But While I am trying to validate for login purpose, it's saying wrong password . My code for authentication is given bellow :
public function authenticate(Request $request){
$email=$request->post('email');
$password=$request->post('password');
$result=Admin::where(['email'=>$email,'password'=>$password])->get();
if(isset($result['0']->id)){
$request->session()->put('ADMIN_LOGIN',true);
$request->session()->put('ADMIN_ID',$result['0']->id);
return redirect('admin');
}else{
$request->session()->flash('error','Please enter valid login details');
return redirect('admin-login');
}
}
You don't need to be building your own authentication system, but this would be the flow:
use App\Models\Admin;
use Hash;
...
public function authenticate(Request $request)
{
...
if ($user = Admin::where($request->only('email'))->first()) {
if (Hash::check($request->input('password'), $user->password)) {
// login
}
}
// not authenticated
}
You have to find the user by an identifier, so 'email' is used here. You can't query against the password because it is a hash. If you get a user from the query you can then do a hash check on the submitted password and the user's password from the record.
This is a simplified version of what SessionGuard::attempt/Auth::attempt([...]) is doing.
You have to find the admin by email like this:
$admin = Admin::where(['email'=>$email])->first();
and than compare the hashes
if ($admin && Hash::check($admin->password, $password)) {
// ... logged in
} else {
// ... not legged in
}
When your request is processed password comes as plain text while password in your database is hashed.
So you have to bcrypt or hash your password first to properly make your query.
You can:
$password = Hash::make($request->post('password'));
Or:
$password = bcrypt($request->post('password'));
Both Hash and bcrypt helper function work in the same way

PHP; How to securely store password protected login as cookie?

This is my PHP code.
<?php
$username = "admin";
$password = "admin";
$session = $_COOKIE['session'];
$private_key = "!$//%$$//%$&=§$!&%&=§$!&%";
if(isset($_POST['login'])) {
if($_POST['username'] == $username && $_POST['password'] == $password) {
setcookie("username", $username, time()+(10*365*24*60*60));
setcookie("session", md5($password.$private_key), time()+(10*365*24*60*60));
echo "You are are logged in!";
} else {
echo "Wrong login!";
}
}
if(isset($_COOKIE['session'])) {
if($_COOKIE['username'] == $username && $_COOKIE['session'] == md5($password.$private_key)) {
echo "You are are logged in!";
} else {
echo "Wrong login!";
}
}
?>
<form method="post" action="">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit" name="login">
</form>
What this code does: When you login with correct data, a cookie will set with username and hashed password. Username isn't secret and can be stored in clear text. Password is combined with a cryptic string before hashed to prevent somebody could guess the password. He won't have success without knowing the $private_key.
When re-visiting the page, you are already logged in because of the cookie.
Of course my script isn't perfect but: Is it the right way?
Without the correct login data you cannot login. A hacker will also not be able to find out the password because it's combined with a cryptic string.
But a hacker could read the cookie data in some way and will be able to login only using the cookie data by manipulate the cookie data in his browser. How can I prevent it?
If you're trying to implement a "remember me on this computer" feature (a.k.a. persistent authentication on top of sessions), you're about to enter a veritable rabbit hole of complexity. I suggest reading this dedicated guide to the topic.
To summarize the strategy: Do not store passwords in a cookie (hashed or otherwise). Instead, you're going to use randomly generated tokens. Specifically, a split token.
Long-Term Authentication ("Remember Me Across Sessions") Cookies
<?php
class Authentication
{
/** #var PDO $db */
private $db;
public function __construct(PDO $pdo)
{
$this->db = $pdo;
}
public function createLongTermToken(int $userId = 0): string
{
// Build the components
$tokenLeft = base64_encode(random_bytes(15));
$tokenRight = base64_encode(random_bytes(33));
$tokenRightHashed = hash('sha256', $tokenRight);
// Insert into the database
$stmt = $this->db->prepare(
"INSERT INTO auth_tokens (user_id, selector, hash) VALUES (?, ?, ?)"
);
$stmt->execute([$userId, $tokenLeft, $tokenRightHashed]);
return $tokenLeft . ':' . $tokenRight;
}
public function loginWithPersistentCookie(string $cookieValue): int
{
// Input validation
if (strpos(':', $cookieValue) === false) {
throw new Exception('Invalid authentication token');
}
list($tokenLeft, $tokenRight) = explode(':', $cookieValue);
if (strlen($tokenLeft) !== 20) || strlen($tokenRight) !== 44) {
throw new Exception('Invalid authentication token');
}
$tokenRightHashed = hash('sha256', $tokenRight);
// Fetch from database
$stmt = $this->db->prepare("SELECT * FROM auth_tokens WHERE selector = ?");
$stmt->execute([$tokenLeft]);
// Now our token data is stored in $row:
$row = $stmt->fetch(PDO::FETCH_ASSOC);
// Delete token after being retrieved
$stmt = $this->db->prepare("DELETE FROM auth_tokens WHERE selector = ?");
$stmt->execute([$tokenLeft]);
// Verify the right hand side, hashed, matches the stored value:
if (!hash_equals($row['hash'], $tokenRightHashed)) {
throw new Exception('Invalid authentication token');
}
return $row['user_id'];
}
}
Usage:
// Post-authentication, before headers are sent:
$cookieValue = $auth->createLongTermToken($userId);
setcookie(
'long_term_auth',
$cookieValue,
time() + (86400 * 30), // 30 days
'',
'',
TRUE, // Only send cookie over HTTPS, never unencrypted HTTP
TRUE // Don't expose the cookie to JavaScript
);
On page load:
if (!isset($_SESSION['user_id']) && isset($_COOKIE['long_term_auth'])) {
try {
$_SESSION['user_id'] = $auth->loginWithPersistentCookie($_COOKIE['long_term_auth']);
} catch (Exception $ex) {
// Security error! Handle appropriately (i.e. log the incident).
}
}
The above code snippets assume that $auth is an instance of the example Authentication class. It also assumes a basic table structure for auth_tokens (user_id points to the users table, selector and hash are VARCHAR or TEXT fields, with a unique constraint on selector).
Why is this code secure?
It separates long-term authentication cookies from the users' passwords.
Tokens are only allowed to be used once.
It uses random_bytes() to generate secure tokens.
It uses SHA256 (instead of MD5) to store the larger piece of the auth. token.
It uses hash_equals() to compare hashes.
It only sends the long-term authentication cookies over HTTPS.
It uses the built-in session management features of PHP.
Why is the question's code insecure?
It uses MD5, which is not a secure hash function.
It uses == instead of hash_equals() to compare hashes. See also: magic hashes.
Furthermore, you'll want to use password_hash() and password_verify() for the actual user authentication steps. This just describes a secure implementation of the "remember me" checkbox convenience feature.
For better security, you'll probably want to use something like password_exposed and/or zxcvbn to prevent weak/compromised passwords from being used.
Your users will want to use a password manager such as KeePassXC, 1Password, or LastPass to generate/store their passwords for your website so they don't end up using weak passwords like admin.

why does in HTTP basic authentication window pop up multiple times even after I enter wrong credentials?

I am trying to implement HTTP basic authentication in my website. The code is given below. When I request the protected resource I get a window that asks for username and password. Why does authentication window pop up when I enter wrong credentials? I know to use a counter and then redirect the users after they reach a limit. But what I don't understand is why the window pops up again. Am I missing something?
<?php
$LoginSuccessful = false;
// Check username and password:
if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])){
$Username = $_SERVER['PHP_AUTH_USER'];
$Password = $_SERVER['PHP_AUTH_PW'];
if ($Username == 'jonas' && $Password == 'foobar'){
$LoginSuccessful = true;
}
}
// Login passed successful?
if (!$LoginSuccessful){
/*
** The user gets here if:
**
** 1. The user entered incorrect login data
** --> User will see the error message from below
**
** 2. Or the user requested the page for the first time
** --> Then the 401 headers apply and the "login box" will
** be shown
*/
// The text inside the realm section will be visible for the
// user in the login box
header('WWW-Authenticate: Basic realm="Secret page"');
header('HTTP/1.0 401 Unauthorized');
print "Login failed!\n";
}
else {
// The user entered the correct login data, put
// your confidential data in here:
print 'you reached the secret page!';
}
?>

php header() Authentication

Can anyone tell me why this authentication won't work?
This is a breakdown:
1) I'm trying to create a simple authentication using WWW-Authenticate with the php header() function.
2) When I go to the page, the authentication box pops up like so:
3) If I remove the script (see script below), the page loads as expected.4) No matter what password I put in or add/remove the Realm, Stale etc, nothing bites and the Authentication box just keeps looping when I click "Log In".
if( $_SERVER['PHP_AUTH_USER'] != NULL && $_SERVER['PHP_AUTH_PW'] != NULL && $_SERVER['PHP_AUTH_USER'] == ‘admin1’ && $_SERVER['PHP_AUTH_PW'] == ‘pwd1’ ) {
$_SESSION['login_flag'] = true;
} else {
header("WWW-Authenticate: Basic realm=\”Schoolroom\”, stale=FALSE");
header("HTTP/1.0 401 Unauthorized");
print "<h1>401 Unauthorized</h1>";
exit();
}
Can anyone tell me what I am doing wrong? I've also tried in multiple browsers and various computers, same issue.
UPDATE: 10:44am PST, July 5 - This is where I am at so far with modified, updated and commented code:
<?php
header('WWW-Authenticate: Basic realm="Secret page"');
header('HTTP/1.0 401 Unauthorized');
// Status flag:
$LoginSuccessful = false;
// Check username and password:
if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])){
$Username = $_SERVER['PHP_AUTH_USER'];
$Password = $_SERVER['PHP_AUTH_PW'];
if ($Username == 'usrnm' && $Password == 'pswrd') {
$LoginSuccessful = true;
}
}
// Login passed successful?
if (!$LoginSuccessful){
/*
** The user gets here if:
**
** 1. The user entered incorrect login data (three times)
** --> User will see the error message from below
**
** 2. Or the user requested the page for the first time
** --> Then the 401 headers apply and the "login box" will
** be shown
*/
// The text inside the realm section will be visible for the
// user in the login box
//header('WWW-Authenticate: Basic realm="Secret page"');
//header('HTTP/1.0 401 Unauthorized');
print "Login failed!\n";
}
else {
// The user entered the correct login data, put
// your confidential data in here:
print 'you reached the secret page!';
}
?>
However, the script does not prompt to login window but instead renders "Login Failed".
We tested it with all available PHP versions 5.0, 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 7.0, 7.1, but to no avail.
It doesn't look like you're testing to see if $_SESSION['login_flag'] is set and also true.
if(isset($_SESSION['login_flag'] && $_SESSION['login_flag'] == true)) {
//load the page without authentication
}
else {
//ask for authentication
}

how to change design /style of the WWW-Authenticate popup box to match my website theme?

Halo there,
I need to change the look /design of the WWW-Authenticate popup box to match my website theme, I don't want it to show the default popup box for authentication when users need to login to secured pages. Below is the PHP script I used to create the WWW-Authenticate popup.
<?php
$_user_ = 'test';
$_password_ = 'test';
session_start();
$url_action = (empty($_REQUEST['action'])) ? 'logIn' : $_REQUEST['action'];
$auth_realm = (isset($auth_realm)) ? $auth_realm : '';
if (isset($url_action)) {
if (is_callable($url_action)) {
call_user_func($url_action);
} else {
echo 'Function does not exist, request terminated';
};
};
function logIn() {
global $auth_realm;
if (!isset($_SESSION['username'])) {
if (!isset($_SESSION['login'])) {
$_SESSION['login'] = TRUE;
header('WWW-Authenticate: Basic realm="'.$auth_realm.'"');
header('HTTP/1.0 401 Unauthorized');
echo 'You must enter a valid login and password';
echo '<p>Try again</p>';
exit;
} else {
$user = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : '';
$password = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : '';
$result = authenticate($user, $password);
if ($result == 0) {
$_SESSION['username'] = $user;
} else {
session_unset($_SESSION['login']);
errMes($result);
echo '<p>Try again</p>';
exit;
};
};
};
}
function authenticate($user, $password) {
global $_user_;
global $_password_;
if (($user == $_user_)&&($password == $_password_)) { return 0; }
else { return 1; };
}
function errMes($errno) {
switch ($errno) {
case 0:
break;
case 1:
echo 'The username or password you entered is incorrect';
break;
default:
echo 'Unknown error';
};
}
function logOut() {
session_start();
session_destroy();
header("Location: index.html");
}
?>
And the following is my code that I use in all pages I need to secure or protect.
<?php
require_once 'auth.php';
echo "You've logged in as {$_SESSION['username']}<br>";
echo '<p>LogOut</p>'
?>
Please help...And recall that the code works fine I only want to change the look. Thanx in advanced :)
Two options:
Javascript
Use javascript to construct a url in the form of http://username:password#domain.com upon form submission, set document.location to the constructed url and the browser will redirect whilst automatically authenticating.
Server Side
Allow the form to submit to itself and use server side code to perform the same redirect - I recommend using a 307 redirect.
In php:
redirect("http://username:password#domain.com", 307);
Downsides
Both versions will still result in the web browser showing the ugly authentication box if the initial username and password submission is incorrect.
If you need to avoid this scenario then you will need to implement your own server side authentication scheme.
More detail
If you can tell me which option you believe you will favour, I can show a little more example code.
Security Implications
There are security implications to using HTTP basic auth. If you do not use SSL then passwords will be sent in cleartext. Using the above methods will send the password in cleartext as part of the URL. There are other security implications too:
https://security.stackexchange.com/questions/988/is-basic-auth-secure-if-done-over-https
Because of these I would always recommend against using http basic auth. Many users may be using the same password for sensitive accounts so it's well worth investing a little time in even a basic authentication system.
You can't change the style of the pop-up box. You will have to create your own login form on your site.

Categories