Skip to content

Instantly share code, notes, and snippets.

@eric1234
Last active March 27, 2024 07:15
Show Gist options
  • Star 35 You must be signed in to star a gist
  • Fork 14 You must be signed in to fork a gist
  • Save eric1234/4692807 to your computer and use it in GitHub Desktop.
Save eric1234/4692807 to your computer and use it in GitHub Desktop.
Protect page with simple password

Purpose

Unauthorized access to a PHP page prompts the user for a password. Once the password is entered the original page will show.

Features

  • The access is recorded in the session so it only needs to be entered once.
  • It is possible for multiple pages to share the same scope so access to one page grants access to another page.

Usage

Protecting the Page

Add to the top of the page you want to protect:

require_once 'protect.php';
Protect\with('form.php', 'my_password');

Password Request Form

Now we need to provide a page to prompt the user for the password. It can look like anything you want. The only two requirements are:

  • It must POST the form (i.e. action="POST")
  • The password field must be named password

The following is an example form:

<html>
<body>

<form method="POST">
  <?php if( $_SERVER['REQUEST_METHOD'] == 'POST' ) { ?>
    Invalid password
  <?php } ?>
  <p>Enter password for access:</p>
  <input type="password" name="password">
  <button type="submit">Submit</button>
</form>

</body>
</html>

Place this in the form.php file (or whatever the first argument to the with method is).

Shared Scope

If you need to secure multiple pages and want access on one page to grant access on all pages simply provide a third argument to with. The value of this argument can be anything you desire. It just needs to be the same for all pages. You probably also want to make the form and password the same for all pages.

<?php
# https://gist.github.com/4692807
namespace Protect;
# Will protect a page with a simple password. The user will only need
# to input the password once. After that their session will be enough
# to get them in. The optional scope allows access on one page to
# grant access on another page. If not specified then it only grants
# access to the current page.
function with($form, $password, $scope=null) {
if( !$scope ) $scope = current_url();
$session_key = 'password_protect_'.preg_replace('/\W+/', '_', $scope);
session_start();
# Check the POST for access
if( $_POST['password'] == $password ) {
$_SESSION[$session_key] = true;
redirect(current_url());
}
# If user has access then simply return so original page can render.
if( $_SESSION[$session_key] ) return;
require $form;
exit;
}
#### PRIVATE ####
function current_url($script_only=false) {
$protocol = 'http';
$port = ':'.$_SERVER["SERVER_PORT"];
if($_SERVER["HTTPS"] == 'on') $protocol .= 's';
if($protocol == 'http' && $port == ':80') $port = '';
if($protocol == 'https' && $port == ':443') $port = '';
$path = $script_only ? $_SERVER['SCRIPT_NAME'] : $_SERVER['REQUEST_URI'];
return "$protocol://$_SERVER[SERVER_NAME]$port$path";
}
function redirect($url) {
header("Location: $url");
exit;
}
@javamel
Copy link

javamel commented Apr 12, 2019

Hi there. Thanks for this great code. How could I create a button to end the session in order to close access to the protected page ? Thanks.

@dexcuit
Copy link

dexcuit commented May 31, 2020

always get error

Warning: session_start() [function.session-start]: Cannot send session cache limiter - headers already sent (output started at /srv/disk6/1363528/www/myhomedecor.c1.biz/go/css.php:23) in /srv/disk6/1363528/www/myhomedecor.c1.biz/go/protect.php on line 14

any help please

@eric1234
Copy link
Author

eric1234 commented Jun 2, 2020

@dexcuit - If you are getting that error that means you are sending content prior to session_start. session_start sends a HTTP Header to set the session cookie. If you have already sent content then you cannot call session_start as that sends a HTTP header which much happen before page conent.

@dexcuit
Copy link

dexcuit commented Jun 2, 2020

eric1234 - your answer make me cry
but thank you any way

@erdengurkan
Copy link

erdengurkan commented Jul 1, 2020

@eric1234 Thanks this works great for single page. I'm trying to use for multiple pages under same directory. Ive added below code on every php page on the same directory.

require_once 'protect.php';
Protect\with('form.php', 'my_password');

