Checklist de revue de code ASP.NET : sécurité, performance et maintenabilité

Les logiciels d’entreprise ont tendance à rester en production pendant une longue période. C’est particulièrement vrai pour ASP.NET et ASP.NET Core, qui sont courants dans les systèmes d’entreprise de longue durée, notamment dans les environnements centrés sur Microsoft. Selon les statistiques, environ 40 % des entreprises exécutent au moins une application ASP.NET critique, et continueront à le faire pendant des années. L’examen du code doit donc tenir compte du support du framework, des correctifs de sécurité, de la maintenabilité et du risque opérationnel.

Il ne fait aucun doute qu’une telle longévité est un atout pour les grandes entreprises aux infrastructures extrêmement complexes. Cependant, cela a un coût, car les systèmes qui durent une décennie accumulent des décisions prises sous d’anciennes contraintes, par des développeurs qui sont partis depuis longtemps. De plus, le paysage des menaces était complètement différent lorsque le système a été conçu. Par conséquent, vous avez probablement besoin aujourd’hui d’un examen du code structuré pour découvrir à quoi ressemble réellement votre application ASP.NET sous la surface. Plus important encore, vous devriez en effectuer un avant qu’un incident de sécurité, un déploiement échoué ou une crise de performance ne révèle des faiblesses critiques du système.

Dans cette checklist, rédigée par des développeurs Redwerk ayant plus de 10 ans d’expérience, nous examinerons ce qui constitue un examen approfondi d’une application web ASP.NET Core. Nous nous sommes assurés qu’elle est compréhensible tant pour les personnes qui commandent les examens que pour les développeurs qui les effectuent. Par conséquent, chaque section explique non seulement quoi vérifier, mais aussi pourquoi cela compte pour votre entreprise sur le long terme.

Préparation préalable à l'examen

Tout d’abord, vous devez toujours vous souvenir qu’une revue n’est aussi bonne que la préparation qui la sous-tend. Par conséquent, le temps consacré à la collecte de contexte en amont détermine si ce processus apporte des éclaircissements ou de la confusion.

Définir la portée et les critères de succès :

  • Clarifiez si la revue couvre l’application complète, un service ou un module spécifique, un candidat pré-lancement, ou une base de code obsolète transmise à une nouvelle équipe
  • Définissez ce que signifie le succès pour vous : une liste priorisée des constatations de sécurité, une base de référence des performances, la préparation d’un déploiement en production, ou une diligence raisonnable avant une acquisition
  • Identifiez les domaines problématiques connus par l’équipe. Il peut s’agir de points de terminaison lents, d’exceptions récurrentes dans les journaux, d’échecs intermittents de déploiement, ou de fonctionnalités que personne ne souhaite toucher

Vérifier l’environnement et la chaîne d’outils :

  • Confirmez que la solution se compile proprement dans les configurations Debug et Release, sans avertissements traités comme des erreurs et sans avertissements supprimés sans justification documentée
  • Vérifiez que le projet cible une version prise en charge de .NET. La page du cycle de vie du support de Microsoft est la référence faisant autorité que vous devriez utiliser. N’oubliez pas qu’exécuter un runtime en fin de vie en production représente un risque de conformité et de sécurité
  • Vérifiez que dotnet restore se termine sans avertissements de vulnérabilité. Exécutez dotnet list package –vulnerable et documentez tous les packages NuGet signalés avant de commencer la revue

Examiner la documentation et l’historique récent :

  • La configuration et l’intégration doivent être suffisamment bien documentées pour qu’un nouveau développeur puisse être opérationnel de manière prévisible, sans dépendre des connaissances internes
  • Examinez l’historique récent des commits pour comprendre ce qui a changé. Ceci est important car une correction de sécurité validée la semaine dernière mérite une attention plus approfondie qu’un code stable inchangé depuis un an
  • Vérifiez que les pull requests ouvertes sont rebasées sur la branche principale afin que la revue reflète l’état actuel du code

Structure et architecture du projet

ASP.NET Core vous offre une grande liberté dans la manière d’organiser un projet. Cette liberté est précieuse lorsqu’elle est utilisée de manière réfléchie et coûteuse lorsqu’elle ne l’est pas du tout, alors gardez à l’esprit les points suivants.

