Cours Laravel 6 – la sécurité

Lorsqu’on développe une application on prend plein de précautions, par exemple les utilisateurs doivent s’authentifier pour éviter des actions non autorisées. Dans le code on peut vérifier si la personne est authentifiée et quel est son degré d’habilitation.

Mais en dehors de l’authentification on doit gérer certaines situations comme par exemple savoir si un utilisateur authentifié a le droit de modifier une ressource particulière. Laravel nous offre un système d’autorisations bien pratique.

L’authentification

Les middlewares

On a déjà vu l’authentification en détail dans un précédent chapitre. On peut aussi gérer les utilisateurs authentifiés selon un rôle ou leur degré d’habilitation. On peut filtrer les accès selon le statut avec des middlewares. Par exemple on peut créer un middleware Admin :

Avec ce code :

<?php

namespace App\Http\Middleware;

use Closure;

class Admin
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $user = $request->user();

        if ($user && $user->role === 'admin') {
            return $next($request);
        }

        return redirect()->route('home');
    }
}

Dans la méthode handle on récupère l’utilisateur en cours ($request->user()) et on teste s’il s’agit d’un administrateur en imaginant que dans la table users on a créé une colonne admin ($user->role === ‘admin’).

On peut ainsi filtrer les routes réservées aux administrateurs :

