UPDATED ARTICLE
Un website (website de prezentare, blog, forum, magazin online, portal) este alcatuit din doua parti:
User-agent: * Disallow: /backend/
In loc de termenul backend se mai folosesc termenii sectiune de administrare si CMS (de la Content Managment System – in limba engleza, Sistem de gestionare a continutului). Cand intalnim unul din acesti termeni trebuie sa stim ca se face referire la unul si acelasi lucru.
Ca sa restrictionam accesul la sectiunea de administrare trebuie mai intai sa inregistram toti utilizatorii intr-o baza de date. Apoi cream un formular de autentificare a utilizatorilor sectiunii de administrare si un tabel, numit ai_authentication_logs, in care vom inregistra loguri despre autentificarile facute.
$q = "CREATE TABLE IF NOT EXISTS ai_authentication_logs( authentication_log_id INT UNSIGNED NOT NULL AUTO_INCREMENT, username VARCHAR(30) NOT NULL, password VARCHAR(40) NOT NULL, server_authentication_date DATETIME NOT NULL, client_authentication_date DATETIME NOT NULL, ip VARCHAR(30) NOT NULL, browser_os VARCHAR(255) NOT NULL, screen_resolution VARCHAR(15) NOT NULL, status VARCHAR(10) NOT NULL, PRIMARY KEY(authentication_log_id))"; mysql_query($q) or die(mysql_error());
Acest tabel ne permite sa tinem o evidenta a celor care utilizeaza sectiunea de administrare. De fiecare data cand cineva se autentifica se introduce in baza de date numele utilizatorului, parola encriptata a utilizatorului, data si ora autentificarii (atat ora de pe server cat si ora de pe PC-ul utilizatorului), IP-ul de pe care s-a facut autentificarea, browserul si sistemul de operare folosite de utilizator, rezolutia monitorului utilizatorului, statusul autentificarii (reusita sau esuata).
Daca o autentificare esueaza parola va fi inregistrata in clar. Astfel putem vedea daca cineva doreste sa intre neautorizat in sectiunea de administrare si cu ce cuvinte incearca sa sparga parola. Se stie foarte bine ca o parola poate fi aflata prin forta bruta (brute force attack), utilizand un dictionar de cuvinte (dictionary attack) sau prin inginerie sociala (social engineering) sau poate fi pur si simplu ghicita. De asemenea un utilizator poate sa isi infecteze PC-ul cu un keylogger si astfel un rau-voitor sa intre in posesia datelor sale de logare.
Mai pe scurt, acest tabel ne ajuta sa vedem daca se intampla ceva suspicios in legatura cu utilizatorii sectiunii de administrare.
In cazul in care autentificarea reuseste, se va crea o sesiune pe server, se va salva in sesiune numele utilizatorului si browserul si sistemul de operare folosite de utilizator si se va face redirectionarea de la pagina de login la pagina principala a sectiunii de administrare.
Apoi pe pagina principala a sectiunii de administrare, deasupra DOCTYPE-ului mai exact, exista un script PHP care verifica daca cel care vrea sa deschida pagina este autorizat sau nu sa faca acest lucru. Daca este autorizat pagina va fi afisata in browser, daca nu este autorizat va fi redirectionat catre pagina de login. Acest script PHP il punem in toate paginile sectiunii de administrare, deasupra DOCTYPE-ului, pentru a restrictiona accesul la ele.
Se prefera utilizarea sesiunilor in detrimentul cookie-urilor deoarece datele de logare ale utilizatorului sunt stocate pe server si nu pe PC-ul utilizatorului. In acest fel datele de logare sunt infinit mai protejate si nu sunt transmise in mod repetat intre server si browser. Pe PC-ul utilizatorului se creeaza doar un cookie cu identificatorul sesiunii. Numele acestui cookie este PHPSESSID si valoarea stocata de cookie este o valoare de genul a76b45cf92d87ea710fc8e9a9f812fa (32 de caractere hexazecimale). Existenta acestui cookie se poate verifica din browser.
Pentru ca acest script PHP de autentificare sa fie complet functional mai am nevoie de fisierul care ma conecteaza la baza de date, connect_to_db.inc.php, pe care il apelez cu functia include().
loginform.php – pagina cu formularul de autentificare
<?php
/*
Titlu: Cum fac un formular de autentificare a utilizatorilor?
Autor: Marian Barbu aka AccesInterzis
Website: http://www.accesinterzis.ro
2010 (c) Toate drepturile rezervate
*/
//-----creez o sesiune pe server pentru a salva in ea, in caz ca autentificarea reuseste, numele utilizatorului si browserul si sistemul de operare folosite de utilizator
#1
session_start();
//-----ma conectez la baza de date
#2
include('includes/connect_to_db.inc.php');
//-----infasor in strip_tags() si htmlentities() URL-urile obtinute dinamic si cookie-urile ca sa ma asigur ca nu contin cod malitios
#3
$php_self = htmlentities(strip_tags($_SERVER['PHP_SELF']), ENT_QUOTES, 'utf-8');
$referer = (isset($_SERVER['HTTP_REFERER'])) ? htmlentities(strip_tags($_SERVER['HTTP_REFERER']), ENT_QUOTES, 'utf-8') : NULL;
$cookie = array();
foreach ($_COOKIE as $k => $v) {
$v = htmlentities(strip_tags($v), ENT_QUOTES, 'utf-8');
$cookie[$k] = $v;
}
//-----specific EXACT cu ce campuri se va lucra
#4
$required_fields = array('username', 'password', 'login');
$sent_fields = array_keys($_POST);
//------scriptul PHP se executa doar daca cererea a fost facuta de pe aceeasi pagina pe care se afla formularul si doar daca toate campurile formularului au fos trimise
#5
if ($referer == 'http://'.$_SERVER['HTTP_HOST'].$php_self && $required_fields == $sent_fields) {
//-----initializez array-ul in care voi stoca mesajele de eroare si array-ul in care voi pasa datele din $_POST dupa ce le filtrez
#5.1
$errors = array();
$post = array();
#5.2
//Starting data validation
if (empty($_POST['username'])) {
$errors['username'] = 'You forgot to enter the <strong>username</strong>.';
} else {
$post['username'] = trim($_POST['username']);
if (ini_get('magic_quotes_gpc')) {
$post['username'] = stripslashes($post['username']);
}
if (strlen($post['username']) < 3) {
$errors['username'] = 'The <strong>username is too short</strong>.';
} else {
if (strlen($post['username']) > 30) {
$errors['username'] = 'The <strong>username</strong> is too long.';
} else {
if (!preg_match('/[a-z0-9_ ]*/i', $post['username'])) {
$errors['username'] = 'The <strong>username</strong> isn\'t valid.';
}
}
}
}
if (empty($_POST['password'])) {
$errors['password'] = 'You forgot to enter the <strong>password</strong>.';
} else {
$post['password'] = trim($_POST['password']);
if (ini_get('magic_quotes_gpc')) {
$post['password'] = stripslashes($post['password']);
}
if (strlen($post['password']) < 5) {
$errors['password'] = 'The <strong>password</strong> is too short.';
} else {
if (strlen($post['password']) > 30) {
$errors['password'] = 'The <strong>password</strong> is too long.';
} else {
if (!preg_match('/^[a-z0-9][a-z0-9_ ]*[a-z0-9]$/i', $post['password'])) {
$errors['password'] = 'The <strong>password</strong> isn\'t valid.';
}
}
}
}
//------daca nu exista niciun fel de erori bag datele in baza de date
#4.3
if (count($errors) == 0) {
//-----verific daca datele de logare exista si in baza de date
#4.3.1
$q = "SELECT username FROM ai_registrationform WHERE username='".$post['username']."' AND password=SHA('".$post['password']."')";
$result = mysql_query($q) or die(mysql_error());
$row = mysql_fetch_array($result);
/*
Daca autentificarea s-a efetuat cu succes:
- salvez in tabelul de loguri un log despre autetificare
- salvez in sesiune numele utilizatorului si numele browserului si sistemului de operare pe care le foloseste
- redirectez utilizatorul de pe pagina de login pe pagina principala a sectiunii de administrare
*/
if ($row) {
$q2 = "INSERT INTO ai_authentication_logs(`username`, `password`, `server_authentication_date`, `client_authentication_date`, `ip`, `browser_os`, `screen_resolution`, `status`)"
."VALUES('".$post['username']."', SHA('".$post['password']."'), NOW(), '".$cookie['client_authentication_date']."', '".$_SERVER['REMOTE_ADDR']."', '".$_SERVER['HTTP_USER_AGENT']."', '".$cookie['screen_resolution']."', 'succesful')";
mysql_query($q2) or die(mysql_error());
$_SESSION['username'] = $row['username'];
$_SESSION['browser_os'] = sha1($_SERVER['HTTP_USER_AGENT']);
header('Location:http://'.$_SERVER['HTTP_HOST'].dirname($php_self).'/backend.php');
exit();
} else {
$q2 = "INSERT INTO ai_authentication_logs(`username`, `password`, `server_authentication_date`, `client_authentication_date`, `ip`, `browser_os`, `screen_resolution`, `status`)"
."VALUES('".$post['username']."', '".$post['password']."', NOW(), '".$cookie['client_authentication_date']."', '".$_SERVER['REMOTE_ADDR']."', '".$_SERVER['HTTP_USER_AGENT']."', '".$cookie['screen_resolution']."', 'failed')";
mysql_query($q2) or die(mysql_error());
$error_message = 'Your login data are wrong.';
$errors['username'] = '';
$errors['password'] = '';
}
}
//-----infasor datele in htmlentities() deoarece urmeaza sa le afisez in formular
#4.4
foreach ($post as $k => $v) {
$post[$k] = htmlentities(stripslashes($v), ENT_QUOTES, 'utf-8');
}
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>How do I make a login form?</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="description" content="How do I make a login form?" />
<meta name="keywords" content="login,form,php,script,mysql,database,user,backend,admin,section,cms" />
<meta name="abstract" content="How do I make a login form?" />
<meta name="author" content="AccesInterzis" />
<meta name="copyright" content="AccesInterzis" />
<meta name="robots" content="index,follow" />
<meta name="revisit-after" content="7 days" />
<style type="text/css">
* {
margin:0;
padding:0;
outline:none;
}
html {
color:black;
background-color:white;
font: normal normal normal 12px Verdana;
/*font-style font-variant font-weight font-size font-family*/
}
/*INCEPUT - LINIILE CSS CARE CREEAZA SKINUL FORMULARULUI DE AUTENTIFICARE*/
div#loginf {
width:285px;
margin:0px auto;
}
div#loginf h1 {
color:black;
font: normal normal normal 24px Verdana;
/*font-style font-variant font-weight font-size font-family*/
padding-bottom:5px;
}
div#loginf div {
margin:0 0 5px 0;
}
div#loginf label {
width:80px;
float:left;
}
div#loginf label span {
color:#c00;
}
div#loginf input {
width:200px;
}
div#loginf textarea {
width:300px;
height:150px;
}
div#loginf input, div#loginf textarea {
border:1px #ccc solid;
}
div#loginf input:hover, div#loginf textarea:hover {
border:1px #666 solid;
}
div#loginf input#login {
width:auto;
color:#FFF;
background-color:#333;
border:1px #000 solid !important;
cursor:pointer;
}
div#loginf input#login:hover {
color:#333;
background-color:#fff;
border:1px #333 solid;
}
/*Inceput - stilurile erorilor*/
div#loginf p {
color:#c00;
padding:0 0 0 80px;
font-size:10px;
text-align:left;
}
div#loginf div#username_field label,
div#loginf div#password_field label {
color:#c00;
}
div#loginf div#username_field input,
div#loginf div#password_field input {
border:1px #c00 solid;
color:#c00;
}
div#loginf div#username_field input:hover,
div#loginf div#password_field input:hover {
border:1px #c00 solid;
}
/*Sfarsit - stilurile erorilor*/
/*SFARSIT - LINIILE CSS CARE CREEAZA SKINUL FORMULARULUI DE AUTENTIFICARE*/
</style>
<script type="text/javascript">
/*
Creez cookie-urile care vor stoca date despre utilizator.
Aceste cookie-uri se creeaza atunci cand se deschide prima oara pagina de login.
De abia cand se reincarca pagina, adica cand utilizatorul apasa butonul "login", scriptul PHP
se poate folosi de aceste cookie-uri.
*/
document.cookie = 'screen_resolution=' + screen.width + '*' + screen.height + ';';
var current_date = new Date();
var year = current_date.getFullYear();
var month = current_date.getMonth();
var day = current_date.getDate();
var hour = current_date.getHours();
var minutes = current_date.getMinutes();
var seconds = current_date.getSeconds();
var current_date = year + '-' + month + '-' + day + ' ' + hour + ':' + minutes + ':' + seconds;
document.cookie = 'client_authentication_date=' + current_date + ';';
</script>
</head>
<body>
<div id="loginf">
<h1>
<label> </label>
Login
</h1>
<?php if (isset($error_message)) echo '<p>'.$error_message.'</p>'; ?>
<form action="<?php echo 'http://'.$_SERVER['HTTP_HOST'].$php_self; ?>" method="post">
<?php echo (isset($errors['username'])) ? '<p>'.$errors['username'].'</p><div id="username_field">' : '<div>' ; ?>
<label for="username">Username<span>*</span>:</label>
<input name="username" type="text" id="username" value="<?php if (isset($post['username'])) echo $post['username']; ?>" />
</div>
<?php echo (isset($errors['password'])) ? '<p>'.$errors['password'].'</p><div id="password_field">' : '<div>' ; ?>
<label for="password">Password<span>*</span>:</label>
<input name="password" type="password" id="password" />
</div>
<div>
<label> </label>
<input name="login" type="submit" id="login" value="login" />
</div>
</form>
</div>
</html>
backend.php – pagina principala a sectiunii de administrare
<?php
#1
session_start();
$php_self = htmlentities(strip_tags($_SERVER['PHP_SELF']), ENT_QUOTES, 'utf-8');
$browser_os = htmlentities(strip_tags($_SERVER['HTTP_USER_AGENT']), ENT_QUOTES, 'utf-8');
#2
if (!isset($_SESSION['username']) || !isset($_SESSION['browser_os']) || $_SESSION['browser_os'] != sha1($browser_os)) {
header('Location:http://'.$_SERVER['HTTP_HOST'].dirname($php_self).'/loginf.php');
exit();
}
#3
if (isset($_GET['action']) && $_GET['action'] == 'logout') {
#3.1
/*
Pentru a deloga un utilizator din sectiunea de administrare trebuie sa urmez pasii de mai jos:
- distrug toate variabilele sesiunii de pe server reinitializind intregul tablou superglobal $_SESSION
- sterg de pe server toate datele sesiunii apeland functia session_destroy()
- sterg de pe PC-ul utilizatorului cookie-ul care stocheaza identificatorul de sesiune
*/
$_SESSION = array();
if (session_destroy() && setcookie('PHPSESSID', '', time()-300, '/', '', 0)) {
header('Location:http://'.$_SERVER['HTTP_HOST'].dirname($php_self).'/loginf.php');
exit();
}
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Backend</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="description" content="How do I make a backend?" />
<meta name="keywords" content="backend,administration,section,admin,cms,content,managament,system,php,script,mysql,database" />
<meta name="abstract" content="How do I make a backend?" />
<meta name="author" content="AccesInterzis" />
<meta name="copyright" content="AccesInterzis" />
<meta name="robots" content="index,follow" />
<meta name="revisit-after" content="7 days" />
<link href="http://www.accesinterzis.ro/myportofolio/css/reset.css" type="text/css" rel="stylesheet" media="all" />
<style type="text/css">
* {
margin:0;
padding:0;
outline:none;
}
html {
color:black;
background-color:white;
font: normal normal normal 12px Verdana;
/*font-style font-variant font-weight font-size font-family*/
}
/*INCEPUT - LINIILE CSS CARE CREEAZA SKINUL SECTIUNII DE ADMINISTRARE*/
div#backend {
width:1000px;
margin:10px auto;
overflow:auto;
border:1px #ccc solid;
padding:10px;
}
div#backend a {
color:#900;
text-decoration:none;
}
div#backend ul#welcome_message {
overflow:auto;
list-style-type:none;
}
div#backend ul#sidebar {
width:10%;
float:left;
list-style-type:none;
border-right:1px #ccc solid;
}
div#backend div#mainarea {
width:80%;
float:right;
}
div#backend div#mainarea p#confirmation_message {
width:75%;
margin:250px auto;
}
div#backend div#footer{
width:100%;
float:left;
text-align:center;
}
div.splitter {
width:100%;
height:1px;
clear:both;
float:left;
border-top:1px #ccc solid;
margin: 10px 0 10px 0;
}
/*SFARSIT - LINIILE CSS CARE CREEAZA SKINUL SECTIUNII DE ADMINISTRARE*/
</style>
</head>
<body>
<div id="backend">
<ul id="welcome_message">
<li style="float:left;">Welcome <strong><?php echo $_SESSION['username']; ?></strong> to the administration section</li>
<li style="float:right;"><a href="<?php echo 'http://'.$_SERVER['HTTP_HOST'].$php_self; ?>?action=logout" title="Logout">Logout</a></li>
</ul>
<div class="splitter"></div>
<ul id="sidebar">
<?php
for ($i = 0; $i < 40; $i++) {
echo '<li>sidebar</li>';
}
?>
</ul>
<div id="mainarea">
<p id="confirmation_message">
<strong><?php echo $_SESSION['username']; ?></strong>, esti aici deoarece ai dovedit ca esti un utilizator autorizat al acestei sectiuni de administrare.
Acum delogheaza-te si incearca sa accesezi din nou <strong><?php echo 'http://'.$_SERVER['HTTP_HOST'].$php_self; ?></strong>.
Vei vedea ce se intampla cand cineva neautentificat incearca sa intre in sectiunea de administrare.
</p>
</div>
<div class="splitter"></div>
<div id="footer">
Designed and developed by <a href="http://www.accesinterzis.ro" title="Programare | Web development | Web design | Securitate IT | SEO" target="_blank">www.accesinterzis.ro</a> © 2010. All rights reserved.
</div>
</div>
</body>
</html>
Pentru un mai bun managment al codului trebuie sa facem urmatoarele lucruri:
<?php @include('includes/authenticate_user.inc.php'); ?>
<link href="css/login_form_design.css" type="text/css" rel="stylesheet" media="all" />
<script type="text/javascript" src="js/user_data.js"></script>
<?php @include('includes/restricted_area.inc.php'); ?>
<link href="css/backend_design.css" type="text/css" rel="stylesheet" media="all" />
Publica acest articol pe Twitter
Articole asemanatoare:
salut nu sunt expert in php dar daca pui $_SERVER['PHP_SELF'] asa simplu o sa ai un bug incearca sa pui asa: htmlspecialchars(strip_tags($_SERVER['PHP_SELF']), trebuie “filtrat”
Nici eu nu sunt expert in PHP. Deci orice observatie e binevenita.
La action-ul formularelor stiu ca e vital. Am descoperit recent gaurica asta si o sa remediez problema in toate formularele.