Séparation des préoccupations :

  • Vérifiez que l’application suit une structure en couches cohérente : les contrôleurs ou les points de terminaison d’API minimaux ne gèrent que les préoccupations HTTP ; la logique métier réside dans des classes de service dédiées. L’accès aux données doit être clairement séparé des préoccupations HTTP. Que vous utilisiez EF Core directement dans les services, des objets de requête ou des gestionnaires de style CQRS, l’approche choisie doit être appliquée de manière cohérente dans toute la base de code.
  • Les contrôleurs doivent principalement coordonner les préoccupations HTTP et déléguer la logique métier. Si les actions contiennent des règles métier substantielles, une logique de persistance ou une orchestration, les responsabilités peuvent être mal placées.

Mauvaise pratique :

[HttpPost("orders")]
public async Task CreateOrder([FromBody] OrderRequest request)
{
    // Validate, calculate tax, update inventory, send email — all in the controller
    var tax = request.Total * 0.2m;
    var finalTotal = request.Total + tax;
    var order = new Order { Total = finalTotal, UserId = request.UserId };
    await _db.Orders.AddAsync(order);
    await _db.SaveChangesAsync();
    await _inventoryService.DecrementAsync(request.Items);
    await _emailService.SendConfirmationAsync(request.UserEmail, order.Id);
    return Ok(order);
}

Bonne pratique :

[HttpPost("orders")]
public async Task CreateOrder([FromBody] OrderRequest request)
{
    var result = await _orderService.CreateAsync(request);
    return result.IsSuccess ? Ok(result.Value) : BadRequest(result.Error);
}

Injection de dépendances :

  • Vérifiez que tous les services sont enregistrés dans le conteneur d’injection de dépendances et que l’injection par constructeur est utilisée de manière cohérente, car la résolution manuelle des services à partir du conteneur à l’intérieur des méthodes est un signe que l’architecture se bat contre sa propre conception.
  • Vérifiez que les durées de vie des services sont appropriées : transitoires pour les opérations légères et sans état. Par requête (scoped) pour les services qui partagent l’état dans le cadre d’une requête (comme DbContext), et singleton uniquement pour les services qui sont véritablement thread-safe et sans état.

Organisation du projet et de la solution :

  • Confirmez que la structure de la solution reflète les frontières architecturales. Un projet nommé MyApp.Core contenant à la fois la logique du domaine et les modèles de base de données a un problème de nommage qui signale généralement un problème organisationnel sous-jacent.
  • Vérifiez qu’il n’existe pas de références circulaires entre les projets. Elles sont techniquement possibles dans certaines configurations, mais indiquent toujours une conception de dépendances qui doit être démêlée.

Pipeline de middleware et gestion des requêtes

La colonne vertébrale de toute application ASP.NET Core est son pipeline de middleware. Par conséquent, l’ordre dans lequel les composants de middleware sont enregistrés affecte directement non seulement ses performances et son comportement, mais aussi sa sécurité. Un corps humain ne peut pas fonctionner correctement avec une colonne vertébrale compromise, et il en va de même pour une application ASP.NET. Un mauvais ordre peut silencieusement casser l’authentification, exposer des exceptions aux clients ou ajouter un traitement inutile à chaque requête.

Ordre des middlewares :

  • Vérifiez que UseExceptionHandler ou UseDeveloperExceptionPage est enregistré en premier dans le pipeline, car le middleware de gestion des exceptions doit envelopper tout le reste pour intercepter les erreurs des composants en aval.
  • Confirmez que les pipelines de production suivent l’ordre recommandé par Microsoft : gestion des exceptions tôt, HSTS dans les environnements autres que le développement, redirection HTTPS avant la gestion des requêtes, puis fichiers statiques si nécessaire, routage, authentification et autorisation. Un ordre incorrect des middlewares peut entraîner un comportement de sécurité erroné ou un traitement de requête inutile.
  • Vérifiez que l’authentification (UseAuthentication) précède l’autorisation (UseAuthorization). Inverser l’ordre signifie que les décisions d’autorisation sont prises avant que l’identité de l’utilisateur ne soit établie, ce qui ne manquera pas de causer des problèmes ultérieurement.
  • Passez en revue le pipeline complet pour tout composant middleware enregistré dans le mauvais ordre ou enregistré plus d’une fois, car un middleware redondant ajoute de la latence sans bénéfice.

