ASP.NET Code Review Checklist: Sicherheit, Leistung & Wartbarkeit

Enterprise-Software verbleibt oft lange Zeit in der Produktion. Dies gilt insbesondere für ASP.NET und ASP.NET Core, die häufig in langlebigen Unternehmenssystemen, insbesondere in Microsoft-zentrierten Umgebungen, eingesetzt werden. Laut Statistiken betreiben etwa 40 % der Unternehmen mindestens eine kritische ASP.NET-Anwendung und werden dies auch in Zukunft tun. Daher müssen bei der Code-Überprüfung Framework-Unterstützung, Sicherheitspatches, Wartbarkeit und Betriebsrisiken berücksichtigt werden.

Zweifellos ist eine solche Langlebigkeit eine Stärke für große Unternehmen mit äußerst komplexen Infrastrukturen. Sie hat jedoch ihren Preis, da Systeme, die ein Jahrzehnt überdauern, Entscheidungen ansammeln, die unter alten Einschränkungen von Entwicklern getroffen wurden, die längst weitergezogen sind. Darüber hinaus sah die Bedrohungslandschaft völlig anders aus, als das System entworfen wurde. Daher benötigen Sie wahrscheinlich eine strukturierte Code-Überprüfung, um herauszufinden, wie Ihre ASP.NET-Anwendung tatsächlich unter der Oberfläche aussieht. Am wichtigsten ist, dass Sie eine durchführen, bevor ein Sicherheitsvorfall, ein fehlgeschlagenes Deployment oder eine Leistungskrise kritische Systemschwächen aufdecken.

In dieser Checkliste, verfasst von Redwerk-Entwicklern mit über 10 Jahren Erfahrung, behandeln wir, was eine gründliche Überprüfung einer ASP.NET Core-Webanwendung ausmacht. Wir haben dafür gesorgt, dass sie sowohl für die Personen, die Überprüfungen in Auftrag geben, als auch für die Entwickler, die sie durchführen, verständlich ist. Daher erklärt jeder Abschnitt nicht nur, was zu prüfen ist, sondern auch, warum es für Ihr Unternehmen auf lange Sicht wichtig ist.

Vorbereitung der Code-Überprüfung

Zunächst müssen Sie sich immer bewusst sein, dass eine Überprüfung nur so gut ist wie die dahinterliegende Vorbereitung. Daher bestimmt die im Voraus aufgewendete Zeit für die Sammlung des Kontexts, ob dieser Prozess zu Erkenntnissen oder Verwirrung führt.

Umfang und Erfolgskriterien definieren:

  • Klären Sie, ob die Überprüfung die gesamte Anwendung, einen bestimmten Dienst oder ein Modul, einen Vorab-Kandidaten oder eine Legacy-Codebasis abdeckt, die an ein neues Team übergeben wird
  • Definieren Sie, was Erfolg für Sie bedeutet: eine priorisierte Liste von Sicherheitsfunden, eine Leistungsgrundlinie, Bereitschaft für ein Produktions-Deployment oder Due Diligence vor einer Akquisition
  • Identifizieren Sie bekannte Problembereiche im Team. Dies können langsame Endpunkte, wiederkehrende Ausnahmen in Protokollen, intermittierende Deployment-Fehler oder Funktionen sein, die niemand anfassen möchte

Umgebung und Toolchain verifizieren:

  • Stellen Sie sicher, dass die Lösung sowohl in der Debug- als auch in der Release-Konfiguration sauber kompiliert, ohne Fehler als Warnungen zu behandeln und ohne unterdrückte Warnungen ohne dokumentierte Begründung
  • Prüfen Sie, ob das Projekt eine unterstützte Version von .NET anvisiert. Die Support-Lifecycle-Seite von Microsoft ist die maßgebliche Referenz, die Sie verwenden sollten. Denken Sie daran, dass die Ausführung einer End-of-Life-Laufzeitumgebung in der Produktion ein Compliance- und Sicherheitsrisiko darstellt
  • Verifizieren Sie, dass „dotnet restore“ ohne Warnungen zu Schwachstellen abgeschlossen wird. Führen Sie „dotnet list package –vulnerable“ aus und dokumentieren Sie alle markierten NuGet-Pakete, bevor Sie mit der Überprüfung beginnen

Dokumentation und aktuelle Historie prüfen:

  • Einrichtung und Onboarding sollten gut genug dokumentiert sein, damit ein neuer Entwickler planmäßig arbeiten kann, ohne auf „tribal knowledge“ angewiesen zu sein
  • Überprüfen Sie die aktuelle Commit-Historie, um zu verstehen, was sich geändert hat. Dies ist wichtig, da eine letzte Woche committete Sicherheitskorrektur mehr Aufmerksamkeit verdient als stabiler Code, der seit einem Jahr unverändert ist
  • Prüfen Sie, ob offene Pull Requests gegen den Hauptzweig rebased sind, damit die Überprüfung den aktuellen Zustand des Codes widerspiegelt

Projektstruktur und Architektur

