Undaunted auth php. HTTP Installing protection on a page using MySQL and PHP. Application. Script sources

To send an "Authentication Required" message to the client browser, which in turn will cause a dialog box to appear for entering the username and password. After the client has entered his name and password, the script will be called again, but with the predefined variables PHP_AUTH_USER, PHP_AUTH_PW and AUTH_TYPE, which respectively contain the username, password and authentication type. These variables can be found in the $_SERVER and $HTTP_SERVER_VARS array. Currently only "Basic" authentication is supported. You can also read a more detailed description of the function header() .

An example of a script fragment that forces the client to log in to view the page:

HTTP Authentication Example

if (!isset($_SERVER [ "PHP_AUTH_USER" ])) (
header( "WWW-Authenticate: Basic realm="My Realm"");

echo "Text sent when
if the user clicked the Cancel button"
;
exit;
) else (
echo
"

Hello ($_SERVER["PHP_AUTH_USER"]).

" ;
echo "

You have entered your password ($_SERVER["PHP_AUTH_PW"]).

"
;
}
?>

Compatibility Note: Be especially careful when specifying HTTP headers. To ensure maximum compatibility with the largest number different clients, the word "Basic" must be written with a capital "B", the region (realm) must be enclosed in double (not single!) quotes, and exactly one space must precede the code 401 in the title HTTP/1.0 401 .

Instead of simply displaying the PHP_AUTH_USER and PHP_AUTH_PW variables on the screen, you may need to check that they are correct. To do this, use a request to database or searching for a user in a dbm file.

You can observe the features of the browser Internet Explorer. It is very picky about the parameters of the transmitted headers. Specifying a title WWW-Authenticate before sending the HTTP/1.0 401 status is a little trick.

As of PHP 4.3.0, in order to prevent someone from writing a script that reveals the password for a page that uses external authentication, the PHP_AUTH variables are not set if this page uses external authentication and is set to secure mode. However, the REMOTE_USER variable can be used to authenticate an externally authenticated user. So you can always use the $_SERVER["REMOTE_USER"] variable.

Note: PHP uses the AuthType directive to indicate whether external authentication is used or not.

It should be noted that all of the above does not prevent the theft of passwords to pages that require authorization by someone who controls pages without authorization located on the same server.

Both Netscape Navigator and Internet Explorer clear the current window's authentication cache for a given realm when received from the server. This can be used to force the user to log out and re-display the username and password dialog box. Some developers use this to time-limit logins or provide a logout button.

Example of HTTP authentication with forced entry of a new login/password pair

function authenticate() (
header( "WWW-Authenticate: Basic realm="Test Authentication System"");
header("HTTP/1.0 401 Unauthorized");
echo "You must enter the correct username and password to gain access to the resource \n";
exit;
}

If (!isset($_SERVER [ "PHP_AUTH_USER" ]) ||
($_POST [ "SeenBefore" ] == 1 && $_POST [ "OldAuth" ] == $_SERVER [ "PHP_AUTH_USER" ])) (
authenticate ();
}
else(
echo
"

Welcome: ($_SERVER["PHP_AUTH_USER"])
" ;
echo "Previous login: ($_REQUEST["OldAuth"])";
echo "

\n";
echo "\n";
echo "\n";
echo "\n";
echo "

\n" ;
}
?>

This behavior is not governed by the HTTP Basic authentication standards, so you should not depend on it. Tests have shown that the Lynx browser does not clear the authorization cache when receiving a 401 status from the server, and by clicking “Back” and then “Forward” in sequence, it is possible to open such a page, provided that the required authorization attributes have not changed. However, the user can press the "_" key to clear the authentication cache.

It should also be noted that prior to PHP 4.3.3, HTTP authentication did not work on servers running Microsoft IIS if PHP was installed as a CGI module, due to some IIS limitations. In order to achieve correct operation in PHP 4.3.3+, you must edit the IIS configuration setting called "Directory Security". Click on the "Edit" inscription and set the "Anonymous Access" option, all other fields should remain unchecked.

Another limitation if you are using IIS via ISAPI: the PHP_AUTH_* variables are not defined, but at the same time the HTTP_AUTHORIZATION variable is available. Example code you could use: list($user, $pw) = explode(":", base64_decode(substr($_SERVER["HTTP_AUTHORIZATION"], 6)));

