Pourquoi Securiser une application Angular ?
Le développement d'applications web modernes nécessite une attention particulière à la sécurité. En tant que développeur Angular, vous avez l'opportunité de construire des applications robustes et sécurisées qui respectent les normes de sécurité en ligne. Un contexte réel pour comprendre l'importance de la sécurité est le débat actuel sur la protection des données personnelles (GDPR) et le conformité réglementaire.
Un cas concret serait une application de gestion de projet où les informations sensibles comme les données personnelles des employés sont stockées. Si cette application n'était pas sécurisée, elle pourrait être vulnérable à des attaques telles que l'injection SQL ou la violation d'accès, ce qui aurait des conséquences graves pour la confidentialité et la sécurité des informations.
Prerequis
- Connaissance de base de Angular
- Familiarité avec les directives, composants, services, etc.
- Connaissance de base des technologies Web (HTML, CSS, JavaScript)
- Connaissance de l'authentification et de l'autorisation
- Connaissance de la gestion des cookies et des sessions
Outils à installer :
- Node.js (v14 ou plus)
- npm (node package manager)
- Angular CLI (v10 ou plus)
- Un éditeur de code (VSCode, WebStorm, etc.)
- Postman (pour tester les API)
Concepts fondamentaux
1. Authentification et Autorisation
L'authentification est le processus par lequel un utilisateur se connecte à une application en fournissant des informations d'identification valides. L'autorisation détermine ce que l'utilisateur peut faire une fois qu'il est authentifié.
// Importer Injectable pour créer un service d'authentification
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class AuthService {
isAuthenticated = false;
login(username: string, password: string): boolean {
// Simuler une authentification réussie
if (username === 'admin' && password === 'password') {
this.isAuthenticated = true;
return true;
}
return false;
}
logout(): void {
this.isAuthenticated = false;
}
}
2. Protection contre l'injection SQL
L'injection SQL est une vulnérabilité qui permet à un attaquant d'exécuter du code SQL malveillant sur le serveur de base de données.
// Importer Injectable pour créer un service de gestion des tâches
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class TaskService {
private apiUrl = 'http://localhost:3000/tasks';
constructor(private http: HttpClient) {}
getTasks(): Observable<Task[]> {
return this.http.get<Task[]>(this.apiUrl);
}
}
3. Utilisation de HTTPS
HTTPS est le protocole sécurisé qui garantit la confidentialité et l'intégrité des données entre le client et le serveur.
## Installer http-server pour créer un serveur web local avec HTTPS
npm install -g http-server
## Créer un fichier SSL (certificat) et une clé privée
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
## Démarrer le serveur avec HTTPS
http-server -S -C cert.pem -K key.pem
4. Gestion des cookies et sessions
Les cookies et les sessions sont utilisés pour maintenir l'état de l'utilisateur entre les requêtes.
// Importer Injectable pour créer un service de gestion des utilisateurs
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class UserService {
private user = null;
setUser(user) {
this.user = user;
}
getUser() {
return this.user;
}
}
Mise en pratique : projet fil rouge
Nous allons construire un gestionnaire de tâches simple pour illustrer la sécurité en Angular.
Étape 1 : Créer le projet Angular
## Installer Angular CLI si ce n'est pas déjà fait
npm install -g @angular/cli
## Créer un nouveau projet Angular
ng new task-manager --strict
## Naviguer dans le répertoire du projet
cd task-manager
Étape 2 : Créer les composants
## Créer un composant de tâche
ng generate component task-list
## Créer un composant d'ajout de tâche
ng generate component add-task
Étape 3 : Créer le service TaskService
// Importer Injectable pour créer un service
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class TaskService {
private apiUrl = 'http://localhost:3000/tasks';
constructor(private http: HttpClient) {}
getTasks(): Observable<Task[]> {
return this.http.get<Task[]>(this.apiUrl);
}
addTask(task: Task): Observable<Task> {
return this.http.post<Task>(this.apiUrl, task);
}
}
Étape 4 : Créer le modèle Task
// Définir l'interface Task
export interface Task {
id?: number;
title: string;
completed: boolean;
}
Étape 5 : Implémenter la logique de tâche dans les composants
task-list.component.ts
// Importer Component, OnInit, Injectable pour utiliser Angular et le service TaskService
import { Component, OnInit } from '@angular/core';
import { TaskService } from '../task.service';
import { Task } from '../models/task';
@Component({
selector: 'app-task-list',
templateUrl: './task-list.component.html',
styleUrls: ['./task-list.component.css']
})
export class TaskListComponent implements OnInit {
tasks: Task[] = [];
constructor(private taskService: TaskService) {}
ngOnInit(): void {
this.getTasks();
}
getTasks(): void {
this.taskService.getTasks().subscribe(tasks => (this.tasks = tasks));
}
}
task-list.component.html
<div *ngFor="let task of tasks">
task.title
</div>
add-task.component.ts
// Importer Component, OnInit, Injectable pour utiliser Angular et le service TaskService
import { Component, OnInit } from '@angular/core';
import { TaskService } from '../task.service';
import { Task } from '../models/task';
@Component({
selector: 'app-add-task',
templateUrl: './add-task.component.html',
styleUrls: ['./add-task.component.css']
})
export class AddTaskComponent implements OnInit {
task: Task = { title: '', completed: false };
constructor(private taskService: TaskService) {}
ngOnInit(): void {}
onSubmit(): void {
this.taskService.addTask(this.task).subscribe(task => {
this.tasks.push(task);
});
}
}
add-task.component.html
<form (ngSubmit)="onSubmit()">
<input type="text" [(ngModel)]="task.title" name="title" />
<button type="submit">Ajouter</button>
</form>
Étape 6 : Ajouter la navigation entre les composants
// Importer Component, NgModule pour utiliser Angular
import { Component, NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { TaskListComponent } from './task-list/task-list.component';
import { AddTaskComponent } from './add-task/add-task.component';
const routes: Routes = [
{ path: 'tasks', component: TaskListComponent },
{ path: 'add-task', component: AddTaskComponent },
{ path: '', redirectTo: '/tasks', pathMatch: 'full' }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
Étape 7 : Mettre en œuvre la sécurité
auth.service.ts
// Importer Injectable pour créer un service d'authentification
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class AuthService {
isAuthenticated = false;
login(username: string, password: string): boolean {
if (username === 'admin' && password === 'password') {
this.isAuthenticated = true;
return true;
}
return false;
}
logout(): void {
this.isAuthenticated = false;
}
}
app-routing.module.ts
// Importer Injectable pour créer un service d'authentification
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthService } from './auth.service';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
if (this.authService.isAuthenticated) {
return true;
}
this.router.navigate(['/tasks']);
return false;
}
}
app-routing.module.ts
const routes: Routes = [
{ path: 'tasks', component: TaskListComponent },
{ path: 'add-task', component: AddTaskComponent, canActivate: [AuthGuard] },
{ path: '', redirectTo: '/tasks', pathMatch: 'full' }
];
Étape 8 : Tester l'application
## Démarrer le serveur de développement
ng serve
## Ouvrir un navigateur et accéder à http://localhost:4200/tasks
Erreurs frequentes et debugging
Erreur 1: Injection SQL
Code incorrect :
const query = `SELECT * FROM tasks WHERE title = '${task.title}'`;
this.db.query(query, (err, results) => {
// Gérer les résultats
});
Correction :
const query = 'SELECT * FROM tasks WHERE title = ?';
this.db.query(query, [task.title], (err, results) => {
// Gérer les résultats
});
Erreur 2: Authentification sans vérification
Code incorrect :
login(username: string, password: string): boolean {
return true; // Toujours authentifié
}
Correction :
login(username: string, password: string): boolean {
if (username === 'admin' && password === 'password') {
return true;
}
return false;
}
Erreur 3: Utilisation de cookies non sécurisés
Code incorrect :
document.cookie = 'user=admin; path=/';
Correction :
document.cookie = 'user=admin; path=/; secure; HttpOnly';
Pour aller plus loin
1. Authentification avec JWT (JSON Web Tokens)
Utiliser des tokens pour gérer l'authentification et l'autorisation.
2. Protection contre les Cross-Site Scripting (XSS)
Inclure la protection contre le XSS dans vos composants Angular.
3. Utilisation de SSL/TLS pour l'authentification
Utiliser SSL/TLS pour sécuriser les communications entre le client et le serveur lors de l'authentification.
Défi pratique
Implémenter une fonctionnalité pour la modification d'une tâche dans l'application de gestion des tâches.