Validation des requêtes :

  • Vérifiez que la validation des modèles est appliquée de manière cohérente. Dans les contrôleurs marqués avec [ApiController], ASP.NET Core retourne automatiquement les erreurs de validation pour les modèles invalides. Dans les contrôleurs MVC, les Razor Pages, les Minimal APIs ou les pipelines personnalisés, confirmez que la validation est gérée via des filtres, des filtres d’endpoints, FluentValidation ou des vérifications explicites le cas échéant.
  • Confirmez que les limites de taille des requêtes sont configurées de manière appropriée. Les paramètres par défaut MultipartBodyLengthLimit et MaxRequestBodySize ne conviennent pas toujours aux charges de travail de production et doivent être définis explicitement en fonction des exigences de l’application.

Gestion des réponses :

  • Vérifiez que les réponses incluent des en-têtes cache-control appropriés pour le contenu qui devrait ou ne devrait pas être mis en cache par les navigateurs et les CDN.
  • Vérifiez que la compression des réponses est activée pour les types de contenu textuels. La compression Brotli et Gzip réduit considérablement la bande passante pour les API riches en JSON.

Authentification et autorisation

Vous devez savoir que les problèmes d’authentification et d’autorisation représentent une part importante des incidents de sécurité réels dans les applications web. Pour mieux comprendre l’importance de ce point, considérez qu’en octobre 2025, Microsoft a corrigé la CVE-2025-55315, une vulnérabilité d’empoisonnement de requête HTTP dans le serveur Kestrel, qui a reçu le score CVSS le plus élevé jamais attribué à un problème ASP.NET Core : 9,9 sur 10.

Microsoft a décrit la CVE-2025-55315 comme une « vulnérabilité d’empoisonnement de requête HTTP dans Kestrel qui pourrait permettre à un attaquant de masquer une requête à l’intérieur d’une autre ». Pour simplifier, en fonction du déploiement et du comportement de l’application, ce problème pourrait affecter les décisions d’authentification et d’autorisation. Une autre vulnérabilité créée était l’ouverture d’une voie pour la manipulation des requêtes. L’incident illustre pourquoi l’authentification et l’autorisation doivent être appliquées de manière cohérente sur les chemins de requête et les configurations de déploiement. N’oubliez pas que les vulnérabilités de l’infrastructure peuvent amplifier les faiblesses du contrôle d’accès au niveau de l’application.

Mise en œuvre de l’authentification :

  • Vérifiez que l’application utilise ASP.NET Core Identity, Azure Active Directory ou une bibliothèque OAuth 2.0 / OpenID Connect établie plutôt qu’une implémentation d’authentification personnalisée, car les schémas d’authentification personnalisés introduisent des risques sans fournir d’avantage significatif par rapport aux solutions matures déjà disponibles.
  • Vérifiez que la configuration JWT spécifie explicitement les algorithmes de signature autorisés. Les paramètres de validation de jeton doivent valider l’émetteur, l’audience, la durée de vie et la clé de signature. Si l’application restreint les algorithmes de signature autorisés, assurez-vous que seuls les algorithmes attendus sont acceptés et que les jetons non signés sont rejetés.
// Explicit token validation — never rely on defaults alone
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = configuration["Jwt:Issuer"],
            ValidateAudience = true,
            ValidAudience = configuration["Jwt:Audience"],
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
        };
    });
  • Confirmez que la logique de rafraîchissement des jetons gère gracieusement l’expiration, et que les jetons expirés sont effacés du stockage client. Un jeton qui était valide il y a six mois ne doit plus accorder d’accès.

Mise en œuvre de l’autorisation :

  • Vérifiez que l’autorisation est appliquée au niveau du contrôleur ou du point de terminaison, et pas seulement vérifiée dans les corps des méthodes d’action. Les vérifications d’autorisation intégrées sont faciles à oublier et impossibles à auditer systématiquement.
  • Confirmez que l’autorisation basée sur les rôles et les politiques est utilisée de manière cohérente et évitez de mélanger des vérifications ad hoc de User.IsInRole() éparpillées dans la base de code avec des définitions de politiques formelles.
  • Recherchez les références d’objets directes non sécurisées : les points de terminaison qui acceptent un ID d’enregistrement dans l’URL doivent vérifier que l’utilisateur authentifié a la permission d’accéder à cet enregistrement spécifique, pas seulement qu’il est connecté.

Vulnérable : tout utilisateur authentifié peut accéder à n’importe quelle facture

[HttpGet("invoices/{id}")]
[Authorize]
public async Task GetInvoice(int id)
{
    var invoice = await _db.Invoices.FindAsync(id);
    return Ok(invoice);
}