Note regarding IIS:: In order for HTTP authentication to work correctly in IIS, the cgi.rfc2616_headers option in the PHP configuration must be set to 0 (the default value).

Attention: In case protected mode is used, the UID of the current script will be added to the realm part of the WWW-Authenticate header.



<<< Назад Content Forward >>>
If you have any other questions or something is not clear - welcome to our

Restricting access to any area of ​​the site usually looks like
monotonous: each user is given a login and password or he himself
selects them, and to enter the secure part of the site they must be entered. From a technical point of view, to check the password they use
different methods. An HTML form can be used to enter your login and password.
In this case, the password is sent to the server in clear text in the POST request.
This is unacceptable if the user is local, where possible
using a sniffer. To solve this problem, a method was invented
authentication using hashes, in which the password is not transmitted, but
a hash string is transmitted, depending on the password, some one-time
parameter and, possibly, from some other parameters. This method is also
called challenge/response, because when using it the client
receives a request with a one-time parameter and sends a response containing the hash. At the HTTP 1.1 protocol level, authentication using the
Basic, which is nothing better use HTML forms, and Digest, which
we will look at it in detail.

When using the Digest method, as already mentioned, the password
is not transmitted and cannot be sniffed, but there is another side
Problems. In order to check the password, the server must calculate
response and compare it with the client response, therefore, the server must
store the password or data dependent on it necessary for
passing authentication. It follows that a person who has received rights
to read accounts (for example, using SQL injection), will be able to get
access to pages protected by the Digest method. When using the method
Basic, it is possible to store hashes instead of passwords, which does not allow you to raise rights,
after reading these hashes (we will see below that Digest can also store hashes,
but such that their knowledge is sufficient to calculate the answer). Thus, we are faced with a dilemma: either our password will be sniffed,
or they will get it through a web vulnerability, which someone will definitely find,
because whoever seeks will always find. There is an authentication method without
Both of these disadvantages are the public key authentication method:
to verify you need a public key, and to pass verification you need a secret key,
however, HTTP 1.1 does not provide such a method. RFC 2069
recommends using SSL if security is so important. Only the transmission of the password is protected, and the content is not encrypted, so
that there is no point in protecting resources with this method, where the user is from
receives secret information. They require SSL. And it makes sense
protect, for example, a forum or uploading content to a website. So, if the hosting does not support SSL, and authentication must
to be safe, we will use Digest. Apache provides the mod_digest module. To use it
in the config (or in .htaccess) we write:

AuthType Digest
AuthUserFile<файл>
AuthName<название защищаемой области>
Require valid_user

User files are created by the utility
htdigest. At one time there were reports about mod_digest that it was vulnerable, so
Perhaps some other problems will appear there. Moreover, when
I tried to use it at home, I got an error
500 Server Internal Error. In addition, if adding accounts should occur
automatically, and there should be a lot of them, they should
stored not in the Apache config, but in MySQL. Solution -
use PHP. PHP doesn't have built-in support for this
method, so it will have to be implemented. To do this you need to study
this method in detail. Let me immediately note that given in this article
the implementation only works on Apache, since full access to headings
request (apache_request_headers function) only works in Apache, but on
may not be available on other servers. We just need to read
Authorization header.

Description of the method

The full description of the method can be read in RFC 2069, and if
In short, the method works like this. When the server receives a request related to the protected area,
it throws error 401 Authorization Required and request header
authentication of this type:

WWW-Authenticate: Digest realm="secure area", nonce="123456123456"

realm is the name of the protected area, and nonce is a one-time use
meaning. There are also optional parameters that we will discuss
we will not. The client repeats the request, adding a header like this:

Authorization: Digest realm="secure area", username="123", uri="/index.php", nonce="123456123456", response="1234567890abcdef1234567890abcdef"

The uri parameter must match the URI in the request, and the response is
the answer, which is calculated like this:

response = H(H(A1) + ":" + nonce + ":" + H(A2))
H - hash function, default MD5
A1 = login + ":" + realm + ":" + password
A2 = request method + ":" + URI
The request method is GET, POST, etc.