ASP.NET Core bietet Ihnen große Freiheit bei der Organisation eines Projekts. Diese Freiheit ist wertvoll, wenn sie bewusst eingesetzt wird, und kostspielig, wenn sie gar nicht genutzt wird. Beachten Sie daher die folgenden Punkte.

Trennung der Zuständigkeiten:

  • Stellen Sie sicher, dass die Anwendung eine konsistente Schichtenstruktur verfolgt: Controller oder Minimal-API-Endpunkte verarbeiten nur HTTP-bezogene Anliegen; die Geschäftslogik liegt in dedizierten Serviceklassen. Der Datenzugriff sollte klar von den HTTP-bezogenen Anliegen getrennt sein. Unabhängig davon, ob EF Core direkt in Diensten, Abfrageobjekten oder CQRS-ähnlichen Handlern verwendet wird, sollte der gewählte Ansatz konsistent über die Codebasis hinweg angewendet werden.
  • Controller sollten primär HTTP-bezogene Anliegen koordinieren und Geschäftslogik delegieren. Wenn Aktionen wesentliche Geschäftsregeln, Persistenzlogik oder Orchestrierung enthalten, sind die Zuständigkeiten möglicherweise falsch platziert.

Schlechte Praxis:

[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);
}

Gute Praxis:

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

Dependency Injection:

  • Stellen Sie sicher, dass alle Dienste im Dependency Injection-Container registriert sind und dass die Konstruktorinjektion konsistent verwendet wird, da die manuelle Auflösung von Diensten aus dem Container innerhalb von Methoden ein Zeichen dafür ist, dass die Architektur gegen ihr eigenes Design verstößt
  • Prüfen Sie, ob die Dienstlebensdauern angemessen sind: transient für leichte, zustandslose Operationen. Scoped für Dienste, die Zustände innerhalb einer Anfrage gemeinsam nutzen (z. B. DbContext), und singleton nur für Dienste, die wirklich threadsicher und zustandslos sind

Projekt- und Lösungsstruktur:

  • Bestätigen Sie, dass die Lösungsstruktur die Architekturgrenzen widerspiegelt. Ein Projekt namens MyApp.Core, das sowohl Domänenlogik als auch Datenbankmodelle enthält, hat ein Namensproblem, das normalerweise auf ein organisatorisches Problem darunter hindeutet
  • Prüfen Sie, ob zirkuläre Projektverweise nicht vorhanden sind. Sie sind in einigen Konfigurationen technisch möglich, deuten aber immer auf ein Abhängigkeitsdesign hin, das entwirrt werden muss

Middleware-Pipeline und Request-Verarbeitung

Das Rückgrat jeder ASP.NET Core-Anwendung ist seine Middleware-Pipeline. Daher wirkt sich die Reihenfolge, in der Middleware-Komponenten registriert werden, direkt auf Leistung und Verhalten sowie auf die Sicherheit aus. Ein menschlicher Körper kann mit einer kompromittierten Wirbelsäule nicht richtig funktionieren, und dasselbe gilt für eine ASP.NET-App. Wenn diese Reihenfolge falsch ist, kann dies heimlich die Authentifizierung brechen, Ausnahmen an Clients durchsickern lassen oder unnötige Verarbeitung für jede Anfrage hinzufügen.

Reihenfolge der Middleware:

  • Stellen Sie sicher, dass UseExceptionHandler oder UseDeveloperExceptionPage zuerst in der Pipeline registriert sind, da die Middleware für die Ausnahmebehandlung alles andere umschließen muss, um Fehler von nachgelagerten Komponenten abzufangen
  • Bestätigen Sie, dass Produktions-Pipelines der empfohlenen Middleware-Reihenfolge von Microsoft folgen: Ausnahmebehandlung frühzeitig, HSTS in Nicht-Entwicklungsumgebungen, HTTPS-Umleitung vor der Anforderungsverarbeitung, dann statische Dateien bei Bedarf, Routing, Authentifizierung und Autorisierung. Falsch geordnete Middleware kann zu fehlerhaftem Sicherheitsverhalten oder unnötiger Anforderungsverarbeitung führen
  • Prüfen Sie, ob die Authentifizierung (UseAuthentication) vor der Autorisierung (UseAuthorization) erfolgt. Wenn Sie die Reihenfolge umkehren, werden Autorisierungsentscheidungen getroffen, bevor die Identität des Benutzers festgestellt wurde, was zwangsläufig zu Problemen führen wird
  • Überprüfen Sie die gesamte Pipeline auf Middleware-Komponenten, die in der falschen Reihenfolge registriert oder mehrfach registriert wurden, da redundante Middleware Latenz ohne Nutzen hinzufügt