Correct : portée de la requête aux données de l’utilisateur authentifié

[HttpGet("invoices/{id}")]
[Authorize]
public async Task GetInvoice(int id)
{
    var userId = User.GetUserId();
    var invoice = await _db.Invoices
        .FirstOrDefaultAsync(i => i.Id == id && i.OwnerId == userId);
    return invoice is null ? NotFound() : Ok(invoice);
}

Accès aux données et Entity Framework Core

La base de données est le lieu d’origine de la plupart des problèmes de performance des applications ASP.NET. La majorité des problèmes dans ce domaine proviennent des requêtes lentes, du chargement de données non suivi, des requêtes N+1 et des appels de base de données synchrones sur les threads de requête. Ces problèmes sont non seulement débilitants pour votre produit, mais aussi plutôt coûteux à résoudre. Par conséquent, leur identification est l’un des avantages les plus précieux d’un examen de code.

Efficacité des requêtes :

  • Recherchez les modèles de requêtes N+1 : une requête qui charge une liste d’entités puis les parcourt pour charger les données associées pour chacune d’elles exécute une requête pour obtenir N enregistrements, puis N requêtes supplémentaires pour charger leurs relations, ce qui dégrade considérablement les performances à mesure que le volume de données augmente.

Mauvaise pratique :

// Loads all orders, then queries the database once per order to load the customer
var orders = await _db.Orders.ToListAsync();
foreach (var order in orders)
{
    order.Customer = await _db.Customers.FindAsync(order.CustomerId); // N extra queries
}

Bonne pratique :

// Single query with a join — one database round trip
var orders = await _db.Orders
    .Select(o => new OrderDto
    {
        Id = o.Id,
        Total = o.Total,
        CustomerName = o.Customer.Name
    })
    .ToListAsync();
  • Vérifiez que les requêtes en lecture seule utilisent `AsNoTracking()`. Selon la documentation EF Core de Microsoft, les requêtes sans suivi renvoient les résultats plus efficacement car EF Core n’a pas besoin de maintenir l’état de suivi des modifications pour les objets qui ne seront jamais mis à jour.
  • Assurez-vous que les projections utilisent `Select()` pour récupérer uniquement les colonnes dont le point de terminaison a réellement besoin, plutôt que de charger des graphes d’entités complets et de rejeter la plupart des données.

Accès asynchrone à la base de données :

  • Confirmez que tous les appels EF Core utilisent leurs variantes asynchrones : `ToListAsync()`, `FirstOrDefaultAsync()`, `SaveChangesAsync()`, etc. La documentation des meilleures pratiques ASP.NET Core de Microsoft indique explicitement que les appels bloquants comme `.Result` et `.Wait()` sur des opérations de base de données asynchrones peuvent épuiser le pool de threads sous charge.
  • Vérifiez que les instances `DbContext` ne sont pas partagées entre les threads ni stockées comme singletons. `DbContext` n’est pas thread-safe, son cycle de vie doit donc être limité à une seule requête.

Hygiène du schéma et des migrations :

  • Confirmez que les migrations de base de données sont validées dans le contrôle de version et que l’historique des migrations est linéaire, car les lacunes ou les conflits dans l’historique des migrations entraînent des échecs de déploiement.
  • Vérifiez que les migrations sont appliquées via un processus de déploiement contrôlé, tel que des scripts de migration examinés, des étapes de déploiement CI/CD ou une procédure de publication approuvée. Vous devriez éviter de vous fier à des migrations manuelles exécutées par les développeurs ou à des modifications automatiques non examinées en production. Les étapes de migration manuelles sont le genre de choses qui sont oubliées au pire moment.
  • Examinez les index pour confirmer que les colonnes utilisées dans les clauses `WHERE`, les conditions `JOIN` et les expressions `ORDER BY` ont des index appropriés. Les index manquants sont la cause la plus fréquente de requêtes qui fonctionnent bien en développement et échouent en production.

Conception d'API et hygiène des contrats

Une API ASP.NET Core est un contrat entre le serveur et chaque client qui en dépend. Par conséquent, rompre ce contrat, même accidentellement, peut entraîner des défaillances souvent invisibles pendant le développement. Cependant, elles seraient extrêmement pénibles lorsque ces problèmes atteignent la production.

