V. La sécurité▲
Lorsque l'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.
V-A. L'authentification▲
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. Ce statut est conservé en session comme on l'a vu dans le précédent chapitre sur les événements.
C'est bien pratique, par exemple pour adapter les menus :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
@if
(session('statut'
) ==
'visitor'
)
<
li {!!
classActivePath('login'
) !!}
>
{!!
link_to('login'
,
trans('front/site.connection'
)) !!}
<
/li
>
@else
@if
(session('statut'
) ==
'admin'
)
<
li>
{!!
link_to_route('admin'
,
trans('front/site.administration'
)) !!}
<
/li
>
@elseif
(session('statut'
) ==
'redac'
)
<
li>
{!!
link_to('blog'
,
trans('front/site.redaction'
)) !!}
<
/li
>
@endif
<
li>
{!!
link_to('/logout'
,
trans('front/site.logout'
),
[
'id'
=>
"logout"
]
) !!}
{!!
Form::
open([
'url'
=>
'/logout'
,
'id'
=>
'logout-form'
]
) !!}{!!
Form::
close() !!}
<
/li
>
@endif
Là il ne s'agit évidemment pas de sécurité mais de commodité.
Par contre on peut filtrer les accès selon le statut avec des middlewares. Dans l'application d'exemple on a le middleware isAdmin :
Avec ce code :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
<?php
namespace
App\Http\Middleware;
use
Closure;
class
IsAdmin
{
public
function
handle($request
,
Closure $next
)
{
if
(session('statut'
) ===
'admin'
) {
return
$next
($request
);
}
return
redirect('/'
);
}
}
Il suffit ensuite de l'utiliser dans les contrôleurs, par exemple ici dans AdminController :
2.
3.
4.
public function __construct()
{
$this
->
middleware('
admin
'
);
}
Ainsi toutes les méthodes du contrôleur sont réservées aux administrateurs.
On a de la même manière le middleware isRedactor.
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 :
C'est ce trait qui assure la sécurité. En cas d'un certain nombre de connexions avortées (cinq par défaut) on bloque l'accès pour le couple « email (ou autre)/mot de passe » pendant une minute.
V-B. Quelques éléments à prendre en considération▲
V-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 (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.
V-B-2. CSRF▲
Je vous ai déjà parlé de la protection CSRF dans ce cours, elle est automatiquement mise en place par Laravel.
V-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, 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.
V-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 (celle que vous mettez dans la propriété $fillable d'un modèle).
V-C. Les requêtes de formulaire et le pot de miel▲
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 est située uniquement dans la classe Request qui est la base de toutes les autres :
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
{
/**
* Check honeypot
*
*
@return
boolean
*/
public
function
authorize()
{
return
$this
->
input('address'
) ==
''
;
}
}
En effet dans tous les formulaires il est prévu un contrôle caché. Par exemple, dans le formulaire de contact on trouve ce code :
{!!
Form::
text('address'
,
''
,
[
'class'
=>
'hpet'
]
) !!}
La classe hpet se contente de rendre le contrôle invisible. Un humain ne voit pas ce contrôle et donc ne le complète pas, par contre un robot risque de l'utiliser.
C'est ce que l'on appelle un pot de miel (pour attirer les abeilles virtuelles).
On vérifie donc si ce contrôle est rempli et, si c'est le cas, on bloque le processus.
C'est évidemment une protection sommaire mais assez efficace. Une autre approche plus classique est d'utiliser un captcha. Il existe de nombreux composant adaptés pour Laravel.
V-D. Les autorisations▲
En plus de ces possibilités Laravel nous offre un système complet d'autorisations. 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 un fichier :
Voyons ce fichier :
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.
33.
34.
35.
<?php
namespace
App\Policies;
use
App\Models\Post;
use
App\Models\User;
class
PostPolicy
{
/**
* Grant all abilities to administrator.
*
*
@param
\App\Models\User
$user
*
@param
string
$ability
*
@return
bool
*/
public
function
before(User $user
,
$ability
)
{
if
(session('statut'
) ==
'admin'
) {
return
true
;
}
}
/**
* Determine if the given post can be changed by the user.
*
*
@param
\App\Models\User
$user
*
@param
\App\Models\Post
$post
*
@return
bool
*/
public
function
change(User $user
,
Post $post
)
{
return
$user
->
id ==
$post
->
user_id;
}
}
La méthode before est la première à être appelée. À ce niveau on vérifie si l'utilisateur est un administrateur, donc possède tous les droits, si c'est le cas on renvoie true.
On a ensuite la méthode change avec comme paramètres l'utilisateur et l'article. Si l'utilisateur est le créateur de l'article on renvoie true.
Maintenant que ces autorisations sont en place il faut les déclarer. Regardez le fichier app/Providers/AuthServiceProvider :
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.
<?php
namespace
App\Providers;
use
Illuminate\Support\Facades\Gate;
use
Illuminate\Foundation\Support\Providers\AuthServiceProvider as
ServiceProvider;
use
App\Models\Post;
use
App\Policies\PostPolicy;
class
AuthServiceProvider extends
ServiceProvider
{
/**
* The policy mappings for the application.
*
*
@var
array
*/
protected
$policies
=
[
Post::
class
=> PostPolicy::class,
];
/**
* Register any authentication / authorization services.
*
*
@return
void
*/
public function boot(Gate $gate)
{
$this
->
registerPolicies($gate
);
}
}
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 :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
<?php
namespace
App\Http\Controllers;
use
Illuminate\Foundation\Bus\DispatchesJobs;
use
Illuminate\Routing\Controller as
BaseController;
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 BlogController :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
public function edit($id
)
{
$post
=
$this
->
blogRepository->
getByIdWithTags($id
);
$this
->
authorize('
change
'
,
$post
);
return view('
back.blog.edit
'
,
$this
->
blogRepository->
getPostWithTags($post
));
}
public function update(PostRequest $request
,
$id
)
{
$post
=
$this
->
blogRepository->
getById($id
);
$this
->
authorize('
change
'
,
$post
);
$this
->
blogRepository->
update($request
->
all(),
$post
);
return redirect('
blog
'
)->
with('
ok
'
,
trans('
back/blog.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 :
$this
->
authorize('
change
'
,
$post
);
Donc s'il ne s'agit pas d'un administrateur ou du propriétaire de l'article ça va coincer ici.
Vous pouvez trouver la documentation complète ici.
V-E. 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.