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" ])) ( Hello ($_SERVER["PHP_AUTH_USER"]). You have entered your password ($_SERVER["PHP_AUTH_PW"]).
header( "WWW-Authenticate: Basic realm="My Realm"");
echo "Text sent when
if the user clicked the Cancel button";
exit;
) else (
echo"
echo "
}
?>
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" ]) || Welcome: ($_SERVER["PHP_AUTH_USER"])
($_POST [ "SeenBefore" ] == 1 && $_POST [ "OldAuth" ] == $_SERVER [ "PHP_AUTH_USER" ])) (
authenticate ();
}
else(
echo"
"
echo "Previous login: ($_REQUEST["OldAuth"])";
echo ";
echo "\n";
echo "\n";
echo "\n";
echo "
}
?>
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("
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("
");");
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:
- Password protection (login/password) using variables $_SERVER["PHP_AUTH_USER"] And $_SERVER["PHP_AUTH_PW"].
- Protection by IP client address using a variable $_SERVER["REMOTE_ADDR"].
- 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.