As we can see, A1 does not depend on either the request or the one-time
values, so the server may store not a password, but
H(A1). This is exactly how it is implemented in mod_digest in Apache.
However, the same data is sufficient for the client. The attacker, having received
this hash can calculate the answer using the above formulas and
generate an HTTP request, for example, using the program
AccessDriver and its HTTP tool
Debugger. This process will be shown in more detail below. The server must check if the value is a nonce
the one that was previously issued to the client and whether it is out of date.
If the response matches the nonce parameter, but the value of that parameter
not relevant, the response described above with code 401 is issued only because
the difference is that the parameter is added to the WWW-Authenticate header
stale=true, indicating that access is denied for this reason only,
and should try again without prompting the user for a new password. This, IMHO, is inconvenient, because if such a situation arises
when making a POST or PUT request with a large block of data, the client will have to
transmit all data twice. To avoid this, the standard provides
Authentication-Info header, in which the server may respond to
successful request to tell the client the next nonce.
The syntax is the same as WWW-Authenticate, except that the nonce
is replaced by nextnonce. However, judging by the results of my
experiments, Opera ignores this header. Another solution: according to
RFC 2068 (HTTP/1.1), the server may respond before the request completes,
so that the client interrupts unnecessary data transfer, but in Apache+PHP this is
is not implemented, since the script starts executing only after
how Apache will fully receive and parse the request.

Storing data between requests

There is a subtle point in implementing the challenge/response method in PHP.
A one-time parameter is generated and issued to the client in one response, and
is checked in another session of the script.
That is, it must be saved from one script call to another, and for this you will have to
use files or database. My example uses files named
corresponding to one-time values, and the files themselves contain
IP addresses of the clients to whom they are issued. Collection is not implemented in the example
garbage: you need to periodically delete old files.

Code parsing

This script only checks the password, and works regardless of
login Depending on the success of the check, simple answers are given.

$realm = "secure area"; // Name of protected area
$pass = "pass"; // Password
$fileprefix = "./"; // Path for tag files indicating the validity of the nonce

/* Let's construct a one-time parameter as recommended in RFC2069, although it can be done differently. The parameter, as recommended, should depend on the client address, current time and secret string. */
$nonce = md5($_SERVER["REMOTE_ADDR"] . ":" . time() . ":MyCooolPrivateKey");

// Getting the headers
$headers = apache_request_headers();

// The flag that we will set to TRUE upon successful verification
$auth_success = FALSE;
$stale = "";