Anforderungsvalidierung:

  • Stellen Sie sicher, dass die Modellvalidierung konsistent durchgesetzt wird. In Controllern mit [ApiController] gibt ASP.NET Core automatisch Validierungsfehler für ungültige Modelle zurück. In MVC-Controllern, Razor Pages, Minimal APIs oder benutzerdefinierten Pipelines stellen Sie sicher, dass die Validierung über Filter, Endpunktfilter, FluentValidation oder explizite Prüfungen erfolgt, wo immer dies angebracht ist
  • Bestätigen Sie, dass die Größenbeschränkungen für Anfragen angemessen konfiguriert sind. Die Standardeinstellungen MultipartBodyLengthLimit und MaxRequestBodySize sind nicht immer für Produktions-Workloads geeignet und sollten explizit basierend auf den Anforderungen der Anwendung festgelegt werden

Antwortbehandlung:

  • Prüfen Sie, ob Antworten geeignete Cache-Control-Header für Inhalte enthalten, die von Browsern und CDNs zwischengespeichert werden sollen oder nicht
  • Stellen Sie sicher, dass die Antwortkomprimierung für textbasierte Inhaltstypen aktiviert ist. Brotli- und Gzip-Komprimierung reduzieren die Bandbreite für JSON-lastige APIs erheblich

Authentifizierung und Autorisierung

Sie sollten sich bewusst sein, dass Authentifizierungs- und Autorisierungsprobleme einen erheblichen Anteil an realen Sicherheitsvorfällen in Webanwendungen ausmachen. Um besser zu verstehen, wie wichtig dies ist, bedenken Sie, dass Microsoft im Oktober 2025 CVE-2025-55315 gepatcht hat, eine HTTP-Request-Smuggling-Schwachstelle im Kestrel-Server, die die höchste jemals für ein ASP.NET Core-Problem vergebene CVSS-Bewertung erhielt: 9,9 von 10.

Microsoft beschrieb CVE-2025-55315 als eine „HTTP-Request-Smuggling-Schwachstelle in Kestrel, die es einem Angreifer ermöglichen könnte, eine Anfrage in eine andere zu verstecken“. Vereinfacht ausgedrückt könnte dieses Problem je nach Deployment- und Anwendungsverhalten die Authentifizierungs- und Autorisierungsentscheidungen beeinflussen. Eine weitere Schwachstelle, die es schuf, war die Eröffnung eines Pfades für die Anforderungsmanipulation. Der Vorfall verdeutlicht, warum Authentifizierung und Autorisierung konsequent über Anforderungspfade und Deployment-Konfigurationen hinweg angewendet werden müssen. Denken Sie daran, dass Infrastrukturschwachstellen Schwächen bei der Zugriffskontrolle auf Anwendungsebene verstärken können.

Implementierung der Authentifizierung:

  • Stellen Sie sicher, dass die Anwendung ASP.NET Core Identity, Azure Active Directory oder eine etablierte OAuth 2.0 / OpenID Connect-Bibliothek verwendet und keine benutzerdefinierte Authentifizierungsimplementierung, da benutzerdefinierte Authentifizierungsschemata Risiken einführen, ohne über die bereits verfügbaren ausgereiften Lösungen hinaus nennenswerte Vorteile zu bieten
  • Prüfen Sie, ob die JWT-Konfiguration explizit die erlaubten Signaturalgorithmen angibt. Token-Validierungsparameter sollten Aussteller, Zielgruppe, Lebensdauer und Signaturschlüssel validieren. Wenn die Anwendung die erlaubten Signaturalgorithmen einschränkt, stellen Sie sicher, dass nur erwartete Algorithmen akzeptiert und unsignierte Tokens abgelehnt werden
// 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,
        };
    });
  • Bestätigen Sie, dass die Token-Aktualisierungslogik die Ablaufzeit ordnungsgemäß behandelt und dass abgelaufene Tokens aus dem Client-Speicher gelöscht werden. Ein Token, das vor sechs Monaten gültig war, darf keinen Zugriff mehr gewähren

Implementierung der Autorisierung:

  • Stellen Sie sicher, dass die Autorisierung auf Controller- oder Endpunkt-Ebene angewendet wird und nicht nur innerhalb von Aktionsmethoden-Körpern geprüft wird. Inline-Autorisierungsprüfungen sind leicht zu vergessen und unmöglich systematisch zu auditieren
  • Bestätigen Sie, dass rollenbasierte und policy-basierte Autorisierung konsistent verwendet werden und vermeiden Sie die Vermischung von Ad-hoc-User.IsInRole()-Prüfungen, die über die Codebasis verstreut sind, mit formalen Policy-Definitionen
  • Prüfen Sie auf unsichere direkte Objektverweise: Endpunkte, die eine Datensatz-ID in der URL akzeptieren, müssen überprüfen, ob der authentifizierte Benutzer die Berechtigung hat, auf diesen spezifischen Datensatz zuzugreifen, nicht nur, dass er angemeldet ist

Schwachstelle: Jeder authentifizierte Benutzer kann auf jede Rechnung zugreifen

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

Korrekt: Begrenzen Sie die Abfrage auf die Daten des authentifizierten Benutzers

[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);
}

Datenzugriff und Entity Framework Core

