FAQ PHP
FAQ PHPConsultez toutes les FAQ
Nombre d'auteurs : 68, nombre de questions : 580, dernière mise à jour : 18 septembre 2021
- 18.1. Cookies (2)
- 18.2. Sessions (19)
- Comment déconnecter automatiquement un utilisateur à la fin de sa navigation ?
- Comment protéger les mots de passe ?
- Comment générer un captcha ?
- Comment reproduire une faille XSS ?
- Comment se protéger de la faille XSS ?
- Que puis-je faire pour protéger les fichiers source dans lesquels apparaissent en clair des informations sensibles ?
- Comment se protéger des failles d'injection ?
Un session_timeout doit être implémenté afin de fermer la session si l'utilisateur quitte le site sans se déconnecter (en fermant son navigateur) ou si le temps d'inactivité est trop long.
La fonction de déconnexion doit détruire l'objet $_SESSION de l'utilisateur. Ainsi si un pirate vole la session, elle sera inutilisable. Il faut faire de même avec le cookie, sauf qu'il n'est pas possible de supprimer un cookie (puisqu'il est côté client et qu'il n'est pas possible d'agir sur les fichiers côté client). Il faut donc remplir le cookie d'une valeur volontairement fausse. Si ce cookie était volé, il serait inutilisable puisqu'il ne contiendrait qu'une valeur fausse.
<?php
// Fonction de déconnexion
function
logout()
{
// On remplit le cookie par une valeur fausse pour ne pas être réutilisé
setcookie("sid"
,
"session ended"
,
time()+
3600
);
// Invalidation de l'objet $_SESSION
session_unset();
// Destruction de l'objet $_SESSION
session_destroy();
// On redirige l'utilisateur vers la page d'accueil
header('HTTP/1.1 401 Unauthorized ou Authorization required'
);
header('location: index.php'
);
exit;
}
?>
Ensuite à chaque affichage de page, il faut vérifier si le temps d'inactivité a été dépassé.
<?php
define ('SESSION_TIMEOUT'
,
"1800"
);
if
(isset($_SESSION
[
'login'
]
))
{
// On vérifie si le temps d'inactivité n'a pas été dépassé
if
(time()-
$_SESSION
[
'last_access'
]
>
SESSION_TIMEOUT)
{
logout();
}
else
{
// On stocke l'heure de dernière connexion
// time s'exprime en secondes à partir du 01/01/70 à 00:00:00
$_SESSION
[
'last_access'
]
=
time();
}
}
?>
Les pirates peuvent parfois réussir à voler le contenu d'une base de données. Les noms d'utilisateur et les mots de passe de l'ensemble des utilisateurs leur sont alors accessibles. Les mots de passe doivent donc être stockés, hashés, dans la base de données. Ainsi, en cas de vol de la base, le pirate n'a pas accès aux mots de passe. Ceux-ci devraient être codés avec l'algorithme du SHA 256 car le MD5 a été craqué, ce qui le rend moins efficace. Lorsque l'utilisateur saisit son mot de passe, il est codé et la vérification s'effectue avec les deux versions codées : celle de la base de données et celle saisie.
De cette manière, l'administrateur de la base de données n'a pas non plus accès aux mots de passe en texte clair.
Dès lors, le pirate aura pour but de récupérer les mots de passe en clair en sniffant le réseau. En fait, ils récupèrent les données transitant entre le client et le serveur puisque les mots de passe sont codés côté serveur.
Pour éviter ce problème, il est possible grâce à un JavaScript d'encoder les mots de passe côté client. Le champ mot de passe est vidé grâce au JavaScript donc, dans la requête, seul le mot de passe crypté transite. L'avantage est que si un pirate récupère les données qui voyagent entre le client et le serveur, il récupère un mot de passe crypté et ne peut pas l'utiliser. Le problème est que le JavaScript peut être désactivé par l'utilisateur. Il faut donc, côté serveur, prévoir deux types de vérification de mot de passe : ceux en clair et en crypté.
<?php
$sql
=
"SELECT U.login, U.password FROM user U WHERE U.login ='"
.
mysql_real_escape_string($_POST
[
'login'
]
).
"'"
;
$result_user
=
mysql_query($sql
);
if
($user
=
mysql_fetch_object($result_user
))
{
if
($user
->
password ==
mhash(MHASH_SHA256,
$_POST
[
'password'
]
)
or
$_POST
[
'sha1'
]
==
mhash(MHASH_SHA256,
user->
password.
$_SESSION
[
'grain_de_sel'
]
) )
{
...
// On redirige vers la rubrique d'accueil après la connexion
header('HTTP/1.1 204 No Content'
);
header('location: index.php'
);
exit;
}
else
{
// On retourne le même message que cela soit le nom d'utilisateur ou le mot de passe qui soit erroné
return
"<div class=MessErr>Erreur, veuillez essayer de nouveau !</div>"
;
}
}
else
{
// On retourne le même message que cela soit le nom d'utilisateur ou le mot de passe qui soit erroné
return
"<div class=MessErr>Erreur, veuillez essayer de nouveau !</div>"
;
}
?>
Il faut utiliser la libgd pour générer une image volontairement dégradée. Bien sûr, moins la fonte utilisée est connue, plus le système est efficace. Il existe des systèmes encore plus efficaces avec des images GIF animées.
<?php
$length
=
5
;
// Longueur de la chaîne générée en image
$alphabet
=
'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'
;
// Liste des caractères possibles
$nb_characters
=
strlen($alphabet
);
// Nombre de caractères possibles
// La variable code contient la chaîne qui sera générée en image
$string
=
''
;
for
($i
=
0
;
$i
<
$length
;
++
$i
)
{
$string
.=
$alphabet
[
mt_rand(0
,
$nb_characters
-
1
)];
}
// Récupération de la longueur de la chaine à afficher
$str_length
=
strlen($string
);
// Création de la zone image en fonction de la longueur de texte à afficher
$image
=
imagecreatetruecolor(30
*
$str_length
,
50
);
// Création du fond de l'image
for
($x
=
0
;
$x
<
imagesx($image
);
++
$x
)
{
for
($y
=
0
;
$y
<
imagesy($image
);
++
$y
)
{
if
(mt_rand(1
,
5
) ==
4
)
{
$vred
=
mt_rand(0
,
100
);
$vgreen
=
mt_rand(0
,
100
);
$vblue
=
mt_rand(0
,
100
);
}
else
{
$vred
=
mt_rand(100
,
150
);
$vgreen
=
mt_rand(100
,
150
);
$vblue
=
mt_rand(100
,
150
);
}
// Allocation d'une couleur au fond
$color
=
imagecolorallocate($image
,
$vred
,
$vgreen
,
$vblue
);
// Affichage d'un pixel ayant la couleur du fond
imagesetpixel($image
,
$x
,
$y
,
$color
);
// Suppression de la couleur du fond allouée
imagecolordeallocate($image
,
$color
);
}
}
// Création de la bordure
$vred
=
mt_rand(0
,
240
);
$vgreen
=
mt_rand(0
,
240
);
$vblue
=
mt_rand(0
,
240
);
// Allocation d'une couleur à la bordure
$color
=
imagecolorallocate($image
,
$vred
,
$vgreen
,
$vblue
);
// Tracé de la bordure
imagerectangle($image
,
0
,
0
,
imagesx($image
)-
1
,
imagesy($image
)-
1
,
$color
);
// Suppression la couleur de la bordure allouée
imagecolordeallocate($image
,
$color
);
// Création du texte
for
($i
=
0
;
$i
<
$str_length
;
++
$i
)
{
$vred
=
mt_rand(150
,
240
);
$vgreen
=
mt_rand(150
,
240
);
$vblue
=
mt_rand(150
,
240
);
$size
=
mt_rand(20
,
30
);
$angle
=
mt_rand(-
10
,
20
);
$x
=
13
+
(20
*
$i
);
$y
=
mt_rand(30
,
imagesy($image
) -
10
);
$color
=
imagecolorallocate($image
,
$vred
,
$vgreen
,
$vblue
);
$font
=
'comic.ttf'
;
// Dessin du texte
imagettftext($image
,
$size
,
$angle
,
$x
,
$y
,
$color
,
$font
,
$string
[
$i
]
);
// Suppression de la couleur du texte allouée
imagecolordeallocate($image
,
$color
);
}
// Création de l'image complète au format PNG
header("Content-type: image/png"
);
imagepng($image
);
?>
Cela se fait en deux temps. D'une part, le code PHP :
echo $_GET
[
'
login
'
];
Ou, si la directive magic_quotes est activée sur votre configuration :
echo stripslashes($_GET
[
'
login
'
]
);
Ensuite, il suffit d'appeler ce script dans le navigateur avec le paramètre "page" et de lui donner du code JavaScript à exécuter :
http://localhost/test.php?login=<script>alert("Je t'ai eu !");</script>
À partir de là, il ne reste plus qu'à être inventif. Il est par exemple possible de récupérer le contenu des cookies du navigateur pour ce site et d'utiliser Ajax pour s'envoyer le tout... Une faille XSS est simplement l'affichage de valeurs soumises par l'utilisateur, directement dans le navigateur sans aucun filtrage.
Il suffit d'envoyer au navigateur Web les caractères auxquels il s'attend. Si l'on souhaite afficher une variable numérique :
// Valeur numérique entière
echo intval(10.5
);
// conversion avec une fonction
echo (int)
10.5
;
// cast type C
// Valeur numérique flottante
echo floatval(10.5
);
// conversion avec une fonction
echo (float)
10.5
;
// cast type C
Si l'on souhaite afficher du texte (attention, la fonction strval() n'est pas suffisante) :
echo htmlentities("
Je suis développeur PHP
"
,
ENT_QUOTES,
'
ISO-8859-1
'
);
Notez l'utilisation des deux paramètres optionnels, fondamentaux pour une bonne sécurité. Notez également que le paramètre charset (le dernier) doit être le même dans tout le script. À la longue, cette syntaxe devient fastidieuse. Il est donc préférable de définir une fonction dont l'usage est plus simple :
function html($string
)
{
return htmlentities($string
,
ENT_QUOTES,
'
ISO-8859-1
'
);
}
echo html($_POST
[
'
username
'
]
);
echo html($_POST
[
'
title
'
]
);
echo html($_POST
[
'
message
'
]
);
function html($string
)
{
return htmlspecialchars($string
,
ENT_QUOTES);
}
echo html($_POST
[
'
username
'
]
);
echo html($_POST
[
'
title
'
]
);
echo html($_POST
[
'
message
'
]
);
Important : Se protéger de la faille XSS permet de se protéger d'une très grosse partie des failles de sécurité des applications Web.
Lien : Définition de cross-site scripting
Lien : Comment reproduire une faille XSS ?
Lien : Tutoriel sur la sécurité en développement Web, par Julien Pauli
Tout d'abord, il faut bien être conscient que dans le cas d'une configuration saine, le code source n'est pas accessible directement. Seul le code HTML (ou autre) généré par le parseur sera envoyé au client. Ainsi vos identifiants de connexion (par exemple) sont normalement protégés. En supposant que c'est un fichier sur lequel vous faites un include, on peut cependant augmenter le niveau de sécurité.
- Vous laissez tel quel, en prenant bien soin de nommer ce fichier en .php et pas en .inc (qui pourraient être lus sans être interprétés).
-
vous mettez ce script dans un répertoire protégé par un .htaccess. Par exemple, pour interdire tout accès au répertoire, le fichier .htaccess pourra contenir :
Sélectionnez<LIMIT GET POST>
order
deny
,allow
deny
from
all
</LIMIT>
- (pas forcément réalisable sur n'importe quel hébergement) : vous mettez ce script dans un répertoire non accessible par le serveur web (c'est à dire, pour apache, à l'extérieur du DocumentRoot spécifié dans le httpd.conf), tout en prenant garde qu'il soit toujours accessible par PHP (si open_basedir est spécifié, il doit pointer vers un répertoire parent du répertoire choisi).
Il faut échapper les paramètres des fonctions de manière adéquate. Dans le cas d'une requête SQL, il vous faut utiliser le mécanisme mis à disposition par votre SGBD. Certaines classes comme PDO proposent un mécanisme bien plus fiable (les paramètres liés) qui nous évite d'utiliser de telles fonctions.
// MySQL
$string
=
mysql_real_escape_string($string
);
// postgreSQL (string)
$string
=
pg_escape_string($string
);
// postgreSQL (byte)
$string
=
pg_escape_bytea($string
);
// SQLite
$string
=
sqlite_escape_string($string
);
// Expression régulière (remplacez le slash par votre véritable délimiteur)
$string
=
preg_quote($string
,
'
/
'
);
// Argument de commande shell
$string
=
escapeshellarg($string
);
// Commande shell
$string
=
escapeshellcmd($string
);
// Numérique entier
$int
=
intval($int
);
// Numérique flottant
$float
=
floatval($float
);