IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Cours pour apprendre à utiliser le framework Laravel 5.5

Plus loin avec Laravel


précédentsommairesuivant

IV. 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 savoir si un utilisateur authentifié a le droit de modifier une ressource particulière. Laravel nous offre un système d'autorisations bien pratique.

IV-A. L'authentification

IV-A-1. Les middlewares

On a déjà vu l'authentification en détail dans un précédent chapitre. On a aussi vu que, dans l'application d'exemple, les utilisateurs authentifiés ont un rôle selon leur degré d'habilitation. On peut filtrer les accès selon le statut avec des middlewares. Dans l'application d'exemple on a le middleware Admin :

Image non disponible

avec ce code :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
<?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 pour savoir s'il s'agit d'un administrateur ($user->role === 'admin').

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

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

On a de la même manière le middleware Redac pour les rédacteurs avec le même type de code :

Image non disponible

IV-A-2. 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 :

 
Sélectionnez
trait AuthenticatesUsers
{
    use RedirectsUsers, ThrottlesLogins;

C'est ce trait qui assure la sécurité. À partir d'un certain nombre de connexions avortées (cinq par défaut), on bloque l'accès pour le couple « e-mail (ou autre) / adresse IP » pendant une minute.

IV-A-3. Le hachage

Laravel possède la façade Hash qui permet de faire du hachage avec Bcrypt. C'est ce qui est utilisé pour l'encryption du mot de passe dans AuthR/egisterController avec l'helper bcrypt :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
protected function create(array $data)
{
    return User::create([
        ...
        'password' => bcrypt($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.

IV-B. Quelques éléments à prendre en considération

IV-B-1. 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.

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.

IV-B-2. CSRF

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

IV-B-3. 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, vous pouvez le faire 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.

IV-B-4. 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.

IV-B-5. Les requêtes de formulaire

Une autre possibilité pour sécuriser l'application existe 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éritent de cette classe parente pour éviter d'avoir du code dupliqué :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
<?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.

IV-C. Les autorisations

En plus de ces possibilités, Laravel nous offre un système complet d'autorisations.

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

 
Sélectionnez
php artisan make:policy TestPolicy
Image non disponible

avec ce code par défaut :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
<?php

namespace App\Policies;

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

class TestPolicy
{
    use HandlesAuthorization;

    /**
     * Create a new policy instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }
}

Le plus simple pour voir comment cela fonctionne est de prendre un exemple. Dans l'application d'exemple, on a le dossier app/Policies avec trois fichiers :

Image non disponible

Voyons le code de PostPolicy :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
<?php

namespace App\Policies;

use App\Models\ {User, Post};

class PostPolicy extends Policy
{
    /**
     * Determine whether the user can manage the post.
     *
     * @param  \App\Models\User  $user
     * @param  \App\Models\Post  $post
     * @return mixed
     */
    public function manage(User $user, Post $post)
    {
        return $user->id === $post->user_id;
    }
}

On voit que la classe étend une autre classe dans le même dossier :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
<?php

namespace App\Policies;

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

class Policy
{
    use HandlesAuthorization;

    /**
     * Grant all abilities to administrator.
     *
     * @param  \App\Models\User  $user
     * @return bool
     */
    public function before(User $user)
    {
        if ($user->role === 'admin') {
            return true;
        }
    }
}

La méthode before est la première à être appelée. À ce niveau on vérifie si l'utilisateur est un administrateur, et donc possède tous les droits : si c'est le cas, on renvoie true.

On a ensuite la méthode manage avec comme paramètres l'utilisateur ($user) et l'article ($post). Si l'utilisateur est le créateur de l'article, on renvoie true.

Maintenant que cette autorisation est en place, il faut la déclarer. Regardez le fichier app/Providers/AuthServiceProvider :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use App\Models\Post;
use App\Policies\PostPolicy;
use App\Models\Comment;
use App\Policies\CommentPolicy;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        Post::class => PostPolicy::class,
        Comment::class => CommentPolicy::class,
    ];

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();
    }
}

Dans la propriété policies, on a prévu d'ajouter la classe PostPolicy.

Il ne nous reste plus qu'à voir comment on l'utilise.

Par défaut le contrôleur de base utilise le trait AuthorizesRequests :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
<?php

namespace App\Http\Controllers;

use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;

class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}

Donc tous les contrôleurs sont au courant de l'existence des autorisations enregistrées.

Regardez ces deux méthodes du contrôleur Back/PostController :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
public function edit(Post $post)
{
    $this->authorize('manage', $post);

    $categories = Category::all()->pluck('title', 'id');

    return view('back.posts.edit', compact('post', 'categories'));
}

public function update(PostRequest $request, Post $post)
{
    $this->authorize('manage', $post);

    $this->repository->update($post, $request);

    return back()->with('post-ok', __('The post has been successfully updated'));
}

Elles sont destinées, pour la première, à afficher le formulaire de modification d'un article, et pour la seconde, à traiter la soumission des modifications.

Dans les deux cas on met en œuvre l'autorisation :

 
Sélectionnez
$this->authorize('manage', $post);

Donc s'il ne s'agit pas d'un administrateur ou du propriétaire de l'article, ça va coincer ici.

On peut également utiliser l'autorisation en tant que middleware dans les routes (can) :

 
Sélectionnez
Route::name('posts.seen')->put('posts/seen/{post}', 'PostController@updateSeen')->middleware('can:manage,post');
Route::name('posts.active')->put('posts/active/{post}/{status?}', 'PostController@updateActive')->middleware('can:manage,post');

On peut aussi utiliser une autorisation dans une vue. Ce n'est pas utilisé dans l'application d'exemple, mais on aurait pu écrire :

 
Sélectionnez
@if (Auth::user()->can('manage', $post))

Vous pouvez trouver la documentation complète ici.

IV-D. 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).

précédentsommairesuivant

Copyright © 2018 Maurice Chavelli. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.