Gestion des versions :

  • Vérifiez que l’API utilise une stratégie de gestion des versions cohérente, qu’il s’agisse de la version dans le chemin d’URL (/api/v1/), de la version dans la chaîne de requête ou de la version basée sur l’en-tête. L’approche spécifique que vous choisissez importe moins que le fait d’en avoir une et de l’appliquer de manière cohérente.
  • Confirmez que les versions obsolètes de l’API sont clairement marquées et qu’un calendrier de migration est communiqué aux consommateurs. Supprimer un point d’accès sans préavis est un moyen rapide de casser les intégrations.

Validation des entrées et mise en forme des réponses :

  • Vérifiez que tous les modèles de requête sont annotés avec des attributs d’annotation de données ou des règles Fluent Validation, et que la validation est appliquée globalement via un filtre plutôt que vérifiée individuellement dans chaque action.
  • Confirmez que les réponses de l’API utilisent un format d’enveloppe cohérent ; des formes de réponse incohérentes obligent les clients à gérer chaque point d’accès différemment et rendent la gestion des erreurs fragile.
  • Vérifiez que les réponses d’erreur renvoient des codes d’état HTTP appropriés et des messages d’erreur significatifs, car le fait de renvoyer un 200 OK avec un champ d’erreur dans le corps est un schéma qui brise les sémantiques HTTP et confond les clients.

OpenAPI et documentation :

  • Vérifiez que Swagger ou un outil de documentation OpenAPI compatible est configuré et produit une documentation précise et à jour. Gardez à l’esprit que des documents d’API obsolètes sont presque pires que pas de documents du tout, car ils induisent activement les consommateurs en erreur.
  • Confirmez que les points d’accès d’API publics et les modèles de requête/réponse consommés en externe sont documentés de manière suffisamment claire pour que la documentation OpenAPI générée soit utile. Donnez la priorité aux contrats publics, aux champs non évidents, aux réponses d’erreur, aux exigences d’authentification et au comportement de gestion des versions. Ceux-ci alimentent directement la documentation générée et ne nécessitent aucun effort supplémentaire une fois l’habitude prise.

Bonnes pratiques de sécurité

Vous devriez absolument consulter la OWASP DotNet Security Cheat Sheet, maintenue spécifiquement pour les applications ASP.NET. Cependant, et c’est le plus important, vous devez la considérer comme un point de départ et non comme une limite à vos pratiques de sécurité. Les éléments ci-dessous découlent des problèmes que nous rencontrons le plus souvent lors des revues de bases de code ASP.NET en production.

Prévention des injections :

  • Examinez toutes les requêtes de base de données pour la concaténation de chaînes ; toute requête qui construit du SQL en ajoutant des entrées utilisateur est vulnérable à l’injection SQL, quelle que soit la probabilité apparente du chemin d’exploitation. Les requêtes paramétrées et l’interface LINQ d’EF Core sont les bons outils.
  • Vérifiez les risques d’injection de commandes dans tout code qui transmet des valeurs fournies par l’utilisateur à Process.Start, des commandes shell ou une exécution de code dynamique.

Script intersites (XSS) et falsification intersites de requêtes (CSRF) :

  • Confirmez que les vues Razor utilisent systématiquement la syntaxe d’encodage @ et que le rendu HTML brut via Html.Raw() n’est utilisé que lorsque strictement nécessaire et avec du contenu entièrement fiable.
  • Pour les flux de navigateur authentifiés par cookie, vérifiez que la protection anti-falsification est appliquée aux soumissions de formulaire qui modifient l’état et aux méthodes HTTP non sécurisées pertinentes. Pour les API de jetons bearer, évaluez le risque CSRF séparément. Le CSRF est principalement une préoccupation lorsque les navigateurs joignent automatiquement les informations d’identification, telles que les cookies ou l’authentification de base.

En-têtes de sécurité HTTP :

  • Confirmez que les en-têtes de sécurité sont définis via des middlewares ou un filtre personnalisé : Content-Security-Policy, X-Content-Type-Options, X-Frame-Options, Referrer-Policy et Strict-Transport-Security constituent la base.
  • Vérifiez que HTTPS est appliqué en production et que UseHsts est configuré avec une valeur max-age appropriée. HSTS indique aux navigateurs d’utiliser toujours HTTPS pour votre domaine, même si l’utilisateur tape HTTP manuellement.