// If there is no Authorization header, then there is nothing to check
if (isset($headers["Authorization"]))
{
$authorization = $headers["Authorization"];

/* Let's parse the title using a regular expression. The title contains the word "Digest" and a list
parameters of the form param="value" or param=value separated by commas. This regular expression matches one such parameter.
*/
preg_match_all("/(,|\s|^)(\w+)=("([^"]*)"|([\w\d]*))(,|$)/",
$authorization, $matches, PREG_SET_ORDER);

/* Now, for the convenience of further processing, let’s create an array, where the keys are the names of the parameters, and the values ​​of the array elements are
parameter values.
*/
$auth_params = Array();
for ($i = 0; $i< count($matches); $i++)
{
$match = $matches[$i];

/* The name is always in the second group of brackets, depending on whether it is in quotes or not, it can
be in the 4th or 5th group. For groups of brackets that fall
to an unimplemented branch, an empty string in the array,
so you can simply add the values.
*/
$auth_params[$match] = $match . $match;
}

/* Let's calculate the answer, which corresponds to
the login entered by the user, our password and a one-time parameter passed by the user.
*/
$a1 = $auth_params["username"] . ":" . $auth_params["realm"] . ":" . $pass;
$a2 = $_SERVER["REQUEST_METHOD"] . ":" . $_SERVER["REQUEST_URI"];
$resp = md5(md5($a1) . ":" . $auth_params["nonce"] . ":" . md5($a2));

// Let's check the answer.
if ($resp == $auth_params["response"])
{
//
Checking the relevance of the one-time parameter
$fn = $fileprefix . $auth_params["nonce"];
if (@file_get_contents($fn) == $_SERVER["REMOTE_ADDR"])
{
unlink($fn); //
This option is no longer relevant
$auth_success = TRUE; //
Authentication passed
) else
{
// One-time parameter is irrelevant
$stale = ", stale=true";
}
}
}

if ($auth_success)
{
print(" Digest auth test

print("Successfully authenticated\n");
var_dump($auth_params);

print("");

) else
{
file_put_contents($fileprefix . $nonce, $_SERVER["REMOTE_ADDR"]);

$proto = $_SERVER["SERVER_PROTOCOL"];
Header("$proto 401 Not Authorized");
Header("WWW-Authenticate: Digest realm=\"$realm\", nonce=\"$nonce\"$stale");

print(" Digest auth test

");
print("You must authenticate with Digest method");
print("
");
}

Passing Digest Auth with known H(A1)

I’ll show you with an example how to pass verification if the password is unknown,
but H(A1) is known. For this, as already mentioned, you will need
AccessDriver. I will do hash calculations by calling from the command line
PHP CLI. Let the protected page be located at
http://mrblack.local/auth1.php and the H(A1) hash is "a8fb5b2d780a7bf0782207a51a013f04".

Open AccessDriver->Tools->HTTP Debugger and enter the address
"http://mrblack.local/auth1.php". Click "Connect". We get:

HTTP Header = HTTP/1.1 401 Authorization Required
HTTP Header = Date: Mon, 04 Jul 2005 08:09:17 GMT
HTTP Header = Server: Apache/1.3.31 (Win32) PHP/5.0.2
HTTP Header = X-Powered-By: PHP/5.0.2
HTTP Header = WWW-Authenticate: Digest realm="secure area", nonce="5925bea78552224abda11bfe318a8a03"
HTTP Header = Connection: close
HTTP Header = Content-Type: text/html

Open the console, go to the folder with PHP and enter the following command:

php -r "print md5("a8fb5b2d780a7bf0782207a51a013f04:
: ".md5("GET:http://mrblack.local/auth1.php"));"

We get the required Digest answer: c6d0af0db239d75c
3f59640a4896d096
Now in AccessDriver, check the "Header Data" box, copy it to the appeared
field headers that were sent in the previous request, and add to them
Authorization. Here's what happens:

GET http://mrblack.local/auth1.php HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, */*
Accept-Language: en-us,en;q=0.5
User-Agent: Mozilla compatible
Host: mrblack.local
Pragma: no-cache
Authorization: Digest username="mrblack", realm="secure area", nonce="5925bea78552224ab
da11bfe318a8a03", uri="http://mrblack.local/auth1.php", response="c6d0af0db239d75c3f59
640a4896d096"

Click "Connect". We get the result:

Sometimes it is necessary to close it from outside access PHP page if you are making a closed area of ​​the site. This could be some hidden information for your clients or site visitors, some kind of administrative interface for you, etc. You can come up with hundreds of different tasks that require access restrictions.

You can close such a page in several complementary ways:

  1. Password protection (login/password) using variables $_SERVER["PHP_AUTH_USER"] And $_SERVER["PHP_AUTH_PW"].
  2. Protection by IP client address using a variable $_SERVER["REMOTE_ADDR"].
  3. Protection by MAC address in local networks (in addition to protection by IP).

Let's first look at the first method, which is the main one. It allows you to block access to the page using your login and password, so only people who know the login and password can gain access. In addition, they can be divided according to this criterion and, accordingly, different information can be provided for each. Implemented by issuing special fields in the protocol header HTTP. Let's create a function auth_send():

" ,"

Authentication Error

" ,"

Contact the administrator to obtain your login and password.

" ,""; exit; ); ?>

This function informs the browser that access requires authorization using a login and password. And it also displays a page in HTML for the user.

" ,"

Welcome!

" ,"

You are logged in using the login ",$auth_user," and the password ",$auth_pass,".

" ,""; ?>

The login and password verification code is not too complicated in this case, since it is implemented for one person. The operating logic is simple if there is no variable $_SERVER["PHP_AUTH_USER"] And $_SERVER["PHP_AUTH_PW"] or their values ​​do not match the required ones, then call the function auth_send(). Don't forget that at the end it calls exit, so the program execution stops.

The next level of protection is implemented by filtering IP addresses of the connecting client. Of course, on the Internet, many providers issue IP temporary addresses and this protection is useless, but if we are talking about corporate local networks, then this check will provide additional protection.

Your IP was not found!!!"; exit; ); ?>

Here in the line $allowed_ips indicated separated by spaces IP addresses that are allowed access. Next we get the array using explode() and search for the client's address from $_SERVER["REMOTE_ADDR"]. I used the function to search array_search(), since it’s unlikely that its code implemented in C will work somewhat faster than what we can write in PHP using loops for or foreach. But speed is not the main thing here :)

And the last step of protection is verification MAC addresses. It is classified as paranoid and should be used if you are accessing from a local network and the data you are protecting is really very important. I have so far implemented this check only on the system Linux, due to the relative simplicity of implementation. But you can try to implement it for any other platform. We write a function:

As Linux users have already understood, it is based on ARP system table, which can be accessed using a file /proc/net/arp. The function searches strings for the required IP address and returns it MAC address:

Your IP=192.168.10.15 and MAC=00:04:31:E4:F8:37

In system Windows Perhaps there are also some ways to get MAC simpler, but from those that actually work, this is the conclusion ARP system tables with the command:

C:\WINDOWS\>arp -a Interface: 192.168.10.15 on Interface 0x1000003 IP Address Physical Address Type 192.168.10.1 00-50-22-b0-6a-aa dynamic 192.168.10.2 00-0f-38-68-e9- e8 dynamic 192.168.10.3 00-04-61-9e-26-09 dynamic 192.168.10.5 00-0f-38-6a-b1-18 dynamic

You can implement protection based on this address yourself if you really need it :) But remember that if you have unmanaged equipment on your network without the ability to bind MAC addresses to the port, this protection may not work, since all your identification data used for protection (login, password, IP and MAC address) can be faked.

Introduction

This is a tutorial designed to show you the basics of securing your web pages using HTTP authentication. Instead of the traditional .htaccess method (Apache server), we are going to use MySQL to store user data and their passwords. I will try to explain as much as possible what, in my opinion, is required for a beginner to learn MySQL and PHP. In principle, based on this program you can use any DBMS (database management system). Why is this method interesting? Well, for example, if only because if you use a database, you can easily allow only a certain group (person) to have certain rights to access this or that information. If you use the traditional .htaccess Apache method, you must manually add users and password in the password file. And the advantage this method, well....take a look for yourself.

Software which is necessary:

· *nix platform (Linux, Unix, *BSD) · PHP 3.0.x or PHP 4.x · MySQL (any version)

Step number one

Go ahead, the first thing we need to figure out is that we want to allow users who are in our database to access the specified page? And how are we going to do this? (many people don’t really like it, but we need to get used to taking a piece of paper before programming and writing down all the requirements that we want to get from the program, in the long run you will save hours or maybe days for making changes to the code (approx.))

Check if the user is already authenticated.

If not, send a message to the browser, with a message and an access form.

If the user clicks on the cancel button, do not allow him access and redirect him to... 403: Access denied, or show (cookie J) a simple message.

If the user has filled out a username and password combination, check them in MySQL database data and make sure that they are correct, if the outcome is positive, allow access.

If you don't understand everything, don't worry, it will become clear later (or maybe never)!

Step Two - Create Our Database

We want the database to store the login and password of our users. The required fields can easily be added to an existing database, but we will assume for now that you are not adding to an existing database but creating a new one. The following code is a description of how to do this. Assumption: if you have Apache on your computer, you can start immediately :)

mysql> create database members;
mysql> create table users (
username varchar(25) NOT NULL,
password varchar(15) NOT NULL,
primary key (username),
unique username (username)
);

We now have a database to store users in, it assumes that username is up to 25 characters, and passwords are up to 15 characters. (if for some reason it doesn’t suit you, set it as you see fit) Username should have the value “primary key” and be “unique”, since we don’t want 2 or more people to have the same username.

Please note that usernames will be sensitive to the following case, user "Vasya" will be identified differently than user "vasya", in other words case sensitive. Now we will add a test user to MySQL so that we can use his data for tests when we create the PHP page.

mysql> grant select on members.users
to httpuser@localhost
identified by "MyPassword";

This is so that when we want to check the user and password of a person registered in our database, we will use the user "httpuser" with the password "MyPassword". Finally we must add the username and password of the person we want to allow access.

mysql> insert into users value("john_doe", "eod_nhoj");

I deliberately did not encrypt the data in the program, so that in case of loss of the password I would not have to decrypt it, and simplify it to a minimum :)) That's it, I'm done with MySQL, now let's move on!

