Nouveau : Datasets open source gratuits disponibles !Decouvrir →
🐘
Intermediaire 25 min PHP

Securiser une application PHP

Pourquoi Securiser une application PHP ?

Le développement d'application web est un processus qui exige une attention particulière à la sécurité. Dans le monde numérique actuel, il est essentiel que nous protégeions les données et l'intégrité de nos utilisateurs. En tant qu'évolutif du langage PHP, il est crucial pour vous comme développeur d'être conscient des risques potentiels et de mettre en œuvre des pratiques de sécurité robustes dès le début.

Un cas concret de la nécessité de sécuriser une application PHP se trouve dans l'industrie financière. Les informations personnelles et financières des utilisateurs sont sensibles, et leur protection est une priorité absolue. Une application non sécurisée pourrait être vulnérable à des attaques telles que les injections SQL, la perte de données ou même l'accès non autorisé aux comptes utilisateurs.

Prerequis

Avant de commencer à coder, il est important d'être équipé de certaines connaissances et outils. Voici ce dont vous aurez besoin :

  • PHP : La version recommandée est la 7.4 ou ultérieure.
  • Un environnement de développement local : XAMPP, WAMP ou MAMP pour Windows/Mac, ou Docker pour un environnement plus portable.
  • Gestionnaire de paquets Composer : Pour gérer les dépendances PHP.
  • IDE/Editeur de code : Visual Studio Code est une option populaire.

Concepts fondamentaux

1. Injection SQL

L'injection SQL est l'un des vulnérabilités les plus courantes dans les applications web. Elle se produit lorsque la saisie utilisateur n'est pas correctement traitée et que le code PHP génère une requête SQL dynamique.

## ❌ Mauvais : Injection SQL possible
$username = $_POST['username'];
$password = $_POST['password'];

$sql = "SELECT * FROM users WHERE username='$username' AND password='$password'";
$result = mysqli_query($conn, $sql);

Pour éviter cette vulnérabilité, utilisez des requêtes préparées.

## ✅ Correct : Utilisation de requêtes préparées
$stmt = $pdo->prepare('SELECT * FROM users WHERE username=? AND password=?');
$stmt->execute([$username, $password]);
$result = $stmt->fetchAll();

2. Cross-Site Scripting (XSS)

L'XSS est une vulnérabilité qui permet aux attaquants d'injecter du code JavaScript dans les pages web visibles par les autres utilisateurs.

## ❌ Mauvais : Injection XSS possible
$user_input = $_GET['input'];
echo "<p>User Input: $user_input</p>";

Pour prévenir l'XSS, utilisez la fonction htmlspecialchars() pour échapper le contenu utilisateur.

## ✅ Correct : Échappement des entrées utilisateurs
$user_input = htmlspecialchars($_GET['input'], ENT_QUOTES, 'UTF-8');
echo "<p>User Input: $user_input</p>";

3. Authentification et Autorisation

La gestion de l'authentification et de l'autorisation est essentielle pour protéger les ressources sensibles de votre application.

## ❌ Mauvais : Authentification simple sans vérification de rôle
if ($_POST['username'] == 'admin' && $_POST['password'] == 'secret') {
    session_start();
    $_SESSION['user'] = 'admin';
}

Pour une authentification sécurisée, utilisez des fonctions cryptées et des rôles d'utilisateur.

## ✅ Correct : Authentification sécurisée avec rôles utilisateurs
if (password_verify($_POST['password'], $hashed_password)) {
    session_start();
    $_SESSION['user'] = 'admin';
    $_SESSION['role'] = 'admin';
}

4. Protection contre les attaques CSRF

Les attaques CSRF, ou Cross-Site Request Forgery, permettent aux attaquants d'effectuer des actions non autorisées sur le compte d'un utilisateur en tromplant l'utilisateur.

## ❌ Mauvais : Formulaires sans token CSRF
<form action="submit.php" method="post">
    <input type="text" name="data">
    <button type="submit">Submit</button>
</form>

Pour prévenir les attaques CSRF, utilisez un jeton CSRF généré et vérifié.

## ✅ Correct : Formulaires avec token CSRF
<form action="submit.php" method="post">
    <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
    <input type="text" name="data">
    <button type="submit">Submit</button>
</form>

## Vérification du token CSRF
if ($_POST['csrf_token'] == $_SESSION['csrf_token']) {
    // Traitement de la requête
}

Mise en pratique : Projet fil rouge

Dans cette section, nous allons construire un mini-projet complet et réaliste : un gestionnaire de tâches simple. Le projet comprendra les fonctionnalités suivantes :

  • Inscription et connexion des utilisateurs.
  • Ajout, modification et suppression de tâches.

Étape 1 : Initialisation du projet

Créez un nouveau dossier pour votre projet et initialisez un fichier composer.json.

