PHP in CGI version – HTTP Authentication – login system

HTTP Authentication

Recently, during the movement of quite old application to a new server I came across a problem because of the use of HTTP authentication. The problem turned out to be sheet, but at the time of the movement and the first test I had a problem with the diagnosing what is the reasons for this.

Description of the situation

At the beginning, briefly describe the situation, the symptoms appeared. In the following I will show how to deal with the problem.

On the previous server PHP was working himself as a regular Apache module. Since the application was written long time ago, the login system based on HTTP authentication seemed to be sufficient. Moreover, that this was a intranet application made it fine to work. After moving to a new server, it turned out that the authentication window does not stop to pop up even after ceased to supply the correct username and password. The first one went to check the correctness transferred database, user database, the contents of tables. It turned out that everything is ok, so he went to the workshop, a test system log.

Login system based on HTTP authentication

The login system was very easy. Retrieved data from $_SERVER ['PHP_AUTH_USER'] and $_SERVER ['PHP_AUTH_PW'] were compared with those in the database. If no authentication has been successful a message was displayed, and after three seconds the page was refreshed with the re-displaying the authentication dialog.

Shortened index.php file, for example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
if (empty($_SERVER['PHP_AUTH_USER'])) {
	header('WWW-Authenticate: Basic realm="Logowanie"');
	header('HTTP/1.0 401 Unauthorized');
	sleep(3);
	exit;
}else {
	DEFINE ('LOGINSALT','TESTOWYSALTLOGOWANIA');
 
	require_once 'class/db/db.class.php';
	require_once 'class/login.class.php';
 
	$login_string = md5($_SERVER['PHP_AUTH_USER'].LOGINSALT.md5($_SERVER['PHP_AUTH_PW']));
	$logowanie = new login($login_string);
	$logowanie->login();
	if ($logowanie->isLogged()){
		// ok
	} else {
		include 'notLogged.php';
		sleep(3);
		header('WWW-Authenticate: Basic realm="Logowanie"');
		header('HTTP/1.0 401 Unauthorized');
		exit;
	}
}
?>

As you can see it’s very simple, almost trivial solution. With this difference in terms of basic systems based on .htpasswds that data is stored in the database.

The login class is also very simple. However, in this case, perfectly meets their assumptions. Slimmed version:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<?php 
/**
 * Klasa logowania
 * 
 * @param string $login_string - Ciąg danych logowania md5(login+salt+md5(hasło))
 */
class login{
	/**
	 * Ciąg logowania
	 *
	 * @var string
	 */
	private $_login_string;
	/**
	 * Stan zalogowania
	 *
	 * @var bool $_logged 
	 */
	private $_logged = false;
	/**
	 * Id zalogowanego użytkownika
	 *
	 * @var int
	 */
	private $_id;
	/**
	 * Konstruktor klasy logowania
	 *
	 * @param string $login_string
	 */
	public function __construct($login_string){
		$this->_login_string = $login_string;
	}
	/**
	 * Logowanie użytkownika
	 *
	 * @return bool
	 */
	public function login(){
		$db = new db_class(DB_HOST,DB_LOGIN,DB_PASS,DB_NAME);
		$query = "SELECT `ID` FROM `LISTA_USR` WHERE md5(CONCAT(`LOGIN`,'".LOGINSALT."',`PASS`))='".$this->_login_string."' ;";
		$db->sqlQuery($query);
		if ($db->sqlNumRows()>0){
			$row = $db->getRow();
			$this->_id = $row['ID'];
			$this->_logged = true;
			return true;
		}
		return false;
	}
	/**
	 * Sprawdzenie czy zalogowany
	 *
	 * @return bool $this->_logged
	 */
	public function isLogged(){
		return $this->_logged;
	}
	/**
	 * Pobranie ID zalogowanego użytkownika
	 *
	 * @return int $this->_id;
	 */
	public function getUsrId(){
		return $this->_id;
	}
}
?>

Solution very easy. There is nothing more to say about it. Unfortunately, in the CGI mode this solution does not work, because there is no $_SERVER['PHP_AUTH_USER'] and $_SERVER['PHP_AUTH_PW'] in $_SERVER variable.

Solution

The solution for the lack of required variables is an entry in the .htaccess, which created a new variable in the $_SERVER array, which pass login string 1 .

RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]

Now, only thing to do is to handle the string on PHP side. Data saved into $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] variable are base64 encoded. In an attempt to get the login and password, you must cut a piece of that variable, decode and break after a colon character.

1
2
3
<?php
list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
?>

That would be over. Site is launched, the login and password accepted, the user logged in. But, but … It would be too simple. After clickin on any link (I was using rewriting), it was throwing an 404 error

It turned out that our rewrite rule for HTTP Authentication was too greedy and took all the work on themself, not allowing other rules to work.

Therefore it was necessary to develop a different solution, one that gave friendly urls and $_SERVER['PHP_AUTH_USER'] and $_SERVER['PHP_AUTH_PW'] variables.

Final solution

Like a man sit down and think, it invents. The first idea was good but lacked the basic things, the condition for which it was supposed to be activated. In addition, placing it at the beginning of the .htaccess file wasn’t a wise thing to do. After moving it at the end of the file, and adding the appropriate condition all started work as expected.

RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule ^(.*) - [E=HTTP_AUTHORIZATION:%1]

In addition, it was necessary to refactor a script which converts user name and password from the $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] variable, because the previous one was buggy when user password was containing colon sign

1
2
3
4
5
6
7
8
<?php
if(isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && empty($_SERVER['PHP_AUTH_USER'])) { 
    $auth_params = explode(":" , base64_decode(substr($_SERVER['REDIRECT_HTTP_AUTHORIZATION'], 6))); 
    $_SERVER['PHP_AUTH_USER'] = $auth_params[0]; 
    unset($auth_params[0]); 
    $_SERVER['PHP_AUTH_PW'] = implode('',$auth_params); 
}
?>

Summary

As it turned out, cared to without rewriting the login system to the usual form. Besides, we shouldn’t always believe what’s written in the manual:

The HTTP Authentication hooks in PHP are only available when it is running as an Apache module and is hence not available in the CGI version. 2

Probably it’s the time to refactor some of the applications to the “good”, well working in different configurations login system. Systems based on HTTP authentication is fine for embedded computing solutions (routers, managed switches, etc), simple applications should have a usual authentication and authorization systems.

Źródła

  1. HTTP Authentication with PHP running as CGI/SuExec []
  2. HTTP authentication with PHP []
 

Przeczytaj także

Komentarze: 2

Dodaj komentarz »

 
 
 

Miło wiedzieć :). Dodam, że takich różnic mod_php/CGI/FastCGI jest więcej i dotyczą one szeregu innych zmiennych, dlatego warto się tym tematem głębiej zainteresować, gdyż szczególnie FastCGI zdobywa ostatnio coraz większą popularność.

 

Reply

 

Wszystko spoko i tak dalej tylko szkoda, że niedoświadczeni webmasterzy nie wiedzą, że jest to najgorsze rozwiązanie 😉 Dlaczego? Wystarczy sobie poczytać jakie zagrożenia to niesie.

 

Reply

 

Dodaj komentarz do CapaciousCore

 
(nie będzie publikowany)
 
 
Komentarz
 
 

Dozwolone tagi XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
© 2009 - 2024 Vokiel.com
WordPress Theme by Arcsin modified by Vokiel