Exposition de données sensibles :

  • Assurez-vous que les secrets de production, les clés d’API, les clés de signature et les chaînes de connexion privilégiées ne sont pas validés dans le contrôle de version. Les valeurs par défaut locales non sensibles peuvent se trouver dans les fichiers de configuration, mais les vrais secrets doivent provenir des variables d’environnement, des magasins de secrets, de l’identité managée ou de la configuration au moment du déploiement. Ils appartiennent aux variables d’environnement, à Azure Key Vault ou à un autre système de gestion des secrets.
  • Vérifiez que les données sensibles ne sont pas journalisées ; une entrée de journal qui capture le corps entier d’une requête peut involontairement conserver des mots de passe, des numéros de carte de crédit ou des données personnelles bien après le traitement de la requête.
  • Confirmez que les détails des exceptions ne sont jamais envoyés aux clients en production. UseDeveloperExceptionPage d’ASP.NET Core doit être restreint à l’environnement de développement.

Modèles asynchrones et performances

ASP.NET Core est conçu par défaut pour une haute concurrence, ce qui explique en partie sa popularité pour les systèmes de niveau entreprise. Cependant, cette concurrence est compromise à chaque fois qu’une opération synchrone bloque un thread de requête. Un seul appel bloquant dans un chemin de code critique peut réduire le débit d’un ordre de grandeur sous forte charge. C’est quelque chose dont vous devez toujours vous méfier.

Éviter le blocage synchrone :

  • Recherchez `.Result`, `.Wait()` et `.GetAwaiter().GetResult()` sur `Task`. Dans ASP.NET Core, ces appels bloquent les threads de requête et peuvent contribuer à la famine du pool de threads, à une faible débit et à des pics de latence sous charge. Il est préférable de privilégier `async` sur toute la pile d’appels.
  • Vérifiez que `async void` n’est jamais utilisé dans les actions de contrôleur ou les méthodes de service. Évitez `async void` en dehors des gestionnaires d’événements. Les méthodes `async void` ne peuvent pas être attendues, les exceptions sont difficiles à observer et à gérer correctement, et les appelants ne peuvent pas savoir quand l’opération est terminée.

ThreadPool et IHttpClientFactory :

  • Vérifiez que `HttpClient` n’est pas instancié directement avec `new HttpClient()` dans les méthodes. Préférez `IHttpClientFactory` pour les appels HTTP sortants, surtout lorsque les clients ont besoin de configurations nommées/typées, de politiques de résilience, de journalisation ou de durées de vie de gestionnaires contrôlées. Vérifiez également que le code ne crée pas d’instances `HttpClient` de courte durée par requête, ce qui peut épuiser les sockets sous charge.
  • Vérifiez toute opération gourmande en CPU exécutée sur les threads de requête. Les charges de travail telles que le traitement d’images, la génération de PDF ou les exportations de grandes quantités de données doivent être déchargées vers des services d’arrière-plan ou des files d’attente de messages plutôt que de bloquer le thread de requête HTTP.

Mise en cache des réponses et mise en cache de la sortie :

  • Identifiez les points de terminaison qui servent répétitivement les mêmes données et vérifiez que la mise en cache des réponses ou de la sortie est implémentée pour eux. Un point de terminaison qui interroge la base de données à chaque requête pour renvoyer des données qui changent une fois par heure gaspille des ressources.
  • Confirmez que la logique d’invalidation du cache existe et est correcte, car un cache qui croît indéfiniment ou qui sert des données obsolètes après une écriture est plus difficile à déboguer que l’absence de cache.

Gestion de la configuration et des secrets

La configuration système est la cause sous-jacente de l’écart entre le comportement d’une application en développement et en production. Par conséquent, il est essentiel de l’examiner attentivement lors de la revue de code pour identifier tout problème susceptible d’entraîner des fuites de données dévastatrices.

Configuration spécifique à l’environnement :

  • Confirmez que toutes les valeurs spécifiques à l’environnement proviennent des fichiers appsettings.{Environment}.json ou des variables d’environnement, et non de valeurs codées en dur dans le code source. Une chaîne de connexion pointant vers une base de données locale qui se retrouve dans un déploiement de production causera une interruption.
  • Vérifiez que l’application valide la configuration requise au démarrage et échoue bruyamment avec une erreur descriptive si quelque chose manque. Une application partiellement configurée qui démarre avec succès mais plante à la première utilisation est plus difficile à diagnostiquer qu’une application qui refuse de démarrer.