mkdir task-manager
cd task-manager
echo '{}' > composer.json

Ajoutez les dépendances nécessaires.

{
    "require": {
        "php": "^7.4",
        "pdo-mysql": "*"
    }
}

Installez les dépendances avec Composer.

composer install

Étape 2 : Configuration de la base de données

Créez un fichier .env pour stocker vos informations de connexion à la base de données.

DB_HOST=localhost
DB_NAME=task_manager
DB_USER=root
DB_PASS=

Ajoutez un fichier config.php pour charger ces variables d'environnement.

<?php
require 'vendor/autoload.php';

$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

return [
    'db' => [
        'host' => $_ENV['DB_HOST'],
        'name' => $_ENV['DB_NAME'],
        'user' => $_ENV['DB_USER'],
        'pass' => $_ENV['DB_PASS']
    ]
];

Étape 3 : Connexion à la base de données

Créez un fichier database.php pour gérer la connexion à la base de données.

<?php
$config = require __DIR__ . '/config.php';

try {
    $pdo = new PDO(
        "mysql:host={$config['db']['host']};dbname={$config['db']['name']}",
        $config['db']['user'],
        $config['db']['pass']
    );
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    die('Database connection failed: ' . $e->getMessage());
}
?>

Étape 4 : Modèle pour les tâches

Créez un fichier Task.php pour définir le modèle des tâches.

<?php
class Task {
    private $pdo;

    public function __construct($pdo) {
        $this->pdo = $pdo;
    }

    public function create($title, $description) {
        $stmt = $this->pdo->prepare('INSERT INTO tasks (title, description) VALUES (?, ?)');
        return $stmt->execute([$title, $description]);
    }

    public function getTasks() {
        $stmt = $this->pdo->query('SELECT * FROM tasks');
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    public function delete($id) {
        $stmt = $this->pdo->prepare('DELETE FROM tasks WHERE id=?');
        return $stmt->execute([$id]);
    }
}
?>

Étape 5 : Vues

Créez des fichiers index.php, login.php et dashboard.php pour afficher les tâches, la connexion et le tableau de bord.

index.php

<?php
require 'config.php';
require 'database.php';

$pdo = new PDO(
    "mysql:host={$config['db']['host']};dbname={$config['db']['name']}",
    $config['db']['user'],
    $config['db']['pass']
);

session_start();
if (isset($_SESSION['user'])) {
    header('Location: dashboard.php');
    exit;
}

require 'login.php';
?>

login.php

<?php
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $username = $_POST['username'];
    $password = $_POST['password'];

    // TODO: Authentification utilisateur
}
?>

<form method="post">
    <input type="text" name="username" placeholder="Username">
    <input type="password" name="password" placeholder="Password">
    <button type="submit">Login</button>
</form>

dashboard.php

<?php
require 'config.php';
require 'database.php';

$pdo = new PDO(
    "mysql:host={$config['db']['host']};dbname={$config['db']['name']}",
    $config['db']['user'],
    $config['db']['pass']
);

session_start();
if (!isset($_SESSION['user'])) {
    header('Location: index.php');
    exit;
}

$taskModel = new Task($pdo);
$tasks = $taskModel->getTasks();

// TODO: Afficher les tâches et le formulaire d'ajout
?>

Étape 6 : Traitement des formulaires

Ajoutez le traitement des formulaires dans dashboard.php.

<?php
require 'config.php';
require 'database.php';

$pdo = new PDO(
    "mysql:host={$config['db']['host']};dbname={$config['db']['name']}",
    $config['db']['user'],
    $config['db']['pass']
);

session_start();
if (!isset($_SESSION['user'])) {
    header('Location: index.php');
    exit;
}

$taskModel = new Task($pdo);
$tasks = $taskModel->getTasks();

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    if (isset($_POST['add_task'])) {
        $title = $_POST['title'];
        $description = $_POST['description'];
        $taskModel->create($title, $description);
        header('Location: dashboard.php');
        exit;
    }

    if (isset($_POST['delete_task'])) {
        $id = $_POST['delete_task'];
        $taskModel->delete($id);
        header('Location: dashboard.php');
        exit;
    }
}
?>

<form method="post">
    <input type="text" name="title" placeholder="Title">
    <textarea name="description" placeholder="Description"></textarea>
    <button type="submit" name="add_task">Add Task</button>
</form>

<table>
    <tr>
        <th>Title</th>
        <th>Description</th>
        <th>Action</th>
    </tr>
    <?php foreach ($tasks as $task): ?>
        <tr>
            <td><?php echo htmlspecialchars($task['title']); ?></td>
            <td><?php echo htmlspecialchars($task['description']); ?></td>
            <td>
                <form method="post" style="display: inline;">
                    <input type="hidden" name="delete_task" value="<?php echo $task['id']; ?>">
                    <button type="submit">Delete</button>
                </form>
            </td>
        </tr>
    <?php endforeach; ?>
