La plupart des failles ne sont pas sophistiquées : un mot de passe dans le code, un endpoint oublié sans contrôle d'accès, une bibliothèque jamais mise à jour. Règle de base : tout est fermé par défaut, on n'ouvre que ce qui doit l'être, explicitement.
OWASP Top 10 côté .NET Confirmé
| Risque | Défense .NET |
|---|---|
| Contrôle d'accès défaillant | RequireAuthorization() par défaut (FallbackPolicy), policies, vérifier que la ressource appartient à l'appelant (IDOR) |
| Défaillances cryptographiques | HTTPS + HSTS, Data Protection, jamais d'algorithme maison ; mots de passe hachés via Identity (PBKDF2) ou délégués à un IdP |
| Injection | Requêtes paramétrées (EF Core, Dapper avec paramètres), FromSql interpolé ; Razor encode le HTML par défaut |
| Conception non sécurisée | Modélisation des menaces, rate limiting, limites de taille des requêtes |
| Mauvaise configuration | Pas de page d'exception développeur en production, en-têtes de sécurité, CORS restreint |
| Composants vulnérables | NuGet Audit (actif par défaut dans le SDK), Dependabot / Renovate, images de base patchées |
| Authentification défaillante | OIDC avec un fournisseur (Entra ID, Keycloak, Identity Platform), jetons courts, PKCE |
| SSRF | Liste blanche d'hôtes pour les URL fournies par l'utilisateur |
Secrets Junior
| Environnement | Où |
|---|---|
| Poste de dev | dotnet user-secrets (hors du dépôt) |
| CI | Secrets du pipeline, idéalement fédération d'identité (OIDC) vers le cloud sans secret stocké |
| GCP | Secret Manager, accès par compte de service |
| Kubernetes | External Secrets Operator ou CSI driver vers Secret Manager ; les Secret natifs ne sont qu'encodés en base64 |
| Windows / IIS | Variables d'environnement du pool, gMSA pour SQL Server (aucun mot de passe) |
Activez un scanner de secrets sur le dépôt (gitleaks, GitHub secret scanning). Un secret commité doit être révoqué, le réécrire dans l'historique ne suffit pas.
Authentification d'une API Confirmé
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(o =>
{
o.Authority = "https://login.acme.fr/realms/shop"; // l'IdP publie ses clés (JWKS)
o.Audience = "shop-api";
o.TokenValidationParameters.ClockSkew = TimeSpan.FromSeconds(30);
o.MapInboundClaims = false; // garder les noms de claims d'origine
});
builder.Services.AddAuthorization(o =>
o.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
// tout endpoint est protégé sauf [AllowAnonymous] explicite
// Autorisation au niveau de la ressource : l'utilisateur est authentifié, mais est-ce SA commande ?
var order = await db.Orders.FindAsync([id], ct);
if (order is null || order.CustomerId != user.CustomerId) return TypedResults.NotFound();
- CORS
AllowAnyOrigin+AllowCredentials: interdit par le navigateur et dangereux ; lister les origines. - Jeton stocké dans
localStoragecôté SPA : exposé au XSS. Préférer le pattern BFF (backend-for-frontend) avec cookieHttpOnly,Secure,SameSite. - Désérialisation :
BinaryFormatterest supprimé depuis .NET 9 ; avec Newtonsoft.Json, ne jamais activerTypeNameHandling.Allsur des entrées externes. - Messages d'erreur bavards : stack traces, noms de tables ou versions exposés au client.
À retenir
- Tout est protégé par défaut, l'anonyme est l'exception explicite.
- Aucun secret dans le code, l'image ou les logs ; identité fédérée partout où c'est possible.
- Audit des dépendances dans chaque build, images de base mises à jour chaque mois.