// Fail at startup, not at runtime
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
    ?? throw new InvalidOperationException(
        "Required configuration 'ConnectionStrings:DefaultConnection' is missing.");
  • Vérifiez que les fichiers appsettings.Development.json sont dans .gitignore s’ils contiennent de véritables informations d’identification, même celles de développement. Les informations d’identification de développeur validées dans le contrôle de version ont un long historique d’apparition en production par copier-coller ou mauvaise configuration. Découvrez ce que cela pourrait entraîner dans l’un de nos articles sur les fuites de données.

Secrets en production :

  • Vérifiez que les secrets de production sont gérés via Azure Key Vault, AWS Secrets Manager, des variables d’environnement injectées au moment du déploiement, ou un mécanisme équivalent.
  • Confirmez que l’application utilise des identités managées ou la fédération d’identité de charge de travail pour accéder aux services Azure plutôt que des chaînes de connexion avec des informations d’identification intégrées, car les identités managées éliminent une catégorie entière de problèmes de rotation et de fuite de secrets.

Tests et couverture de code

Il est crucial de se rappeler qu’il est impossible de vérifier la stabilité de la base de code sans tests. Il est indéniable que les systèmes non testés présentent des instabilités et des vulnérabilités qui se manifesteraient au pire moment possible.

Tests unitaires et d’intégration :

  • Vérifiez que la logique métier critique, les cas limites et les chemins d’échec sont couverts par des tests automatisés.
  • Confirmez que les tests utilisent l’injection de dépendances et le mock (Moq, NSubstitute) pour isoler le code testé des bases de données réelles, des API externes et des systèmes de fichiers. Notez que les tests qui nécessitent une base de données active sont des tests d’intégration et doivent être traités différemment des tests unitaires.
  • Vérifiez que WebApplicationFactory<T> est utilisé pour les tests d’intégration plutôt que de lancer des environnements de déploiement complets. Il fournit un hôte de test léger en mémoire qui exécute le pipeline de middlewares complet sans nécessiter de serveur déployé.

Qualité des tests :

  • Examinez les noms des tests pour confirmer qu’ils décrivent le comportement plutôt que les noms de méthodes : CreateOrder_WhenInventoryIsInsufficient_ShouldReturnBadRequest est plus utile que TestCreateOrder.
  • Vérifiez que le pipeline CI bloque les fusions en cas d’échec des tests et qu’aucun test n’est désactivé avec [Ignore] ou Skip sans raison suivie.
  • Confirmez que les chemins critiques sont testés et que la couverture est suivie. Les pourcentages bruts seuls ne garantissent pas la qualité : une série de tests superficiels peut atteindre 80 % de couverture tout en manquant la logique qui compte réellement. Pour les systèmes critiques, imposer un seuil minimum ajoute un filet de sécurité utile, mais l’objectif est une couverture significative de la logique métier, des cas limites et des chemins d’échec.

Journalisation, Observabilité et Gestion des erreurs

Imaginez la situation : un appel à 2 heures du matin car quelque chose a mal tourné en production et l’ensemble du système a planté. Le niveau de votre panique dans ce cas dépendra de la qualité de vos journaux. Les informations que vous en tirez font souvent la différence entre une résolution en dix minutes et une enquête de trois heures. Par conséquent, imposer l’observabilité et une journalisation cohérente est une base indispensable pour tout système de niveau entreprise, quel que soit le langage de programmation ou le framework utilisé.

Journalisation structurée :

  • Vérifiez que l’application utilise ILogger<T> de l’abstraction intégrée Microsoft.Extensions.Logging plutôt que des bibliothèques de journalisation statiques ou des appels Console.WriteLine dispersés. L’abstraction intégrée s’intègre à Serilog, NLog et Azure Application Insights par configuration plutôt que par des modifications de code.
  • Confirmez que les entrées de journal utilisent des propriétés structurées plutôt que l’interpolation de chaînes ; logger.LogInformation(“Processing order {OrderId} for user {UserId}”, orderId, userId) produit une entrée de journal interrogeable, tandis que logger.LogInformation($”Processing order {orderId} for user {userId}”) produit une chaîne simple qui ne peut pas être interrogée.

