Pourquoi Securiser une application .NET ?
La sécurité est un élément fondamental pour tout développement logiciel, mais particulièrement crucial dans les environnements modernes où l'information est partagée et traitée à grande échelle. En tant que développeur senior .NET avec 10+ ans d'expérience, vous devez comprendre que la sécurité ne se limite pas aux aspects graphiques ou fonctionnels de votre application. Elle englobe également des aspects tels que la protection contre les attaques externes, la gestion des données sensibles et la conformité réglementaire.
Un cas concret est le secteur financier, où une application non sécurisée peut entraîner des dommages financiers importants et même des pénalités légales. Dans ce contexte, la sécurité n'est pas un choix optionnel mais une exigence de la part des clients et des régulateurs.
Prerequis
- Connaissance approfondie du langage C#
- Compréhension des concepts fondamentaux de l'architecture .NET
- Familiarité avec les frameworks ASP.NET Core et Entity Framework Core
- Connaissance des principes de sécurité informatique (cryptage, authentification, autorisation)
- Installation d'Visual Studio 2019 ou ultérieur avec la charge de travail de développement web
Concepts fondamentaux
Authentification et Autorisation
L'authentification est le processus par lequel un utilisateur prouve son identité. L'autorisation consiste à déterminer les actions que l'utilisateur peut effectuer une fois authentifié.
Schema Mental:
Authentification -> Identification (Utilisateur) -> Authentification (Serveur)
Autorisation -> Permission de l'utilisateur sur la ressource
net
## Authentification avec ASP.NET Core
public async Task<IActionResult> Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
var user = await _userManager.FindByNameAsync(model.Username);
if (user != null && await _userManager.CheckPasswordAsync(user, model.Password))
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
}
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
}
return View(model);
}
Cryptage des données
Le cryptage est un processus qui transforme les données en une forme non lisible pour prévenir les accès non autorisés.
Schema Mental:
Données claires -> Cryptage (algorithme) -> Données chiffrées
Données chiffrées -> Décryptage (meme algorithme) -> Données claires
net
## Cryptage et décryptage de mots de passe avec ASP.NET Core
public string Encrypt(string password)
{
using (var sha256 = SHA256.Create())
{
byte[] bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(password));
StringBuilder builder = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
builder.Append(bytes[i].ToString("x2"));
}
return builder.ToString();
}
}
Validation des entrées utilisateur
La validation des entrées utilisateur est cruciale pour prévenir les attaques telles que l'injection SQL et le hacking par déni de service (DoS).
Schema Mental:
Entrée utilisateur -> Validation -> Traitement sécurisé
net
## Validation des entrées utilisateur avec ASP.NET Core
[HttpPost]
public async Task<IActionResult> Create([Bind("Title,Description")] BlogPost blogPost)
{
if (ModelState.IsValid)
{
// Traitement sécurisé de la publication de blog
_context.Add(blogPost);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(blogPost);
}
Mise en pratique : projet fil rouge
Mini-Projet : Gestionnaire de Tâches
Étape 1 : Création du Projet
- Ouvrez Visual Studio.
- Créez un nouveau projet ASP.NET Core Web App (Model-View-Controller).
- Sélectionnez "ASP.NET Core 6.0" et "Web Application".
- Nommez le projet
TaskManageret cliquez sur "Create".
Étape 2 : Configuration de la base de données
- Installez Entity Framework Core.
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
- Créez un modèle de données.
## Models/Task.cs
public class Task
{
public int Id { get; set; }
public string Title { get; set; }
public bool IsCompleted { get; set; }
}
- Configurez la base de données.
## Data/ApplicationDbContext.cs
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options) {}
public DbSet<Task> Tasks { get; set; }
}
- Mettez à jour le fichier
appsettings.json.
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=TaskManager;Trusted_Connection=True;"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
- Ajoutez la configuration de la base de données dans
Program.cs.
## Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
Étape 3 : Création des contrôleurs et vues
- Créez un contrôleur
TasksController.
## Controllers/TasksController.cs
public class TasksController : Controller
{
private readonly ApplicationDbContext _context;
public TasksController(ApplicationDbContext context)
{
_context = context;
}
public async Task<IActionResult> Index()
{
return View(await _context.Tasks.ToListAsync());
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Title,IsCompleted")] Task task)
{
if (ModelState.IsValid)
{
_context.Add(task);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(task);
}
}
- Créez une vue
Index.
<!-- Views/Tasks/Index.cshtml -->
@model IEnumerable<TaskManager.Models.Task>
@{
ViewData["Title"] = "Tasks";
}
<h1>Tasks</h1>
<p>
<a asp-action="Create">Create New Task</a>
</p>
<table class="table">
<thead>
<tr>
<th>Title</th>
<th>Status</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>@item.Title</td>
<td>@(item.IsCompleted ? "Completed" : "Pending")</td>
<td>
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-action="Details" asp-route-id="@item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
<p>
<a asp-action="Create">Create New Task</a>
</p>
- Créez une vue
Create.
<!-- Views/Tasks/Create.cshtml -->
@model TaskManager.Models.Task
@{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Task</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="IsCompleted" class="control-label"></label>
<input asp-for="IsCompleted" class="form-check-input" />
<span asp-validation-for="IsCompleted" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Étape 4 : Exécution de l'application
- Lancez l'application.
dotnet run
- Naviguez jusqu'à
/Taskspour voir la liste des tâches et créer de nouvelles tâches.
Erreurs frequentes et debugging
Erreur 1 : InvalidOperationException: The model backing 'DbContext' has changed since the database was last read.
## ❌ Mauvais
_context.Tasks.ToList();
## ✅ Correct
await _context.Tasks.ToListAsync();
Erreur 2 : SqlException: There is already an object named 'Task' in the database.
## ❌ Mauvais
public DbSet<Task> Tasks { get; set; }
## ✅ Correct
public DbSet<MyTask> Tasks { get; set; }
Erreur 3 : InvalidOperationException: The ViewData item that has the key 'TaskManager.Models.Task' is of type 'System.Collections.Generic.List1[TaskManager.Models.Task]' but must be of type 'TaskManager.Models.Task'.`
## ❌ Mauvais
@model TaskManager.Models.Task
## ✅ Correct
@model IEnumerable<TaskManager.Models.Task>
Pour aller plus loin
1. Authentification avec JWT (JSON Web Tokens)
L'utilisation de JWT permet une authentification sécurisée sans utiliser des cookies.
Lien vers la documentation officielle
2. Sécurité des APIs RESTful
La sécurisation des APIs est cruciale pour prévenir l'accès non autorisé.
Lien vers la documentation officielle
3. Protection contre les injections SQL
La protection contre les injections SQL est essentielle pour prévenir les attaques SQL injection.
Lien vers la documentation officielle
Défi pratique : Ajouter une fonctionnalité de sécurité
Ajoutez une fonctionnalité de protection contre les injections SQL à un projet existant et expliquez comment elle fonctionne.
Ce tutoriel couvre les principaux aspects de la sécurité d'une application .NET, en fournissant des exemples pratiques et des détails techniques. En suivant ce guide, vous serez mieux préparé pour gérer la sécurité dans vos projets .NET futurs.