Die Datenbank ist die Quelle der meisten Leistungsprobleme in ASP.NET-Anwendungen. Die meisten Probleme in diesem Bereich ergeben sich aus langsamen Abfragen, nicht verfolgter Datenladung, N+1-Abfragen und synchronen Datenbankaufrufen auf Anforderungs-Threads. Diese Probleme sind nicht nur nachteilig für Ihr Produkt, sondern auch ziemlich teuer zu beheben. Daher ist die Identifizierung dieser Probleme einer der wertvollsten Vorteile einer Code-Überprüfung.

Effizienz von Abfragen:

  • Achten Sie auf N+1-Abfragemuster: Eine Abfrage, die eine Liste von Entitäten lädt und dann diese durchläuft, um verwandte Daten für jede einzelne zu laden, führt eine Abfrage aus, um N Datensätze und dann N zusätzliche Abfragen, um deren Beziehungen zu laden, was die Leistung mit wachsender Datenmenge erheblich verschlechtert.

Schlechte Praxis:

// 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
}

Gute Praxis:

// 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();
  • Stellen Sie sicher, dass Leseabfragen AsNoTracking() verwenden. Laut Microsofts EF Core-Dokumentation geben No-Tracking-Abfragen Ergebnisse effizienter zurück, da EF Core keinen Zustandsverfolgung für Objekte aufrechterhalten muss, die nie aktualisiert werden
  • Prüfen Sie, ob Projektionen Select() verwenden, um nur die Spalten abzurufen, die der Endpunkt tatsächlich benötigt, anstatt vollständige Entitätsgraphen zu laden und den Großteil der Daten zu verwerfen

Asynchroner Datenbankzugriff:

  • Stellen Sie sicher, dass alle EF Core-Aufrufe ihre asynchronen Varianten verwenden: ToListAsync(), FirstOrDefaultAsync(), SaveChangesAsync() usw. Microsofts ASP.NET Core Best Practices-Dokumentation besagt explizit, dass blockierende Aufrufe wie .Result und .Wait() bei asynchronen Datenbankoperationen den Thread-Pool unter Last auslasten können
  • Stellen Sie sicher, dass DbContext-Instanzen nicht zwischen Threads geteilt oder als Singletons gespeichert werden. DbContext ist nicht threadsicher, daher sollte seine Lebensdauer auf eine einzelne Anfrage beschränkt sein

Schema- und Migrations-Hygiene:

  • Bestätigen Sie, dass Datenbankmigrationen in die Versionskontrolle eingecheckt werden und dass die Migrationen-Historie linear ist, da Lücken oder Konflikte in der Migrationen-Historie zu Deployment-Fehlern führen
  • Prüfen Sie, ob Migrationen über einen kontrollierten Deployment-Prozess angewendet werden, wie z. B. überprüfte Migrationsskripte, CI/CD-Deployment-Schritte oder ein genehmigtes Release-Verfahren. Sie sollten sich nicht auf manuelle, von Entwicklern durchgeführte Migrationen oder nicht überprüfte automatische Produktionsänderungen verlassen. Manuelle Migrationen sind die Art von Dingen, die zum schlechtesten Zeitpunkt vergessen werden
  • Überprüfen Sie Indizes, um sicherzustellen, dass Spalten, die in WHERE-Klauseln, JOIN-Bedingungen und ORDER BY-Ausdrücken verwendet werden, über geeignete Indizes verfügen. Fehlende Indizes sind die häufigste Ursache für Abfragen, die in der Entwicklung gut laufen und in der Produktion versagen

API-Design und Vertrags-Hygiene

Eine ASP.NET Core API ist ein Vertrag zwischen dem Server und jedem Client, der von ihr abhängt. Daher kann das Brechen dieses Vertrags, selbst versehentlich, zu Fehlern führen, die während der Entwicklung oft unsichtbar sind. Diese Probleme könnten sich jedoch in der Produktion als äußerst schmerzhaft erweisen.

Versionierung:

  • Stellen Sie sicher, dass die API eine konsistente Versionierungsstrategie verwendet, sei es URL-Pfad-Versionierung (/api/v1/), Query-String-Versionierung oder Header-basierte Versionierung. Der spezifische Ansatz, den Sie wählen, ist weniger wichtig als einfach nur einen zu haben und ihn konsistent anzuwenden
  • Bestätigen Sie, dass veraltete API-Versionen klar gekennzeichnet sind und dass ein Migrationszeitplan den Verbrauchern mitgeteilt wird. Das Entfernen eines Endpunkts ohne Vorankündigung ist ein schneller Weg, Integrationen zu brechen

Eingabevalidierung und Antwortgestaltung:

  • Stellen Sie sicher, dass alle Anfrage-Modelle mit Data Annotation-Attributen oder Fluent Validation-Regeln annotiert sind und dass die Validierung global über einen Filter durchgesetzt wird, anstatt einzeln in jeder Aktion geprüft zu werden
  • Bestätigen Sie, dass API-Antworten ein konsistentes Umschlagformat verwenden; inkonsistente Antwortformen erfordern, dass Clients jeden Endpunkt unterschiedlich behandeln und machen die Fehlerbehandlung anfällig
  • Prüfen Sie, ob Fehlerantworten geeignete HTTP-Statuscodes und aussagekräftige Fehlermeldungen zurückgeben, da die Rückgabe eines 200 OK mit einem Fehlerfeld im Body ein Muster ist, das die HTTP-Semantik bricht und Clients verwirrt

