I've recently started learning html and php. I'd appreciate it if someone could point me in the right direction: I've made a basic webpage that has basic authentication (to enter it). When the user clicks the cancel button, I would like the browser to do something! All it does is, remain on the page it was on (before the user attempted to access my page). I guess I would like it to display the 401 error. Is the only way to do this, to insert text after:
header('www-authenticate: basic');
?
I've tried redirecting 401 errors in the .htaccess file, though it would seem that the 401 error never occurs (although the server access log says that there was a 401 error). When I redirected my 404 error using .htaccess, it worked.
This is the code that I've got for the authentication:
<?php
$user = array('Michael' => 'Mike');
$pass = array('Michael' => 'fish');
$userSuccess = false;
$passSuccess = false;
if(!isset($_SERVER['PHP_AUTH_USER']) && !isset($_SERVER['PHP_AUTH_PW']))
{
header('http/1.1 401 Unauthorized');
header('www-authenticate: basic');
exit;
}
else
{
foreach($user as $value)
if($_SERVER['PHP_AUTH_USER'] == $value)
$userSuccess = true;
foreach($pass as $value)
if($_SERVER['PHP_AUTH_PW'] == $value)
$passSuccess = true;
if(!$userSuccess || !$passSuccess)
{
header('http/1.1 401 Unauthorized');
header('www-authenticate: basic');
exit;
}
}
?>
Also, if I've done anything stupid in my code, feel free to point it out.
I thought that since I sent the 401 header to the server, and the server logged having received it, it would've displayed some text say, 'Error 401: Unauthorised Access' or something along those lines.
Thanks.
you need to serve the browser with the content for 401. This is not done automatically. The 401 header you send is never really seen by the user. Think of it more as a status flag than content. Programs use that header to detect certain issues and act upon them (i.e. a download manager may detect the 404 header which then shows the download line in its window with a red background and error symbol).
In conclusion then, you need to print the error to the browser yourself
You're going on the right way. I'll do just some changes on your code:
$user = array('Mike' => array('Michael', 'fish'));
if(!isset($_SERVER['PHP_AUTH_USER']) && !isset($_SERVER['PHP_AUTH_PW']))
{
header('http/1.1 401 Unauthorized');
header('www-authenticate: basic');
exit;
}
else
{
$input_user = $_SERVER['PHP_AUTH_USER'];
$input_pw = $_SERVER['PHP_AUTH_PW'];
if($user[$input_user][1] != $input_pw)
{
header('http/1.1 401 Unauthorized');
header('www-authenticate: basic');
exit;
}
}
echo "Hi, " . $user[$input_user][0];
First of all I've changed your users array layout on this layout to this:
Array
(
[Mike] => Array
(
[0] => Michael
[1] => fish
)
)
Read for further info Multidimensional Arrays.
Secund, is your username, then it will have another array with name and password of the user. So you'll not have to user foreach to match your username and pass. BTW, the way you done before your script will allow any user to access with another users pass. If you have other user foo and try user = foo and pass = fish it will gain the access.
Third thing is that you don't need to use $userSuccess and $passSuccess no more. Just to decrease code lenght.
Then I associate two vars to the _SERVER vars, to make easy their use. So you can match then with this sentence: $user[$input_user][1] != $input_pw.
I hope this can helps you.
Related
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!';
}
?>
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
}
I have a table in my database for users, each user has an auth_level (1-3). On my site I have a admin page for adding/deleting new user accounts, I want to restrict access to this page only to users that have an auth level of 3.
I have been trying to get this to work with sessions but I am stuck. Hoping someone can help me out.
Here is my code for restricting access. Not sure where I have gone wrong.
the check.php start the session.
<?php
require_once('check.php');
//This function returns True if auth level = '3'
//Otherwise it returns False
function CheckAccess()
{
$result = (isset($_SESSION['SESS_AUTH_LEVEL']) && $_SESSION['SESS_AUTH_LEVEL'] == 3 );
if(!$result)
{
header('WWW-Authenticate: Basic realm=“Test restricted area”');
header('HTTP/1.0 401 Unauthorized');
return false;
}
else
{
header("location: admin.php");
}
}
?>
If I log in as a user with auth level 1 or 3, I get the same blank page.
I think you never call CheckAccess(). Calling it maybe fixes your error.
It's perfectly valid to do the following, which your function is doing.
if (this=that) { /*do multiple*/ } else //do single;
However, it appears you are not calling this function? Try this:
<?php
require_once('check.php');
CheckAccess();
?>
You are missing the closing } of your function so all the if statement is seen as part of the method by PHP.
Add a } after $result = and before if
A slightly alternative version that should achieve what you want would be
<?php
function CheckAccess() {
$result = (isset($_SESSION['SESS_AUTH_LEVEL']) && $_SESSION['SESS_AUTH_LEVEL'] == 3 );
return $result;
}
if (CheckAccess()) {
header("location: admin.php");
} else {
header('WWW-Authenticate: Basic realm=“Test restricted area”');
header('HTTP/1.0 401 Unauthorized');
}
The problem is that there is already output before the headers are send:
this need to be changed
<?php
require_once('check.php');
?>
<?php
//This function returns True if auth level = '3'
//Otherwise it returns False
function CheckAccess()
{
...
to this
<?php
require_once('check.php');
//This function returns True if auth level = '3'
//Otherwise it returns False
function CheckAccess()
{
...
because the headers needs to be sent first then the content :)
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.
I'm trying to incorporate PagSeguro (a payment gateway - Brazil's version of PayPal) into my site. After the customer finishes with PagSeguro, they send data (via POST) to a function which I specify. However, I'm not receiving the POST. After doing all the troubleshooting I could think of, I contacted PagSeguro. They said that their log indicates that the POST is being sent as normal but they are receiving an HTTP 302 response.
In order to figure out why this is happening, I created a form with hidden values to simulate sending a POST to my function. I put this form under a different domain just in case it had something to do with that. Every time I send the POST from my simulation form, I receive an HTTP 200 response, and my log indicates that the POST was received.
How is it possible that PagSeguro is receiving a different response than my simulation? Could it have something to do with the server or is it something to do with my script?
Here is the function (using CodeIgniter) that should be receiving the POST:
function pagseguro_retorno(){
if (count($_POST) == 0) {
return FALSE;
}
$msg = 'POST RECEIVED';
$simulate = $this->input->post('Simulate');
if ( ! empty($simulate)){
$result = 'VERIFICADO';
$msg .= ' FROM SIMULATOR';
} else {
$this->load->library(PagSeguroNpi);
$result = $this->PagSeguroNpi->notificationPost();
}
$this->log($msg);
if ($result == "VERIFICADO") {
$id = $this->input->post('Referencia');//cart id
$this->load->model('transacao_model');
$trans_row = $this->transacao_model->get_transaction($id);
if ( ! is_object($trans_row)){
//LOAD NEW TRANSACTION
if ( ! $this->new_transaction($id)){
$notice = "Unable to load new transaction</p><p>";
$this->log($notice);
$notice .= '<pre>'.print_r($_POST, TRUE).'</pre>';
$this->email_notice($notice);
}
}
$this->load->model('carrinho_model');
if($_POST['StatusTransacao'] == 'Aprovado'){
$status = 'apr';
}elseif($_POST['StatusTransacao'] == 'Em Análise'){
$status = 'anl';
}elseif($_POST['StatusTransacao'] == 'Aguardando Pagto'){
$status = 'wtg';
}elseif($_POST['StatusTransacao'] == 'Completo'){
$status = 'cmp';
//nothing more happens here - client must click on 'mark as shipped' before cart is removed and history data is loaded
}elseif($_POST['StatusTransacao'] == 'Cancelado'){
//reshelf - don't set $status, because the cart's about to be destroyed
$this->carrinho_model->reshelf(array($id));
}
if (isset($status)){
$this->carrinho_model->update_carrinho($id, array('status' => $status));
}
} else if ($result == "FALSO") {
$notice = "PagSeguro return was invalid.";
$this->log($notice);
$notice .= '<pre>'.print_r($_POST, TRUE).'</pre>';
$this->email_notice($notice);
} else {
$notice = "Error in PagSeguro request";
$this->log($notice);
$notice .= '<pre>'.print_r($_POST, TRUE).'</pre>';
$this->email_notice($notice);
}
}
SECURITY UPDATE:
After posting, I soon realized that I was opening myself up to hack attempts. The function necessarily has to be public, so anyone who knows the name of the function could access it and post 'simulate' to get immediate verification. Then they could pass whatever data they wanted.
I changed the name of the function to something that would be impossible to guess, and when not in production mode, I've disabled the simulate option.
The problem was that my CSRF protection causes a redirect when the proper CSRF code isn't sent with the POST. My simulator was working, because I copied the HTML generated by a dynamic simulator that I made on the site in question. I then pasted the HTML to create a static simulator and put it under a different URL. Along with the HTML, I inadvertently pasted the CSRF code as well. The static simulator worked fine until the code expired. I quit using it after a couple times, so I didn't discover that it was no longer working until today.
I know that this exact scenario probably won't happen again in the next 500 years, but things like CSRF security can cause problems with things like payment gateways if not disabled for the function receiving the POST.