Route::middleware('admin')->group(function () {
    ...
}

Le throttle

D’autre part est prévue par défaut dans l’authentification une sécurisation de la connexion contre les attaques « brute force ». Si vous regardez dans le trait Illuminate\Foundation\Auth\AuthenticatesUsers vous trouvez le trait ThrottlesLogins :

trait AuthenticatesUsers
{
    use RedirectsUsers, ThrottlesLogins;

C’est ce trait qui assure la sécurité. En cas d’un certain nombre de connexions avortées (5 par défaut) on bloque l’accès pour le couple « email (ou autre) / adresse IP » pendant une minute.

Vous pouvez aussi inclure cette protection dans n’importe quelle route en ajoutant le middleware throttle :

Route::middleware('auth:api', 'throttle:60,1')->group(function () {
    Route::get('/user', function () {
        //
    });
});

On a deux paramètres :

  1. le nombre d’accès
  2. la durée en minutes

Par exemple dans le code ci-dessus on autorise 60 accès par minute.

On peut aussi différencier les invités et les personnes authentifiées en prévoyant deux valeurs :

Route::middleware('throttle:20|60,1')

Ici les invités on droit à 20 accès par minutes contre 60 pour les authentifiés.

Le hashage

Laravel possède la façade Hash qui permet de faire du hashage. C’est ce qui est utilisé pour l’encryption du mot de passe dans Auth/RegisterController :

$user = User::create([
    ...
    'password' => Hash::make($data['password']),
]);

Si vous utilisez l’authentification par défaut de Laravel vous n’avez pas besoin d’en savoir davantage parce que tout est géré par le framework.

Laravel peut utiliser 3 types d’encryption :

Quelques éléments à prendre en considération

Les injections SQL

Une injection SQL est une attaque qui consiste à ajouter à une requête inoffensive un complément qui peut être dangereux pour l’application (des explications plus précises ici).

Si vous utilisez Eloquent vous êtes automatiquement immunisé contre ces attaques.

Par contre si vous faites des requêtes avec par exemple DB::raw() ou whereRaw, autrement dit si vous contournez Eloquent, alors vous devez vous-même vous prémunir contre les attaques SQL.

CSRF

Je vous ai déjà parlé de la protection CSRF dans ce cours, elle est automatiquement mise en place par Laravel.

XSS (Cross-Site Scripting)

Dans ce cas c’est une injection insidieuse de Html ou de JavaScript. Laravel ne fait pas de nettoyage des entrées en dehors de la validation. Si vous voulez en faire un libre à vous, par exemple avec ce composant. Par contre vous avez avec Blade la syntaxe sécuritaire {{ .. }} qui « échappe » les données à l’affichage.

Faites très attention avec la syntaxe {!! … !!} ! En gros vous devez l’éviter avec des données issues de l’extérieur de l’application.

L’assignation de masse

Je vous ai déjà parlé de l’assignation de masse. Vous devez choisir avec soin les colonnes des tables qui sont sans danger dans une mise à jour de masse, celles que vous mettez dans la propriété $fillable (ou $guarded avec la logique inverse) d’un modèle.

Les requêtes de formulaire

Un autre lieu où on peut sécuriser l’application est au niveau des requêtes de formulaires. On a vu qu’il y a une méthode authorize. Dans l’application d’exemple cette méthode n’est pas utilisée mais il faut renvoyer true pour que ça fonctionne. C’est pour cette raison que toutes les requêtes de formulaires hérite de cette classe parente pour éviter d’avoir du code dupliqué :

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

abstract class Request extends FormRequest
{
    /**
     * Authorization
     *
     * @return boolean
     */
    public function authorize()
    {
        return true;
    }
}

Vous pouvez ici vérifier par exemple si un utilisateur a le droit d’utiliser effectivement le formulaire et renvoyer false si ce n’est pas le cas.

Les autorisations

En plus de ces possibilités Laravel nous offre un système complet d’autorisations. Pour voir de quoi il s’agit on va prendre un exemple simple. On va créer une ressource pour les utilisateurs à partir d’une installation de base de Laravel équipé de l’authentification :

php artisan make:controller UserController --resource

Changez ainsi le code de ces deux fonctions :

public function edit($id)
{
    return 'Formulaire pour modifier';
}

public function update(Request $request, $id)
{
    return 'Ok on a modifié !';
}

Et les routes correspondantes qu’on protège avec le middleware auth :

Route::resource('users', 'UserController')->middleware('auth');

Maintenant on est sûrs que seules les personnes authentifiées peuvent accéder à la ressource mais… si quelqu’un veux faire une modification sur un utilisateur, donc passer par les routes users.edit et users.update il serait souhaitable de ne laisser l’accès à un utilisateur que pour ses propres informations. Autrement dit si on a l’utilisateur avec l’id 6 il ne doit avoir accès qu’aux urls :

  • users/6
  • users/6/edit

Comment réaliser celà ?

On dispose d’une commande pour créer une autorisation :

php artisan make:policy UserPolicy --model=User

Comme on a mentionné le modèle on a déjà toutes les méthodes prêtes :

<?php

namespace App\Policies;

use App\User;
use Illuminate\Auth\Access\HandlesAuthorization;

class UserPolicy
{
    use HandlesAuthorization;
    
    /**
     * Determine whether the user can view any models.
     *
     * @param  \App\User  $user
     * @return mixed
     */
    public function viewAny(User $user)
    {
        //
    }

    /**
     * Determine whether the user can view the model.
     *
     * @param  \App\User  $user
     * @param  \App\User  $model
     * @return mixed
     */
    public function view(User $user, User $model)
    {
        //
    }

    /**
     * Determine whether the user can create models.
     *
     * @param  \App\User  $user
     * @return mixed
     */
    public function create(User $user)
    {
        //
    }

    /**
     * Determine whether the user can update the model.
     *
     * @param  \App\User  $user
     * @param  \App\User  $model
     * @return mixed
     */
    public function update(User $user, User $model)
    {
        //
    }

    /**
     * Determine whether the user can delete the model.
     *
     * @param  \App\User  $user
     * @param  \App\User  $model
     * @return mixed
     */
    public function delete(User $user, User $model)
    {
        //
    }

    /**
     * Determine whether the user can restore the model.
     *
     * @param  \App\User  $user
     * @param  \App\User  $model
     * @return mixed
     */
    public function restore(User $user, User $model)
    {
        //
    }

    /**
     * Determine whether the user can permanently delete the model.
     *
     * @param  \App\User  $user
     * @param  \App\User  $model
     * @return mixed
     */
    public function forceDelete(User $user, User $model)
    {
        //
    }
}

Il faut ensuite déclarer cette autorisation dans le provider AuthServiceProvider. Mais comme Laravel est conciliant, si on respecte les appelations, ce qui est le cas ici parce que j’ai respecté le nom du modèle, la déclaration est automatique. On va donc protéger la méthode update :

public function update(User $user, User $model)
{
    return auth()->user() === $user;
}

Il ne reste plus qu’à utiliser l’autorisation dans le contrôleur :

use App\User;

...


public function edit(User $user)
{
    $this->authorize('update', $user);

    return 'Formulaire pour modifier';
}

public function update(Request $request, User $user)
{
    $this->authorize('update', $user);
    
    return 'Ok on a modifié !';
}

Et maintenant si c’est le bon utilisateur il a l’accès sinon il tombe sur une erreur 404.

Plutôt que d’intervenir dans chaque méthode de la ressource on peut aussi faire agir l’autorisation globalement sur la ressource avec ce code dans le constructeur :

public function __construct()
{
    $this->authorizeResource(User::class, 'user');
}

Vous pouvez trouver la documentation complète ici.

En résumé

  • L’authentification permet de sécuriser une application et d’adapter l’affichage selon le degré d’habilitation de l’utilisateur.
  • Eloquent immunise contre les injections SQL.
  • La protection CSRF est automatiquement mise en place par Laravel.
  • Il faut être très prudent avec la syntaxe {!! … !!} de Blade.
  • On peut sécuriser les requêtes de formulaire avec leur méthode authorize.
  • Laravel comporte un système complet et simple de gestion des autorisations (policies).

 

Print Friendly, PDF & Email

Laisser un commentaire