OpenAPI und Dokumentation:

  • Stellen Sie sicher, dass Swagger oder ein kompatibles OpenAPI-Dokumentationstool konfiguriert ist und genaue, aktuelle Dokumentationen erstellt. Beachten Sie, dass veraltete API-Dokumentation fast schlimmer ist als keine Dokumentation, da sie Verbraucher aktiv irreführt
  • Bestätigen Sie, dass öffentliche API-Endpunkte und extern konsumierte Anfrage-/Antwortmodelle klar genug dokumentiert sind, damit die generierte OpenAPI-Dokumentation nützlich ist. Priorisieren Sie öffentliche Verträge, nicht offensichtliche Felder, Fehlerantworten, Authentifizierungsanforderungen und das Verhalten der Versionierung. Diese fließen direkt in die generierte Dokumentation ein und erfordern keine zusätzliche Anstrengung, sobald die Gewohnheit etabliert ist

Sicherheits-Best-Practices

Sie sollten unbedingt das OWASP DotNet Security Cheat Sheet überprüfen, das speziell für ASP.NET-Anwendungen gepflegt wird. Noch wichtiger ist jedoch, dass Sie es als Grundlage und nicht als Obergrenze für Ihre Sicherheitspraktiken betrachten müssen. Die folgenden Punkte ergeben sich aus den Problemen, die wir am häufigsten bei der Überprüfung von ASP.NET-Codebasen in der Produktion finden.

Schutz vor Injection:

  • Überprüfen Sie alle Datenbankabfragen auf Zeichenkettenverkettung; jede Abfrage, die SQL durch Anhängen von Benutzereingaben erstellt, ist anfällig für SQL-Injection, unabhängig davon, wie unwahrscheinlich der Exploit-Pfad erscheinen mag. Parametrisierte Abfragen und die LINQ-Schnittstelle von EF Core sind die richtigen Werkzeuge
  • Prüfen Sie auf Command-Injection-Risiken in Code, der vom Benutzer bereitgestellte Werte an Process.Start, Shell-Befehle oder dynamische Codeausführung übergibt

Cross-Site Scripting (XSS) und Cross-Site Request Forgery (CSRF):

  • Stellen Sie sicher, dass Razor Views durchweg die @-Kodierungssyntax verwenden und dass die Roh-HTML-Ausgabe über Html.Raw() nur dort verwendet wird, wo sie unbedingt notwendig ist und mit vollständig vertrauenswürdigem Inhalt
  • Verifizieren Sie für Cookie-authentifizierte Browser-Flows, dass der Schutz vor Angriffen für zustandsverändernde Formularübermittlungen und relevante unsichere HTTP-Methoden angewendet wird. Bewerten Sie für Bearer-Token-APIs das CSRF-Risiko separat. CSRF ist hauptsächlich ein Problem, wenn Browser automatisch Anmeldeinformationen anhängen, wie z. B. Cookies oder Basic Authentication

HTTP-Sicherheitsheader:

  • Bestätigen Sie, dass Sicherheitsheader über Middleware oder einen benutzerdefinierten Filter gesetzt werden: Content-Security-Policy, X-Content-Type-Options, X-Frame-Options, Referrer-Policy und Strict-Transport-Security sind die Grundlagen
  • Stellen Sie sicher, dass HTTPS in der Produktion erzwungen wird und dass UseHsts mit einem geeigneten max-age-Wert konfiguriert ist. HSTS weist Browser an, immer HTTPS für Ihre Domain zu verwenden, auch wenn der Benutzer manuell HTTP eingibt

Exposition sensibler Daten:

  • Stellen Sie sicher, dass Produktionsgeheimnisse, API-Schlüssel, Signaturschlüssel und privilegierte Verbindungspunkte nicht in die Versionskontrolle eingecheckt werden. Nicht sensible lokale Standardwerte können in Konfigurationsdateien vorhanden sein, aber echte Geheimnisse sollten aus Umgebungsvariablen, Geheimnissspeichern, verwalteten Identitäten oder Deployment-Zeitkonfigurationen stammen. Sie gehören in Umgebungsvariablen, Azure Key Vault oder ein anderes Geheimnisverwaltungssystem
  • Verifizieren Sie, dass sensible Daten nicht protokolliert werden; ein Protokolleintrag, der den vollständigen Anforderungskörper erfasst, kann versehentlich Passwörter, Kreditkartennummern oder persönliche Daten speichern, lange nachdem die Anfrage verarbeitet wurde
  • Stellen Sie sicher, dass Ausnahme-Details niemals in der Produktion an Clients gesendet werden. UseDeveloperExceptionPage von ASP.NET Core muss auf die Entwicklungsumgebung beschränkt sein