</table>

Erreurs frequentes et debugging

1. Injection SQL

Erreur :

## Mauvais : Injection SQL possible
$username = $_POST['username'];
$password = $_POST['password'];

$sql = "SELECT * FROM users WHERE username='$username' AND password='$password'";
$result = mysqli_query($conn, $sql);

Correction :

## Correct : Utilisation de requêtes préparées
$stmt = $pdo->prepare('SELECT * FROM users WHERE username=? AND password=?');
$stmt->execute([$username, $password]);
$result = $stmt->fetchAll();

2. Cross-Site Scripting (XSS)

Erreur :

## Mauvais : Injection XSS possible
$user_input = $_GET['input'];
echo "<p>User Input: $user_input</p>";

Correction :

## Correct : Échappement des entrées utilisateurs
$user_input = htmlspecialchars($_GET['input'], ENT_QUOTES, 'UTF-8');
echo "<p>User Input: $user_input</p>";

3. Authentification simple

Erreur :

## Mauvais : Authentification simple sans vérification de rôle
if ($_POST['username'] == 'admin' && $_POST['password'] == 'secret') {
    session_start();
    $_SESSION['user'] = 'admin';
}

Correction :

## Correct : Authentification sécurisée avec rôles utilisateurs
if (password_verify($_POST['password'], $hashed_password)) {
    session_start();
    $_SESSION['user'] = 'admin';
    $_SESSION['role'] = 'admin';
}

Pour aller plus loin

1. Sécurité des cookies

Utilisez les fonctionnalités de sécurité avancées pour les cookies, comme secure et httponly.

## Configuration de cookies sécurisés
session_set_cookie_params([
    'lifetime' => 0,
    'path'     => '/',
    'domain'   => $_SERVER['HTTP_HOST'],
    'secure'   => true,
    'httponly' => true,
]);
session_start();

2. Utilisation de middleware

Créez un système de middleware pour gérer les actions avant et après chaque requête.

class Middleware {
    public function handle($request, $next) {
        // Actions avant la requête
        return $next($request);
    }
}

$middleware = new Middleware();
$response = $middleware->handle($request, function($request) {
    // Traitement de la requête
});

3. Sécurité des fichiers

Utilisez des fonctions sécurisées pour gérer les fichiers uploadés.

## Gestion des fichiers uploadés
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['file'])) {
    $file = $_FILES['file'];
    $target_dir = "uploads/";
    $target_file = $target_dir . basename($file["name"]);
    move_uploaded_file($file["tmp_name"], $target_file);
}

Défi pratique

Développez une fonction qui vérifie si une adresse e-mail est valide et sécurisée avant de l'utiliser dans votre application.

function isValidEmail($email) {
    // Vérification basique
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        return false;
    }

    // Extraction du domaine
    $domain = explode('@', $email)[1];

    // Vérification de la validité du domaine
    if (checkdnsrr($domain, 'MX')) {
        return true;
    }

    return false;
}

Ce tutoriel couvre les concepts fondamentaux de la sécurité en PHP et guide vous à travers le processus de création d'un projet complet. En suivant ces étapes et en appliquant ces pratiques de sécurité, vous serez bien équipé pour développer des applications web sécurisées et robustes.

Besoin d'aide sur PHP ?

Besoin d'aide sur un projet technique ? Decrivez-le pour des conseils personnalises.

Recevoir des conseils

Questions frequentes

Comment sécuriser les connexions à la base de données dans une application PHP ?
Pour sécuriser les connexions à la base de données, utilisez des requêtes préparées et des paramètres nommés. Évitez l'utilisation directe de chaînes de caractères pour former des requêtes SQL afin d'éviter les injections SQL.
Quelles sont les meilleures pratiques pour la gestion des mots de passe dans une application PHP ?
Stocker les mots de passe en utilisant un hachage sécurisé comme bcrypt. Assurez-vous que les mots de passe ne peuvent pas être récupérés même si l'hachage est volé. Utilisez également des salts uniques pour chaque utilisateur.
Comment prévenir les attaques XSS (Cross-Site Scripting) dans une application PHP ?
Filtrer et échapper toutes les entrées utilisateurs avant de les afficher dans la page. Utilisez des fonctions comme htmlspecialchars() pour transformer des caractères spéciaux en entités HTML. Assurez-vous également que le type de contenu approprié est défini lors de l'affichage du contenu HTML.

Pages liees

Chaque semaine, le meilleur de la tech francaise

Tendances, salaires, outils et opportunites — directement dans votre boite mail.

Gratuit. Desabonnement en un clic. Pas de spam.