Step Three - write PHP code

Before we begin, I will briefly describe what the paga will do.

01 02
When you get to a secure page, the server will send a request and display a page for entering your name and password. If you click on the cancel button or enter incorrect data, the server will send you (401 Unauthorized header, and will deny access.) - this is how the prompt usually translates the line (401 Unauthorized header, and deny access) I won’t explain, in my opinion there’s no better way to say it !!! In case you enter everything correctly, it turns out that you will simply get access (that’s what needed to be proven). Now the fun part, this is the same code. It is written deliberately with line numbers, after the code (below) explanations for the lines are given.
03 function access_denied() (
05 }
06
04 echo "401 Unauthorized: The username / password combination you entered was invalid.n";
07 function auth_headers($title) (
08 Header("WWW-Authenticate: Basic realm="$title"");
10 }
11
09 Header("HTTP/1.0 401 Unauthorized");
12 if(!isset($PHP_AUTH_USER)) (
13 auth_headers("My Protected Web Page");
14 access_denied();
16 }
15 exit;
18
17 else (
19 $hostname = "localhost";
20 $username = "httpuser";
21 $password = "MyPassword";
23
22 $database = "members";
24 $query = "select username,password from users where username="$PHP_AUTH_USER" and password="$PHP_AUTH_PW"";
26
25 $link = mysql_connect($localhost, $username, $password) or die("Unable to connect to database server");
27 if (mysql_num_rows(mysql_db_query($database, $query)) == 0) (
28 auth_headers("My Protected Web Page");
29 access_denied();
31 }
32
30 exit;
34 }
35 ?>