Async-Muster und Leistung

ASP.NET Core ist standardmäßig für hohe Nebenläufigkeit ausgelegt, was einer der Gründe für seine Popularität bei Unternehmenssystemen ist. Diese Nebenläufigkeit wird jedoch jedes Mal untergraben, wenn eine synchrone Operation einen Anforderungs-Thread blockiert. Ein einziger blockierender Aufruf in einem Hot-Code-Pfad kann den Durchsatz unter Last um eine Größenordnung reduzieren. Das ist etwas, auf das man immer achten muss.

Vermeidung synchroner Blockaden:

  • Suchen Sie nach .Result, .Wait() und .GetAwaiter().GetResult() auf Task. In ASP.NET Core blockieren diese Aufrufe Anforderungs-Threads und können zu Thread-Pool-Überlastung, schlechtem Durchsatz und Latenzspitzen unter Last beitragen. Es ist am besten, Async über den gesamten Aufrufstapel hinweg zu bevorzugen
  • Bestätigen Sie, dass Async void niemals in Controller-Aktionen oder Service-Methoden verwendet wird. Vermeiden Sie Async void außerhalb von Ereignishandlern. Async void-Methoden können nicht awaited werden, Ausnahmen sind schwer zu beobachten und korrekt zu behandeln, und Aufrufer wissen nicht, wann die Operation abgeschlossen ist

ThreadPool und IHttpClientFactory:

  • Stellen Sie sicher, dass HttpClient nicht direkt mit new HttpClient() in Methoden instanziiert wird. Bevorzugen Sie IHttpClientFactory für ausgehende HTTP-Aufrufe, insbesondere wenn Clients benannte/typisierte Konfiguration, Resilienzrichtlinien, Protokollierung oder kontrollierte Handler-Lebensdauern benötigen. Überprüfen Sie auch, ob der Code keine kurzlebigen HttpClient-Instanzen pro Anfrage erstellt, was unter Last Sockets erschöpfen kann
  • Prüfen Sie auf CPU-intensive Operationen, die auf Anforderungs-Threads laufen. Workloads wie Bildverarbeitung, PDF-Generierung oder große Datenexporte sollten an Hintergrunddienste oder Nachrichtenwarteschlangen ausgelagert werden, anstatt den HTTP-Anforderungs-Thread zu blockieren

Antwort-Caching und Output-Caching:

  • Identifizieren Sie Endpunkte, die wiederholt dieselben Daten liefern, und stellen Sie sicher, dass für sie Antwort- oder Output-Caching implementiert ist. Ein Endpunkt, der bei jeder Anfrage auf die Datenbank zugreift, um Daten zurückzugeben, die sich nur einmal pro Stunde ändern, verschwendet Ressourcen
  • Bestätigen Sie, dass eine Logik zur Cache-Invalidierung vorhanden und korrekt ist, da ein Cache, der unbegrenzt wächst oder nach einem Schreibvorgang veraltete Daten liefert, schwerer zu debuggen ist als kein Cache

Konfiguration und Geheimnisverwaltung

Die Systemkonfiguration ist die zugrunde liegende Ursache für die Lücke zwischen der Funktionsweise einer Anwendung in der Entwicklung und in der Produktion. Daher ist es unerlässlich, dass Sie sie während der Code-Überprüfung sorgfältig prüfen, um Probleme zu identifizieren, die zu verheerenden Datenlecks führen können.

Umgebungsspezifische Konfiguration:

  • Bestätigen Sie, dass alle umgebungsspezifischen Werte aus appsettings.{Environment}.json-Dateien oder Umgebungsvariablen stammen und nicht aus hartcodierten Werten im Quellcode. Eine Verbindungzeichenfolge, die auf eine lokale Datenbank verweist und in ein Produktions-Deployment gelangt, verursacht einen Ausfall
  • Stellen Sie sicher, dass die Anwendung die erforderliche Konfiguration beim Start validiert und mit einem aussagekräftigen Fehler lautstark fehlschlägt, wenn etwas fehlt. Eine teilweise konfigurierte Anwendung, die erfolgreich startet, aber beim ersten Gebrauch abstürzt, ist schwerer zu diagnostizieren als eine, die sich weigert zu starten
// Fail at startup, not at runtime
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
    ?? throw new InvalidOperationException(
        "Required configuration 'ConnectionStrings:DefaultConnection' is missing.");
  • Prüfen Sie, ob appsettings.Development.json-Dateien in .gitignore enthalten sind, wenn sie echte Anmeldeinformationen enthalten, selbst Entwicklungsvarianten. Entwickler-Anmeldeinformationen, die in die Versionskontrolle eingecheckt wurden, sind seit langem dafür bekannt, dass sie durch Kopieren und Einfügen oder Fehlkonfiguration in die Produktion gelangen. Lesen Sie, was dazu führen könnte, in einem unserer Artikel über Datenlecks