Gestion globale des exceptions :

  • Vérifiez qu’un middleware de gestion globale des exceptions est configuré et qu’il renvoie des réponses d’erreur cohérentes et assainies, car différentes parties de l’application ne doivent pas gérer les exceptions différemment.
  • Confirmez que les exceptions non gérées sont enregistrées avec suffisamment de contexte sécurisé pour reconstruire ce qui s’est passé : chemin de la requête, ID de corrélation, identifiant de l’utilisateur authentifié le cas échéant et pile d’appels. Évitez d’enregistrer les corps de requête bruts, les identifiants, les jetons, les données de paiement ou les données personnelles, sauf si elles sont explicitement protégées et justifiées.
  • Vérifiez que les erreurs opérationnelles (échecs de validation, ressources introuvables, violations de règles métier) renvoient des réponses 4xx appropriées et sont distinguées des erreurs inattendues qui renvoient des réponses 5xx. Traiter chaque exception comme une 500 rend impossible la distinction entre les bugs et les conditions d’erreur attendues.

Contrôles d’intégrité et surveillance :

  • Vérifiez que les points de terminaison de contrôle d’intégrité sont configurés à l’aide des contrôles d’intégrité intégrés d’ASP.NET Core. Ceux-ci doivent valider que l’application peut se connecter à sa base de données, aux dépendances externes et à toute infrastructure critique.
  • Confirmez qu’Application Insights, Datadog ou un outil APM équivalent est configuré en production. Les journaux structurés et les contrôles d’intégrité sont nécessaires, mais le traçage distribué entre les limites des services nécessite des outils dédiés.

Pourquoi faire confiance à Redwerk pour la revue de code ASP.NET

L’enquête de Stack Overflow auprès des développeurs a confirmé qu’ASP.NET Core se classe parmi les dix frameworks web les plus utilisés au monde, avec 19,1 % des développeurs professionnels qui l’utilisent activement. Cela prouve qu’ASP.NET Core est un framework mature et performant, et que les applications qui en sont dotées peuvent servir des millions d’utilisateurs, fonctionner dans des secteurs réglementés et rester en production pendant une décennie ou plus.

Cependant, la liste de contrôle ci-dessus existe car la maturité du framework ne protège pas une base de code des décisions prises pendant le développement. Des éléments tels qu’une vérification d’authentification manquante, un appel bloquant à la base de données sur un chemin d’accès critique, des secrets envoyés au contrôle de version ou un pipeline de middleware assemblé dans le mauvais ordre entraînent des problèmes sérieux qui pourraient potentiellement faire planter l’ensemble du système.

Jetons un coup d’œil à un exemple pratique de ce à quoi ressemble ce système lorsqu’il est bien réalisé : Current, un SaaS de gouvernement électronique que nous avons construit pour les agences d’aide sociale à travers les États-Unis. La plateforme fonctionne sur .NET et Azure, gère des données sensibles de citoyens pour plusieurs agences gouvernementales américaines et nécessitait une conformité ADA à 100 % dès le premier jour.

Alors que nous construisions pour cet environnement, nous avons dû respecter chaque élément de cette liste de contrôle avant qu’une seule ligne de code ne soit mise en production. Par conséquent, les développeurs et testeurs de Redwerk se sont assurés que l’autorisation était étroitement liée aux données de chaque agence et que le HTTPS était appliqué partout. Nous avons également mis en œuvre une journalisation structurée qui satisfaisait aux exigences d’audit et des vérifications de santé qui permettaient aux équipes opérationnelles de vérifier chaque dépendance sans toucher à l’application. La plateforme est maintenant utilisée par des divisions d’aide sociale à travers le pays.

Notre équipe de développement ASP.NET travaille avec la pile Microsoft depuis 2005. Par conséquent, lorsque nous examinons une base de code, nous couvrons l’ensemble du tableau, y compris :

  • L’architecture
  • La sécurité
  • Les modèles d’accès aux données
  • La correction asynchrone
  • L’hygiène de la configuration
  • La couverture des tests

Lorsque l’examen est terminé, vous recevez un rapport spécifique, classé par gravité, et rédigé de manière à ce que les décideurs et les développeurs puissent agir. Si votre application ASP.NET a besoin d’une analyse plus approfondie, notre service de revue de code est le point de départ. Dites-nous ce que vous avez construit et nous vous dirons ce que nous trouvons.

Voyez par vous-même comment fonctionne une revue de code : plus de 80 améliorations et risques de sécurité identifiés pour une place de marché mobile

Veuillez saisir votre adresse courriel professionnelle n'est pas un courriel professionnel