It works but asks for password for every page.

Is it possible to share the session with other .php pages within the directory without reentering the password for each page ?

Many thanks in advance!

@eric1234
Copy link
Author

eric1234 commented Jul 3, 2020

@erdengurkan - Yes, that is what the "scope" parameter is for. If every page has the same scope they will share the same session key.

@erdengurkan
Copy link

@eric1234 Thanks a lot for your reply, I've been researching about setting scope but couldn't make it work yet but thanks a lot for showing a direction I will research further

@Pan-dodo
Copy link

Exactly what I have been looking for. Great code. Thank you very much.

@alexnathanson
Copy link

Great code! On line 19 why do you use redirect(current_url()); but line 23 just uses return? Also, is it necessary to separate the HTML form from the PHP? Thanks!

@Baljeet21
Copy link

Is there any possibility of logging out?

@eric1234
Copy link
Author

@alexnathanson - With regard to redirect vs return, either could work and using return would obviously simplify the code as you then don't need the redirect and current_url methods. But when the page is rendered it is in response to a POST request which can cause user confusion if they hit the refresh for example as it will ask them if they want to re-submit the form. By doing a redirect we turn it back into a GET request as they originally requested which results in a slightly cleaner experience for the user. Yes, you could put the form inline vs as a separate file. I just try to separate code logic from markup so having them as separate files makes it a bit cleaner as the form can then be designed and styled separate from the authentication logic.

@Baljeet21 - To logout you would need to delete the session variable. Some sort of button to signal to the script to do this delete.

@Baljeet21
Copy link

Thank you. I used this method from stackoverflow: https://stackoverflow.com/questions/37548496/using-onclick-to-destroy-session and everything works perfectly

@Ol-manGrunge
Copy link

I need to protect one file and one folder in my php based script. I am not a developer and have no training in PHP. Would it be possible to talk me through this?

@Ol-manGrunge
Copy link

Further to the above post, I would be happy to pay for this service.

@eric1234
Copy link
Author

I would suggest reaching out to a local PHP user's group. You should be able to find a contractors that could help you with this.

@Ol-manGrunge
Copy link

Thank's Eric.

@Gygmus
Copy link

Gygmus commented Dec 4, 2021

Hi, amazing code.
Im getting through this a but i gota little of notices from php. Could you pleaase take a look and tell me what could i do wrong?
I'm a newbie xd. Ty.:

Notice: Undefined index: password in C:\xampp\htdocs\website\protect.php on line 17

Notice: Undefined index: password_protect_http_localhost_website_protectedpage_php in C:\xampp\htdocs\website\protect.php on line 23

@St0wy
Copy link

St0wy commented Jul 25, 2022

Thanks for this, I modified it a bit to remove warnings and to not read from post directly. It also uses a hash for the password so that it is not in clear in the code :

<?php
# https://gist.github.com/4692807
namespace Protect;

/**
 * Will protect a page with a simple password. The user will only need
 * to input the password once. After that their session will be enough
 * to get them in.
 *
 * @param string $form Location of the form file
 * @param string $passwordHash Hash of the good password.
 * @param string $scope Scope of the password. It allows access on one page to
 * grant access on another page. If not specified then it only grants
 * access to the current page.
 * @return void
 */
function with($form, $passwordHash, $scope = null)
{
	if (!$scope) $scope = current_url();
	$session_key = 'password_protect_' . preg_replace('/\W+/', '_', $scope);

	if (session_status() == PHP_SESSION_NONE) session_start();

	// If user has access then simply return so original page can render.
	if (isset($_SESSION[$session_key])) return;

	// Check that a password has been posted
	if (filter_has_var(INPUT_POST, 'password'))
	{
		$recievedPassword = filter_input(INPUT_POST, 'password', FILTER_SANITIZE_STRING);

		// Check the POST for access
		if (password_verify($recievedPassword, $passwordHash))
		{
			$_SESSION[$session_key] = true;
			redirect(current_url());
		}
	}

	require $form;
	exit;
}

#### PRIVATE ####