Geheimnisse in der Produktion:

  • Stellen Sie sicher, dass Produktionsgeheimnisse über Azure Key Vault, AWS Secrets Manager, Umgebungsvariablen, die zur Deployment-Zeit injiziert werden, oder einen äquivalenten Mechanismus verwaltet werden
  • Bestätigen Sie, dass die Anwendung verwaltete Identitäten oder Workload-Identitätsverbund verwendet, um auf Azure-Dienste zuzugreifen, anstatt Verbindungspunkte mit eingebetteten Anmeldeinformationen, da verwaltete Identitäten eine ganze Kategorie von Problemen bei der Geheimnisrotation und -leckage beseitigen

Tests und Codeabdeckung

Eine wichtige Sache, die man sich merken sollte, ist, dass es ohne Tests unmöglich ist zu überprüfen, ob die Codebasis stabil ist. Es besteht kein Zweifel daran, dass ungetestete Systeme Instabilitäten und Schwachstellen aufweisen, die im schlimmsten Moment auftreten würden.

Unit- und Integrationstests:

  • Stellen Sie sicher, dass kritische Geschäftslogik, Randfälle und Fehlerpfade durch automatisierte Tests abgedeckt sind
  • Bestätigen Sie, dass Tests Dependency Injection und Mocking (Moq, NSubstitute) verwenden, um den zu testenden Code von echten Datenbanken, externen APIs und Dateisystemen zu isolieren. Beachten Sie, dass Tests, die eine Live-Datenbank erfordern, Integrationstests sind und anders behandelt werden sollten als Unit-Tests
  • Prüfen Sie, ob WebApplicationFactory<T> für Integrationstests verwendet wird und nicht vollständige Deployment-Umgebungen hochgefahren werden. Es bietet einen leichten In-Process-Test-Host, der die gesamte Middleware-Pipeline ausführt, ohne einen bereitgestellten Server zu benötigen

Testqualität:

  • Überprüfen Sie Testnamen, um sicherzustellen, dass sie das Verhalten und nicht Methodennamen beschreiben: CreateOrder_WhenInventoryIsInsufficient_ShouldReturnBadRequest ist nützlicher als TestCreateOrder
  • Stellen Sie sicher, dass die CI-Pipeline fehlgeschlagene Tests blockiert und dass keine Tests mit [Ignore] oder Skip ohne Grund deaktiviert werden
  • Bestätigen Sie, dass kritische Pfade getestet werden und dass die Abdeckung verfolgt wird. Reine Prozentzahlen garantieren keine Qualität: Eine Reihe oberflächlicher Tests kann 80 % Abdeckung erreichen, während die tatsächlich wichtige Logik fehlt. Für kritische Systeme stellt die Erzwingung eines Mindestschwellenwerts ein nützliches Sicherheitsnetz dar, aber das Ziel ist eine sinnvolle Abdeckung der Geschäftslogik, Randfälle und Fehlerpfade

Protokollierung, Beobachtbarkeit und Fehlerbehandlung

Stellen Sie sich eine Situation vor: Sie erhalten um 2 Uhr morgens einen Anruf, weil etwas in der Produktion schiefgelaufen ist und das gesamte System abgestürzt ist. Der Grad Ihrer Panik in diesem Fall sollte von der Qualität Ihrer Protokolle abhängen. Die Informationen, die Sie daraus erhalten, machen oft den Unterschied zwischen einer Zehn-Minuten-Reparatur und einer Dreistunden-Untersuchung. Daher ist die Durchsetzung von Beobachtbarkeit und konsistenter Protokollierung eine unverzichtbare Grundlage für jedes Enterprise-Grade-System, unabhängig von der verwendeten Programmiersprache oder dem Framework.

Strukturierte Protokollierung:

  • Stellen Sie sicher, dass die Anwendung ILogger<T> aus der integrierten Microsoft.Extensions.Logging-Abstraktion verwendet und nicht statische Protokollierungsbibliotheken oder verstreute Console.WriteLine-Aufrufe. Die integrierte Abstraktion integriert sich über die Konfiguration und nicht über Codeänderungen mit Serilog, NLog und Azure Application Insights
  • Bestätigen Sie, dass Protokolleinträge strukturierte Eigenschaften anstelle von String-Interpolation verwenden; logger.LogInformation(“Verarbeite Bestellung {OrderId} für Benutzer {UserId}”, orderId, userId) erzeugt einen durchsuchbaren Protokolleintrag, während logger.LogInformation($”Verarbeite Bestellung {orderId} für Benutzer {userId}”) einen einfachen String erzeugt, der nicht abgefragt werden kann