33 mysql_close($link); That's the trick, it works, you can use it wherever you want, whenever you want and however you want, change it, improve it, if you manage to reduce the code to two lines when saving functionality

In this section, we will quickly explore each line in order to prevent heated exchanges of questions in the comments to this article.

Line 3:
This function will display a message if an “evil user” persistently enters incorrect data. I made this a function because we use it twice, and just to shorten the source code.

Line 7:
Since we are using this header twice too, I also made this a function.

Line 8:
Pass a header to the browser that will force the user to enter their username and password. The $title variable will be shown in the login dialog.

Line 9:
At the first request, a header is displayed; when canceled again, a message is displayed indicating that access is denied.

Line 12:
$PHP_AUTH_USER is a loop that displays a message saying that the page is protected, and get out!

Line 19-23:
This is something that no one knows except you, that is, the means for connecting to the database, the host name, database names, user name, and password. (for connecting to MySQL)

Line 24:
A MySQL query that returns usernames and passwords.

Line 25:
Establish a connection with MySQL and display a curse if there is no connection!!! (this means that you have something wrong in lines 19-23, or there is no MySQL at all)

Line 27:
Process $query. If it returns - 0, it means that an invalid combination was entered.

Line 33:
Disconnect connection to MySQL.

I would recommend saving the PHP code in a file called user_auth.php, user_auth.php3, or... (here is your imagination on this topic) Let's assume that you did save this code in the file user_auth.php. Whenever we suddenly want to protect our top-secret page on the network, we simply include this file. The only thing I would like to draw your attention to is that, logically, you need to connect it at the very top of your protected PHP page, I advise you to write the following in line number 1 of your pages:

where "user_auth.php" is the name of the file under which you saved the code.

Your question - I don't use MySQL, what should I do?

Consult with the admin of your server, if he turns out to be kind, then he will help you, for him it’s 5 minutes of work, if the evil one doesn’t help, then go to the forum related to the database you are using and shout for help! Or if you consider yourself a normal programmer, then... you won’t need this code at all, and you will create “sessions”, encrypt using PGP, and generally pervert as if you were doing protection for amazon.com