This commit is contained in:
Florent Madelaine 2022-05-24 15:00:21 +02:00
parent 32a093d982
commit e8b40afaa8
13 changed files with 665 additions and 0 deletions

16
5TDSecurite.md Normal file
View File

@ -0,0 +1,16 @@
# TD Sécurité Web. #
## Exo (Quizz) ##
* Comment échappe-t-on les caractères spéciaux en sql?
## Exo (Little Bobby Table) ##
* Allez lire le célèbre cartoon [exploits of a mum](https://xkcd.com/327/)
* Quel est le problème?
* Donnez un exemple de base de données et de requêtes nécessaires pour recréer ce qui est évoqué dans ce cartoon.
**corection**
Voir [explain xkcd](https://www.explainxkcd.com/wiki/index.php/327:_Exploits_of_a_Mom)?
## Exo (Pour aller plus loin) ##
Implémentez une base de donnée et un fichier d'inscription d'étudiant mal protégé pour réaliser concrètement l'attaque par inscription de *little bobby table*.

160
5TPSecurite.md Normal file
View File

@ -0,0 +1,160 @@
# Introduction à la sécurité des applis web en PHP.
Le tp utilise une application de mini-blog. Le code PHP est
volontairement simplifié et suffisamment laxiste pour illustrer les
failles de sécurité abordées en cours.
Le site utilise un base de données sql. Vous pouvez directement
l'importer (répertoire `extra`). Les paramètres pour l'accès à la base
de données sont dans `lib/common.php`.
Avant de démarrer, consultez les pages existantes, créez un compte et
authentifiez-vous.
#### Formulaires et html
##### Modification de contenu
1. Postez un commentaire sur un article.
2. Modifiez votre commentaire. Modifiez le commentaire d'un autre
utilisateur.
<details><summary>Aide</summary>
Regardez l'url !
</details>
3. Sécurisez l'écriture du formulaire `comment_create.php` afin qu'on
ne puisse pas accéder à un commentaire d'un autre utilisateur.
<details><summary>Aide</summary>
Dans la requête de modification d'un commentaire, ajoutez à la
clause WHERE la vérification du user.
</details>
4. Le client peut-il modifier le formulaire, en changeant par exemple
l'id du commentaire à modifier ? comment ?
<details><summary>Aide</summary>
Il suffit d'écrire son propre formulaire, avec la possibilité de
saisir le champ id du commentaire !
</details>
5. Pour se prémunir contre cela, on va, dans le formulaire, rajouter un
code d'authentification (hash avec une clé secrète) du champ
`id_comment` pour être sur que les données proviennent du formulaire
de l'application. Uilisez la fonction
[hash_mac](http://php.net/manual/fr/function.hash-hmac.php).
Rajoutez, dans le formulaire, un hash que vous pourrez comparé au
hash re-calculé après l'envoie des données.
<details><summary>Aide</summary>
<div>
générez un hash avec une clé secrète :
```php
$hashCode = hash_hmac("sha256",$_REQUEST['id_comment'],"Ma clé");
echo '<input name="cle" type="hidden" value="'.$hashcode .'" >';
```
Au moment du traitement du formulaire, quand on reçoit le champ
`id_comment`, on recalcule et compare le hash avec celui reçu.
</div>
</details>
##### XSS
1. Ajoutez un commentaire qui masque tout ce qui suit son affichage
(uniquement de l'html svp !). Revenez à un fonctionnement normal.
<details><summary>Aide</summary>
Pensez à fermer des balises, et à en ouvrir une qui cachera tout le
reste au moyen d'une propriété css.
</details>
2. Ajoutez un commentaire qui modifie le titre `h1` de la page de
l'article.
<details><summary>Aide</summary>
<div>
Le javascript permet de modifier le contenu HTML d'un élément :
```js
document.querySelector("h1").innerHTML = "hey !!!!";
```
</div>
</details>
3. Empêchez la saisie de balises HTML (les supprimer) pour éviter les
attaques ci-dessus. Est-ce suffisant ?
4. Echappez (protéger) les textes affichés dans le html
(htmlspecialchars).
5. Créez un compte en donnant une url de façon à exploiter une faille
XSS sur la page `article_view.php`.
<details><summary>Aide</summary>
<div>
```js
javascript:alert(/XSS !/);
```
</div>
</details>
6. Proposez une url vers la page `user_login.php` qui affiche dans un
popup les login/mots de passe saisies dans le formulaire (cela
pourrait être plus dangereux, en les envoyant sur un serveur par
exemple ...)
<details><summary>Aide</summary>
La valeur de la variable login est écrite dans le formulaire, dans
l'attribut value du champ texte correspondant.
Il donc tout à fait possible d'injecter du code javascript via un
attribut html, par exemple onchange ...
</details>
##### CSRF
Les vunérabilités de type CSRF (Cross-Site Request Forgery) consiste à
forger de fausses requêtes à partir d'url authentifiées et à pousser le
client à exécuter des actions sans le savoir.
Le client authentifié exécute à son insu une requête (suppression par
exemple) par un formulaire dissimuler ou une balise image contrôlée par
du javascript.
1. Se rendre sur la page `csrf.html` (soyez authentifié). Que s'est-il
passé ?
2. Passer les données en POST résoudra le problème précédent.
Malheureusement, on pourra quand même créer une attaque avec du
javascript. (vous verrez l'année prochaine qu'on peut faire une
requête http à partir de javasscript.
3. Résoudre le problème en utilisant un token unique dans le
formulaire.
#### Injection SQL {#injection-sql .alert}
1. Sur la page `user_login.php`, connectez-vous sans mot de passe,
juste avec un login valide.
2. connectez-vous sans mot de passe ni login.
3. "Sécurisez" avec la méthode `quote` de PDO.
4. Sur la page `comment_create.php`, malgré l'échappement SQL,
prouvez que l'on peut toujours modifier les commentaires d'autrui.
<details><summary>Aide</summary>
Regerdez la clause WHERE dans la requête d'update d'un
commentaire.
</details>
5. Utilisez le [filtrage](http://fr.php.net/manual/fr/book.filter.php)
en entrée des données.
6. Sécurisez la page `comment_create.php` avec des requêtes
préparées.
#### Sessions
1. Utilisez une faille XSS pour afficher le contenu du cookie. Volez
alors la session associée. (Il suffit d'écrire depuis un autre
navigateur la valeur du cookie récupéré)

38
TP5/article_list.php Normal file
View File

@ -0,0 +1,38 @@
<?php
require_once 'lib/common.php';
session_start();
$db = initDatabase();
$articles = $db->query("SELECT * FROM article")->fetchAll(PDO::FETCH_OBJ);
?>
<?php
include './templates/header.php';
?>
<body container>
<h1>Liste des articles</h1>
<?php
if (!empty($_SESSION['user'])) {
echo "<p>Bonjour, " . $_SESSION['user']->name . ".</p>";
}
?>
<ul>
<?php
foreach ($articles as $article) {
echo '<li><a href="article_view.php?id=' . $article->id .'">'
. $article->title . "</a></li>\n";
}
?>
</ul>
<?php
include './templates/footer.php';
?>
</body>
</html>

72
TP5/article_view.php Normal file
View File

@ -0,0 +1,72 @@
<?php
require_once 'lib/common.php';
session_start();
if (empty($_GET['id'])) {
header('Location: article_list.php');
exit();
}
$db = initDatabase();
$article = $db->query("SELECT * FROM article WHERE id=" . $_GET['id'])
->fetch(PDO::FETCH_OBJ);
$article->comments = $db->query("SELECT c.*, u.login, u.url "
. "FROM comment c JOIN user u ON c.id_user=u.id"
. " WHERE id_article=" . $_GET['id'])
->fetchAll(PDO::FETCH_OBJ);
?>
<?php
include 'templates/header.php';
?>
<body container>
<h1>Article</h1>
<?php
echo '<div id="article">'
. '<h3>'. $article->title .'</h3>'
. '<div id="content">' . $article->content . '</div>';
echo '<h5 class="_bb1">Commentaires</h5>';
if (empty($article->comments)) {
echo '<p>Aucun</p>';
} else {
foreach ($article->comments as $comment) {
echo '<section class="alert-box">';
echo "<b class='_ts2'>".$comment->title."</b>"
. (isset($_SESSION['user']->id) && $comment->id_user == $_SESSION['user']->id ?
' <a href="comment_create.php?id_article=' . $comment->id_article
. '&amp;id_comment=' . $comment->id . '" title="'. $comment->title
. '">Modifier ce commentaire</a>' :
'')
. '<p class="_ts2">' . $comment->content ."</p>"
. "<p><span class='tag-box -warning'><a href=\"$comment->url\">$comment->login</a></p>";
echo "</section>";
}
}
echo "</div>";
if (empty($_SESSION['user'])) {
echo '<p>Il faut être identifié pour poster un commentaire.</p>';
} else {
if ($article->closed) {
echo "<p>Article fermé, non modifiable.</p>";
} else {
echo '<p> <a href="comment_create.php?id_article='. $article->id
.'">Ajouter un commentaire</a> avec votre compte : ' . $_SESSION['user']->name
.' </p>';
}
}
?>
<p> <a href="article_list.php">Retour à la liste des articles</a> </p>
<?php
include './templates/footer.php';
?>
</body>
</html>

50
TP5/comment_create.php Normal file
View File

@ -0,0 +1,50 @@
<?php
require_once 'lib/common.php';
session_start();
$db = initDatabase();
if (empty($_REQUEST['id_article'])) {
header('Location: article_list.php');
exit();
}
if (!empty($_GET['title']) && !empty($_GET['content'])) {
$title = $_GET['title'];
$content = $_GET['content'];
if (empty($_GET['id_comment'])) { // nouveau ou modif ?
$sql = "INSERT INTO comment (id_article, title, content, id_user) "
."VALUES (".$_GET['id_article'].", '$title', '$content', ".$_SESSION['user']->id.")";
} else {
$sql = "UPDATE comment SET title='$title', content='$content', id_user=". $_SESSION['user']->id
." WHERE id = " . $_GET['id_comment'];
}
if ($db->query($sql)) {
header('Location: article_view.php?id=' . $_GET['id_article']);
exit();
} else {
die("Erreur : $sql");
}
}
?>
<?php
include './templates/header.php';
?>
<body container>
<h1>Ajouter/modifier un commentaire</h1>
<form action="" method="get">
<fieldset>
<?php if (!empty($_REQUEST['id_comment'])) {
echo '<input name="id_comment" type="hidden" value="' . $_REQUEST['id_comment'] ."\" />\n";
} ?>
<input name="id_article" type="hidden" value="<?php echo $_REQUEST['id_article']; ?>" />
<div><label> Titre <input name="title" type="text" value="" size="60" /></label></div>
<div> <label> Texte <textarea name="content" cols="60" rows="6"></textarea></label></div>
<button type="submit" name="ok" value="1">Ajouter ce commentaire</button>
</fieldset>
</form>
<?php
include './templates/footer.php';
?>
</body>
</html>

9
TP5/csrf.html Normal file
View File

@ -0,0 +1,9 @@
<html>
<head>
<title>Sécurité PHP</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<img src="./comment_create.php?id_article=2&title=CSRF&content=Triche&ok=1" alt="image"/>
</body>
</html>

136
TP5/extra/db.sql Normal file
View File

@ -0,0 +1,136 @@
-- phpMyAdmin SQL Dump
-- version 4.7.0
-- https://www.phpmyadmin.net/
--
-- Host: localhost
-- Generation Time: May 29, 2017 at 06:15 AM
-- Server version: 10.1.22-MariaDB
-- PHP Version: 7.1.4
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Database: `securite`
--
-- --------------------------------------------------------
--
-- Table structure for table `article`
--
CREATE TABLE `article` (
`id` int(11) NOT NULL,
`title` text COLLATE utf8mb4_unicode_ci,
`closed` decimal(10,0) DEFAULT NULL,
`content` text COLLATE utf8mb4_unicode_ci
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data for table `article`
--
INSERT INTO `article` (`id`, `title`, `closed`, `content`) VALUES
(1, 'Premier', '1', 'Premier message, sans commentaire.'),
(2, 'Second', '0', 'Second message, avec des commentaires.');
-- --------------------------------------------------------
--
-- Table structure for table `comment`
--
CREATE TABLE `comment` (
`id` int(11) NOT NULL,
`id_article` decimal(10,0) DEFAULT NULL,
`title` text COLLATE utf8mb4_unicode_ci,
`id_user` decimal(10,0) DEFAULT NULL,
`content` text COLLATE utf8mb4_unicode_ci
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data for table `comment`
--
INSERT INTO `comment` (`id`, `id_article`, `title`, `id_user`, `content`) VALUES
(1, '2', 'Super', '2', 'Article très intéressant !'),
(2, '2', 'Un commentaire', '1', 'Pour tester !'),
(3, '2', 'Un deuxième commentaire', '1', 'ça casse pas trois pattes à un canard !');
-- --------------------------------------------------------
--
-- Table structure for table `user`
--
CREATE TABLE `user` (
`id` int(11) NOT NULL,
`login` varchar(60) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`name` varchar(60) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`password` text COLLATE utf8mb4_unicode_ci,
`url` text COLLATE utf8mb4_unicode_ci
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data for table `user`
--
INSERT INTO `user` (`id`, `login`, `name`, `password`, `url`) VALUES
(1, 'francois', 'François', 'mdp', 'http://fr.php.net'),
(2, 'denis', 'Denis', 'moi', 'http://lemonde.fr');
--
-- Indexes for dumped tables
--
--
-- Indexes for table `article`
--
ALTER TABLE `article`
ADD PRIMARY KEY (`id`);
--
-- Indexes for table `comment`
--
ALTER TABLE `comment`
ADD PRIMARY KEY (`id`);
--
-- Indexes for table `user`
--
ALTER TABLE `user`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `login` (`login`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `article`
--
ALTER TABLE `article`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;
--
-- AUTO_INCREMENT for table `comment`
--
ALTER TABLE `comment`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=23;
--
-- AUTO_INCREMENT for table `user`
--
ALTER TABLE `user`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=6;COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

18
TP5/lib/common.php Normal file
View File

@ -0,0 +1,18 @@
<?php
function initDatabase() {
$dir = dirname(__FILE__);
try {
$db = new PDO (
'mysql:host=localhost;dbname=securite',
'',
'');
//
//$db = new PDO('sqlite:' . $dir .'/database.sq3');
// $db = new PDO('mysql:host=localhost;dbname=Base', 'login', 'mdp');
} catch (PDOException $e) {
die('DB error: ' . $e->getMessage());
}
return $db;
}

9
TP5/templates/footer.php Normal file
View File

@ -0,0 +1,9 @@
<hr>
<p class="pull-left">
<a href="./article_list.php"><i class="fa fa-home fa-2x"></i></a>
</p>
<p class="pull-right">
<a href="#" class="btn btn-normal"><i class="fa fa-arrow-circle-o-up fa-2x"></i></a>
</p>
</body>
</html>

9
TP5/templates/header.php Normal file
View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<title>Sécurité PHP</title>
<link rel="stylesheet" href="http://www.iut-fbleau.fr/css/tacit.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="css/style.css">
</head>

51
TP5/user_create.php Normal file
View File

@ -0,0 +1,51 @@
<?php
require_once 'lib/common.php';
session_start();
if (!empty($_POST['name']) && !empty($_POST['login']) && !empty($_POST['password'])) {
$db = initDatabase();
$sql = "INSERT INTO user (name, login, password, url) "
."VALUES ('".$_POST['name']."', '".$_POST['login']."', '".$_POST['password']
."', '" . $_POST['url'] . "')";
try{
if ($db->query($sql)){
header('Location: user_login.php');
exit();
}else{
die("Erreur !!!!");
}
} catch(PDOException $e) {
echo "Erreur: ".$e;
}
}
?>
<?php
include './templates/header.php';
?>
<body container>
<h1>Création de compte</h1>
<form action="" method="POST">
<fieldset>
<div>
<label>Nom <br> <input name="name" type="text" value="" /> </label>
</div>
<div><label> Site perso <br> <input name="url" type="text" value="" /> </label></div>
<div><label> Login <br> <input name="login" type="text" value="" /> </laebl></div>
<div><label> Mot de passe <br> <input name="password" type="password" value="" /> </label></div>
<button type="submit" name="ok" value="1">Créer ce compte</button>
</fieldset>
</form>
<p> <a href="article_list.php">Retour à la liste des articles</a> </p>
<?php
include './templates/footer.php';
?>
</body>
</html>

37
TP5/user_login.php Normal file
View File

@ -0,0 +1,37 @@
<?php
require_once 'lib/common.php';
session_start();
if (!empty($_REQUEST['login']) && !empty($_REQUEST['password'])) {
$db = initDatabase();
$sql = "SELECT * FROM user "
."WHERE login='".$_POST['login']."' AND password='".$_POST['password']."'";
$user = $db->query($sql)->fetch(PDO::FETCH_OBJ);
if ($user) {
$_SESSION['user'] = $user;
header('Location: article_list.php');
exit();
}
}
?>
<?php
include 'templates/header.php';
?>
<body container>
<h1>Authentification et injection SQL</h1>
<form action="" method="POST">
<fieldset>
<div>
<label> Login : <input name="login" type="text" value="<?php if (isset($_REQUEST['login'])) { echo $_REQUEST['login']; } ?>" /> </label></div>
<div><label> Mot de passe : <input name="password" type="password" value="" /> </label></div>
<button type="submit" name="ok" value="1">S'authentifier</button>
</fieldset>
</form>
<?php
include './templates/footer.php';
?>
</body>
</html>

60
TP5/web_security.txt Normal file
View File

@ -0,0 +1,60 @@
[ Une liste de ressources partagée par Théo Debauvais ]
XSS:
explication:
https://owasp.org/www-project-top-ten/OWASP_Top_Ten_2017/Top_10-2017_A7-Cross-Site_Scripting_(XSS)
https://www.acunetix.com/websitesecurity/xss/
défense:
site pour s'entrainer (que des DOM-XSS et client-side XSS): https://xss.pwnfunction.com/
Injection SQL:
explication:
explication:
https://owasp.org/www-project-top-ten/OWASP_Top_Ten_2017/Top_10-2017_A1-Injection
https://owasp.org/www-community/attacks/SQL_Injection
https://www.w3schools.com/sql/sql_injection.asp
défense:
https://www.php.net/manual/en/mysqli.real-escape-string.php ou une fonction similaire en fonction du language que vous utilisez
Preparez vos requests, ou echappez les entrée comme ci-dessus
Programme pour tester automatiquement : http://sqlmap.org/
Local File Inclusion (LFI) / Remote File Inclusion (RFI):
https://en.wikipedia.org/wiki/File_inclusion_vulnerability
CSRF:
explication:
https://portswigger.net/web-security/csrf
https://owasp.org/www-community/attacks/csrf
défense:
https://thisinterestsme.com/php-csrf-protection/ , surtout le principe utilisé
OS injection:
explication:
https://portswigger.net/web-security/os-command-injection
https://owasp.org/www-community/attacks/Command_Injection
défense:
Pourquoi il ne faut pas essayer de cacher des fichiers sur un serveur http:
https://github.com/OJ/gobuster
https://tools.kali.org/web-applications/dirbuster
https://tools.kali.org/web-applications/dirb
Liste de vulnérabilitées (PortSwigger est une référence avec OWASP, ils développent https://tools.kali.org/web-applications/burpsuite, download=https://portswigger.net/burp):
https://portswigger.net/web-security/all-materials
Liste des failles web les plus connues (référence):
https://owasp.org/www-project-top-ten/
Applications vulnérables pour s'entrainer:
http://www.dvwa.co.uk/
http://itsecgames.com/
Site connus pour apprendre et s'entrainer (très axé sur le pentest, moins sur la défense):
https://www.root-me.org/
https://www.hackthebox.eu/
https://tryhackme.com/
Lectures de Stanford sur la sécurité web (très récent: commence en septembre 2019; très bon et explique plein d'autres failles et sujets sur la sécurité web):
https://www.youtube.com/watch?v=5JJrJGZ_LjM&list=PLMF2PpA06Sb26oYT-dfNOt3Y4wwoLAho0