Password for a page. Part I.
Password for a page. Part 1. Rather theoretical.
I decided to describe the methods of closing with password part of a site. The theme is actually rather large, so for the first time I’m going to limit myself with php+mysql authorization.
The first question to appear is how to close directory with administration scripts with a password. At the same time you don’t need anything special – one or some administrators have the same rights and the personalities change seldom. The simplest way in the given situation is to use standard server authorization – put files .htaccess and .htpasswd and write all the necessary parameters into them. A lot has been written about it so I’m not going to add anything new to it. You’d better treat following.
I’ll add two things. The first one is the place where .htpasswd file should be put. By experimental way it cleared out that path to the document with error message (ErrorDocument) is written with regard to the system variable DocumentRoot. But path to the passwords containing file (UserFile) is written with regard to ServerRoot. As I understood it’s impossible to put .htpasswd above ServerRoot - "../" won’t be treated. All this is done to enable putting file with passwords for example one level higher than the directory of a site in order to exclude access to the file from the net.
The second one is the fact that script may find out who opens it and a password: variables $PHP_AUTH_USER ? $PHP_AUTH_PW.
The main disadvantage of this method is that server cannot block password selection (after some unsuccessful enter efforts the user is offered to wait for some time and during this time all addressing from his IP-address is ignored). This is written in the official Apache documentation.
Another disadvantage is necessity to rewrite files with passwords by deleting user and input of a new one. But if it occurs seldom, this method is quite enough and besides that there is no need in writing authorization mechanism.
Authorization’s automation
This is necessary not only to simplify work with great users’ number and their routine. If you need to save optional information about users or have a flexible rights’ delimitation, it’s better to transfer authorization into the base.
Each page of the secret territory attaches file with such code:
$result = mysql_query("
SELECT * FROM person WHERE
login="". preg_replace("/[^\w_-]/","",$PHP_AUTH_USER). ""
AND pass="". md5($PHP_AUTH_PW). """);
if (@mysql_num_rows($result)!=1) {
header("WWW-Authenticate: Basic realm=\"User area\"");
header("HTTP/1.0 401 Unauthorized");
print("Write name and password to enter the users’ part of site ");
exit();
};
$user_row = mysql_fetch_array($result);
In the first line from login are deleted all the symbols except letters, numbers, dash and underlining symbol. Than the number of strings is checked and only in case we’ve got only one string the access is allowed. In the rest of cases user sees a browser window which offers to write login and password. If the user enters successfully, we have complete information about him in the $user_row massive.
Of course, the example given by me has got a row of considerable disadvantages. Don’t rewrite it letter-by-letter in order to evade password selection efforts because
- there is no selection safety here
- if the users’ table is large, hacker obviously will ‘break’ the base by the password selection.
And the last method for today – saving ciphered data in cookies.
There is an enter script, all the rest attach code which enables only prolongation of actions in the secret area – if cookies expire or he leaves it he will have to return to the enter page.
The enter script checks login and password and shows up two cookies. The first one contains login to recognize a user at once (of course, login field is unique and even dominant in the login field). In the second cookie – hash from the entry time and password (for complete conspiracy I’ll add to these strings letter ‘Z’- then it’s nearly impossible to select hash).
The rest of programs attach code which does following. It sends request to the base and chooses the string with obtained login. From this string it extracts field "log_time" and password and makes them into hash as it is described before. Compares it with what it has obtained and if they coincide, it shows up a new hash cookie from password, time and letter ‘Z’ and sends request to the data base "UPDATE user SET log_time='...' WHERE login='$cookie_login'".
if (isset($HTTP_COOKIE_VARS[$cookie_login]) && isset($HTTP_COOKIE_VARS[$cookie_code])) {
$login = $HTTP_COOKIE_VARS[$cookie_login];
$code = $HTTP_COOKIE_VARS[$cookie_code];
$result = mysql_query("SELECT date_format(log_date,"%Y%m%d%H%i%s") as log_date1,pass,uid
FROM user WHERE email="$login" AND log_date>"DATE_SUB(NOW(),INTERVAL 15 MINUTE)"");
if (!mysql_error() && @mysql_num_rows($result)==1) {
$log_time0 = time();
$log_time1 = date("YmdHis", $log_time0);
$log_time2 = date("Y-m-d H:i:s", $log_time0);
$current_user = mysql_fetch_array($result);
if (md5($current_user["pass"].$current_user["log_date1"].$md5letter) == $code) {
mysql_query("UPDATE user SET log_date="$log_time2" WHERE uid=".$current_user["uid"]);
setcookie($cookie_code, md5($current_user["pass"].$log_time1.$md5letter),
time()+900, $site_path);
$auth = true;
}
else
unset($current_user);
};
};
There is no selection and attack on the server safety here (by the way, you may write user’s IP-address instead of letter ‘Z’ in order to disable your office mate to take file containing cookie and enter from his computer).
Password for a page. Part 2. Blockage of selection
Password which consists of ten symbols from Latin letters and numbers implies a lot of variants. If you select 1 000 000 per second, you’ll need some thousands years. But as it difficult to memorize such nonsense, we’d rather do password from sensible words. It appeared some years ago that it’s possible to select the most of passwords by means of vocabulary containing 10 000 words. In its time a worm (kind of virus) appeared in the net which swarmed up the Unix servers using loop-holes in their defense and selected passwords of VIP-users by means of… Unix system orthographic dictionary.
Each user until he’s written valid login and password is treated as an evil hacker. What do we deal with when user writes something incorrectly?
- oblivion (there is a ‘forgot password’ form on each large site for such cases in order to send this password to the indicated in the system settings e-mail)
- monkey tricks
- password selection using dictionary (probability of successful selection is big, so the password should be closed, especially one of a commercial site)
- DoS-attack (in order to evade site’s overload you are to minimize actions which script performs in such a case)
I’ve thought a lot how it is possible to cause overload on a server if the safety mechanism is put on files. It proved out to be quite easy (its costs are separate theme). So let’s assume that the site doesn’t endure in case script tries to open files for record and write down data into them 1000 times per second. As after 5 unsuccessful efforts to enter a system access will be denied for a user (without any data entries into the file), you are to find 200 unique IPs and address 5 times from each of them. It’s possible. We hang html-banner with five tags in the banner roller:
<img src="http://user:password@www.host.ru/secret/absent.gif" width=1 height=1>
User instantly makes five addresses, server writes five times into the file (by the way, in some browsers window for login and password entry may appear). You may make a html-page with five such pictures and insert the picture itself through uniframe in the attended site (through uniframe in order not to be found through field ‘referer’. Support service of a free hosting is unlikely to deal with such things as digging in the log-files in search for referrers). The examples I’ve given are, of course, rather strained but the fact of possibility for using such disadvantage of a system is proved.
But nevertheless I’ll bring this example. It also may be applied for the limited addresses’ number without any fear (for example, for the local net of a firm). For this you are to put .htaccess with following contents into the directory:
order deny,allow
deny from all
allow from xxx.xxx.xxx
And here is the program code:
$errors = 0;
$fn = "ignore/". preg_replace("[^\d\.]", "", $REMOTE_ADDR. ".". $HTTP_FORWARDED_FOR);
if (is_file($fn)) {
if (filectime($fn) < time()-3600)
unlink($fn);
else
$errors = fread(fopen($fn, "r"), 2);
};
if ($errors>5) {
print ("Access denied. Enter in an hour. ");
exit();
};
//here installation of connection with data base server occurs.
$result = mysql_query("SELECT * FROM user WHERE
login="". preg_replace("/[^\w_\-]/", "", $PHP_AUTH_USER). "" AND
pass="". md5($PHP_AUTH_PW). """);
if (@mysql_num_rows($result)!=1) {
header("WWW-Authenticate: Basic realm=\"secret area\"");
header("HTTP/1.0 401 Unauthorized");
print ("Authorization required");
fwrite(fopen($fn, "w"), ++$errors);
exit();
};
$current_user = mysql_fetch_array($result);
mysql_free_result($result);
For uncompleted authorizations we create a table:
CREATE TABLE unauth (username VARCHAR(64) NOT NULL, pass VARCHAR(64) NOT NULL, ip VARCHAR(255), logintime TIMESTAMP)
Instead of addressing to files we work with base.
$errors = @mysql_result(mysql_query("SELECT count(username) as falses FROM unauth WHERE
logintime>DATE_SUB(NOW(),INTERVAL 1 HOUR) AND ip="$REMOTE_ADDR""),0);
if (mysql_error())
die(mysql_error());
if ($errors>5) {
print ("Access denied. Try in a hour.");
exit();
};
$result = mysql_query("SELECT * FROM user WHERE
login="". preg_replace("/[^\w_\-]/", "", $PHP_AUTH_USER). "" AND
pass="". md5($PHP_AUTH_PW). """);
if (@mysql_num_rows($result)!=1) {
header("WWW-Authenticate: Basic realm=\"secret area\"");
header("HTTP/1.0 401 Unauthorized");
print ("Authorization required");
mysql_query("INSERT INTO unauth (username, pass, ip) VALUES
("$PHP_AUTH_USER", "$PHP_AUTH_PW", "$REMOTE_ADDR $HTTP_X_FORWARDED_FOR")");
exit();
};
$current_user = mysql_fetch_array($result);
mysql_free_result($result);
It’s up to you if you save the entries for statistics. You may delete them doing a request before authorization:
DELETE FROM unauth WHERE logintime<DATE_SUB(NOW(),INTERVAL 1 HOUR)
By big load such mechanism works faster and more reliable than files do. Frequently used data are buffered in the base and processed directly in the operative memory.
Password for a page. Part 3. Password from the base.
Once I had a problem: I needed to shut administrative part of a site but at the same time I couldn’t put file .htpasswd above the root directory of a site. My jealousy didn’t let me put the file with password in a separate directory and block access to it through http. I decided to try doing protection like in phpMyAdmin: a user is asked login and password by means of which script is connected with the base. I did exactly like this in my logs’ analyzer. The convenience of the method is the possibility to put a file wherever you want – there are no cookies, no server’s directives for the directory. And if the password in the data base is changed, you don’t need to correct anything in the script.
I’ll give detailed description of the method on the example of MySQL. We write a function, for example mysql_die:
function mysql_die() {
header("HTTP/1.0 401 Unauthorized");
header("WWW-authenticate: basic realm=\"Statistics\"");
print ("Access denied. User name and password required.");
exit();
}
At the beginning of the program the tail of data base server is indicated and, if necessary, the name of the base:
$db_host = "localhost";
$db_name = "somedatabase";
And for connection with the base the server variables are taken: $PHP_AUTH_USER ? $PHP_AUTH_PW.
$db_connect = @mysql_connect($db_host, $PHP_AUH_USER, $PHP_AUTH_PW)
or mysql_die();
That’s all. Now about disadvantages. Of course, you may try to select a password (adding of blocking is possible, but then all the beauty of method will be lost). The password like in the case with protection by means of the sever is sent as opened. But it’s quite suitable for the simple tasks.