Globale Ausnahmebehandlung:

  • Stellen Sie sicher, dass eine globale Middleware zur Ausnahmebehandlung konfiguriert ist und dass sie konsistente, bereinigte Fehlerantworten zurückgibt, da verschiedene Teile der Anwendung Ausnahmen nicht jeweils unterschiedlich behandeln sollten
  • Bestätigen Sie, dass unbehandelte Ausnahmen mit ausreichend sicherem Kontext protokolliert werden, um zu rekonstruieren, was passiert ist: Anforderungspfad, Korrelations-ID, authentifizierter Benutzeridentifikator, wo angebracht, und Stack-Trace. Vermeiden Sie die Protokollierung von Roh-Anforderungs-Bodies, Anmeldeinformationen, Tokens, Zahlungsdaten oder persönlichen Daten, es sei denn, sie sind explizit geschützt und begründet
  • Prüfen Sie, ob operative Fehler (Validierungsfehler, nicht gefundene Ressourcen, Verstöße gegen Geschäftsregeln) geeignete 4xx-Antworten zurückgeben und von unerwarteten Fehlern, die 5xx-Antworten zurückgeben, unterschieden werden. Jede Ausnahme als 500 zu behandeln, macht es unmöglich, Bugs von erwarteten Fehlerbedingungen zu trennen

Health Checks und Monitoring:

  • Stellen Sie sicher, dass Health Check-Endpunkte mithilfe der integrierten Health Checks von ASP.NET Core konfiguriert sind. Diese sollten überprüfen, ob die Anwendung eine Verbindung zu ihrer Datenbank, externen Abhängigkeiten und kritischer Infrastruktur herstellen kann
  • Bestätigen Sie, dass Application Insights, Datadog oder ein äquivalentes APM-Tool in der Produktion konfiguriert ist. Strukturierte Protokolle und Health Checks sind notwendig, aber verteiltes Tracing über Servicegrenzen hinweg erfordert spezielle Werkzeuge

Warum Sie Redwerk für die ASP.NET Code-Überprüfung vertrauen sollten

Die Stack Overflow Developer Survey bestätigte, dass ASP.NET Core weltweit zu den zehn am häufigsten verwendeten Web-Frameworks gehört, wobei 19,1 % der professionellen Entwickler damit arbeiten. Dies beweist, dass ASP.NET Core ein ausgereiftes, leistungsfähiges Framework ist und darauf basierende Anwendungen Millionen von Benutzern bedienen, in regulierten Branchen laufen und über ein Jahrzehnt oder länger in Produktion bleiben können.

Die obige Checkliste existiert jedoch, weil die Reife des Frameworks eine Codebasis nicht vor den Entscheidungen schützt, die während der Entwicklung getroffen werden. Dinge wie eine fehlende Authentifizierungsprüfung, ein blockierender Datenbankaufruf in einem Hot Path, Geheimnisse, die in die Versionskontrolle eingecheckt wurden, oder eine falsch zusammengestellte Middleware-Pipeline führen zu ernsthaften Problemen, die das gesamte System zum Einsturz bringen könnten.

Werfen wir einen Blick auf ein praktisches Beispiel, wie dieses System richtig gemacht aussieht: Current, eine E-Government-SaaS, die wir für Wohlfahrtsämter in den gesamten Vereinigten Staaten entwickelt haben. Die Plattform läuft auf .NET und Azure, verarbeitet sensible Bürgerdaten über mehrere US-Regierungsbehörden hinweg und erforderte von Anfang an 100 % ADA-Konformität.

Da wir für diese Umgebung entwickelten, mussten wir jeden Punkt dieser Checkliste korrekt umsetzen, bevor auch nur eine Zeile Code in die Produktion ging. Daher stellten Redwerk-Entwickler und -Tester sicher, dass die Autorisierung eng an die Daten jeder Behörde gebunden war und HTTPS durchgängig erzwungen wurde. Wir implementierten auch strukturierte Protokollierung, die Audit-Anforderungen erfüllte, und Health Checks, die es Betriebsteams ermöglichten, jede Abhängigkeit zu überprüfen, ohne die Anwendung zu berühren. Die Plattform wird jetzt von Wohlfahrtsabteilungen im ganzen Land genutzt.

Unser ASP.NET-Entwicklungsteam arbeitet seit 2005 mit dem Microsoft-Stack. Daher decken wir bei der Überprüfung einer Codebasis das Gesamtbild ab, einschließlich:

  • Architektur
  • Sicherheit
  • Datenzugriffsmuster
  • Async-Korrektheit
  • Konfigurations-Hygiene
  • Testabdeckung

Nach Abschluss der Überprüfung erhalten Sie einen Bericht, der spezifisch, nach Schweregrad priorisiert und so verfasst ist, dass sowohl Entscheidungsträger als auch Entwickler darauf reagieren können. Wenn Ihre ASP.NET-Anwendung eine genauere Betrachtung verdient, ist unser Code-Review-Service der richtige Ausgangspunkt. Erzählen Sie uns, was Sie gebaut haben und wir werden Ihnen sagen, was wir finden.

Erleben Sie selbst, wie eine Code-Überprüfung funktioniert: Über 80 identifizierte Verbesserungen und Sicherheitsrisiken für einen mobilen Marktplatz

Bitte geben Sie Ihre Geschäfts-E-Mail-Adresse ein ist keine Geschäfts-E-Mail