function current_url($script_only = false)
{
	$protocol = 'http';
	$port = ':' . $_SERVER["SERVER_PORT"];
	if (isset($_SERVER["HTTPS"])) $protocol .= 's';
	if ($protocol == 'http' && $port == ':80') $port = '';
	if ($protocol == 'https' && $port == ':443') $port = '';
	$path = $script_only ? $_SERVER['SCRIPT_NAME'] : $_SERVER['REQUEST_URI'];
	return "$protocol://$_SERVER[SERVER_NAME]$port$path";
}

function redirect($url)
{
	header("Location: $url");
	exit;
}

@vietbuiminh
Copy link

So at the beginning, you mentioned that add the <?php require_once 'protect.php'; Protect\with('form.php', '123'); ?>
at the top of the page.
but for my case. adding this at the bottom of the php will help to load all the content which will get it appear after the input of the correct password.

@afoster1955
Copy link

StOwy, where in your code is the password hashed? Am I missing something?

@St0wy
Copy link

St0wy commented Jan 21, 2023

@afoster1955 So when you call the function with() you need to provide the hashed correct password to it as the second argument. For this you need to hash the password with password_hash (I did it in a terminal with the php interpreter). As the first argument you just give the password in clear, and it's gonna be verified with password_verified.
I hope this helps !

@afoster1955
Copy link

Thanks for the response...I have hashed the password but can't figure out where in the code to place it. Can you be specific? Thanks.

@St0wy
Copy link

St0wy commented Jan 21, 2023

@afoster1955 the usage is basically the same as in the readme above :

require_once 'protect.php';
Protect\with('form.php', 'my_hashed_password');

It is placed in the "my_hashes_password" string.

@afoster1955
Copy link

OK, I must be denser than I thought. I have hashed the password that is in this code: require_once 'protect.php'; Protect\with('form.php', 'my_hashed_password');

I thought that the hashed password was placed in the code you provided...not in the code that is placed in the page to be protected. I am so confused.

@St0wy
Copy link

St0wy commented Jan 21, 2023

You could remove the parameter $passwordHash from the function with() and replace it with your hashed password if you really want to

@afoster1955
Copy link

I am just not getting it.
What should be entered in the code Protect\with('form.php', 'my_hashed_password'); The password in the clear or the hashed password?

Next, is the hashed password entered anywhere in the protect.php code you supplied, and if so where is it placed. Perhaps if you can break it down like that I may be able to figure it out. Thanks for your patience.

@St0wy
Copy link

St0wy commented Jan 21, 2023

You should not modify the protect.php file at all. You should just copy it in your project and leave it as is. Then, on the page you want to protect with this code, you add the two lines I provided above. As indicated by both the comments, the name of the parameter and the example string I used, you should enter the hashed password (NOT the password in clear) in the with function.

This means that the two lines might look like that :

require_once 'protect.php';
Protect\with('form.php', ' $2y$10$fU8lgwPd6C4.B2GEWn716.RphJKljJIeTH8.cdT.Wx210KZ.vH7O.');

And this is all you have to do. The protect.php file recieves the hashed password through the second argument of the with function, and this is all it needs to check against the one entered by the user in the form.

@afoster1955
Copy link

I must not have the hashed password hashed correctly then, because I keep getting the Invalid password error message when trying to access the protected page. I used the following code to hash the password...is that a problem?

`<?php

/* User's password. /
$password = password_to_be_hashed';
/
Secure password hash. */
$hash = password_hash($password, PASSWORD_DEFAULT);
echo $hash;

?>`

@afoster1955
Copy link

I don't know what I did, but it is now working in that it takes me to the protected page. The problem I am having now is that every time I do something in the protected page, it takes me back to the enter the password page. Where am I making a mistake?

@rrafluap
Copy link

First, thank you St0wy for the tweaks to make this work again!

I have another issue which is that as of PHP 8.1.0 FILTER_SANITIZE_STRING is deprecated. Therefore the flowing line of code now throws a depreciated warning: $recievedPassword = filter_input(INPUT_POST, 'password', FILTER_SANITIZE_STRING);

Does anyone know of a suitable function to replace this?

TIA

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment