# ASP.NET Core / Blazor Security Audit Guide

> ASP.NET Core Framework Security Audit Module
> Applicable to: ASP.NET Core 6/7/8, Blazor Server/WebAssembly, Razor Pages, Minimal APIs

## Identification Patterns

```csharp
// ASP.NET Core project identification
<Project Sdk="Microsoft.NET.Sdk.Web">
using Microsoft.AspNetCore;

// File structure
├── Program.cs / Startup.cs
├── appsettings.json
├── Controllers/
├── Pages/ (Razor Pages)
├── Views/ (MVC)
├── Models/
├── Data/ (EF Core DbContext)
└── wwwroot/
```

---

## SQL Injection Detection

```csharp
// Dangerous: EF Core raw SQL concatenation
var users = db.Users
    .FromSqlRaw("SELECT * FROM Users WHERE Name = '" + name + "'");  // ❌ Critical

// Dangerous: Dapper string concatenation
connection.Query("SELECT * FROM Users WHERE Id = " + id);  // ❌ Critical
connection.Execute($"DELETE FROM Users WHERE Id = {id}");   // ❌ Critical

// Dangerous: ADO.NET raw concatenation
var cmd = new SqlCommand("SELECT * FROM Users WHERE Name = '" + input + "'", conn);  // ❌

// Dangerous: EF Core ExecuteSqlRaw concatenation
db.Database.ExecuteSqlRaw("UPDATE Users SET Name = '" + name + "' WHERE Id = " + id);  // ❌

// Audit regex
FromSqlRaw\s*\(.*\+|ExecuteSqlRaw\s*\(.*\+
connection\.(Query|Execute)\s*\(.*\+|SqlCommand\s*\(.*\+

// Safe: Parameterized queries
db.Users.FromSqlInterpolated($"SELECT * FROM Users WHERE Name = {name}");  // ✓
db.Database.ExecuteSqlInterpolated($"UPDATE Users SET Name = {name}");     // ✓

// Dapper parameterized
connection.Query("SELECT * FROM Users WHERE Id = @Id", new { Id = id });  // ✓

// ADO.NET parameterized
var cmd = new SqlCommand("SELECT * FROM Users WHERE Name = @Name", conn);
cmd.Parameters.AddWithValue("@Name", input);  // ✓
```

---

## XSS Detection (Razor Views)

```html
<!-- Dangerous: Html.Raw outputting user input -->
@Html.Raw(Model.UserComment)          <!-- ❌ Critical: Direct raw HTML output -->
@Html.Raw(ViewBag.Message)            <!-- ❌ ViewBag may contain user input -->

<!-- Dangerous: Unencoded output in JavaScript context -->
<script>var name = '@Model.Name';</script>  <!-- ❌ Can escape quotes -->

<!-- Audit regex -->
Html\.Raw\s*\(|@Html\.Raw|@\(.*Html\.Raw
<script>.*@Model\.|<script>.*@ViewBag\.

<!-- Safe: Razor default encoding -->
<p>@Model.UserComment</p>                  <!-- ✓ Auto HTML-encoded -->

<!-- Safe: JavaScript context -->
<script>var name = @Json.Serialize(Model.Name);</script>  <!-- ✓ JSON-encoded -->
```

---

## CSRF Configuration Detection

```csharp
// Dangerous: Globally disabled anti-forgery validation
services.AddControllersWithViews(options =>
{
    // AutoValidateAntiforgeryTokenAttribute not added
});

// Dangerous: Controller-level ignore
[IgnoreAntiforgeryToken]  // ❌ Skips CSRF validation
public IActionResult UpdateProfile(ProfileModel model) { ... }

// Dangerous: Razor Pages disabled
@attribute [IgnoreAntiforgeryToken]  // ❌

// Audit regex
\[IgnoreAntiforgeryToken\]|IgnoreAntiforgeryToken
AddControllersWithViews(?!.*AutoValidateAntiforgeryToken)

// Safe: Globally enabled
services.AddControllersWithViews(options =>
{
    options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());  // ✓
});

// Razor Pages enabled by default; APIs using JWT can disable CSRF but must confirm
```

---

## Authorization Configuration Flaws

```csharp
// Dangerous: [AllowAnonymous] overrides [Authorize]
[Authorize(Roles = "Admin")]
public class AdminController : Controller
{
    [AllowAnonymous]  // ❌ Critical: Overrides class-level Authorize
    public IActionResult SensitiveAction() { ... }
}

// Dangerous: Missing authorization attribute
public class PaymentController : Controller  // ❌ No [Authorize]
{
    public IActionResult ProcessPayment() { ... }
}

// Dangerous: Minimal API missing authorization
app.MapGet("/admin/users", GetAllUsers);  // ❌ No RequireAuthorization

// Audit regex
\[AllowAnonymous\]|AllowAnonymous
MapGet\(|MapPost\(|MapPut\(|MapDelete\(  // Check for RequireAuthorization

// Safe: Fallback policy (default deny)
builder.Services.AddAuthorization(options =>
{
    options.FallbackPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();  // ✓ Unannotated endpoints require authentication by default
});

// Minimal API authorization
app.MapGet("/admin/users", GetAllUsers).RequireAuthorization("AdminPolicy");  // ✓
```

---

## Unsafe Deserialization

```csharp
// Dangerous: BinaryFormatter (deprecated, but still in legacy code)
BinaryFormatter formatter = new BinaryFormatter();
object obj = formatter.Deserialize(stream);  // ❌ Critical: RCE

// Dangerous: Newtonsoft TypeNameHandling
var settings = new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.All  // ❌ Critical: Deserializes arbitrary types
};
JsonConvert.DeserializeObject(json, settings);

// Dangerous: TypeNameHandling.Auto/Objects is also exploitable
TypeNameHandling = TypeNameHandling.Auto    // ❌
TypeNameHandling = TypeNameHandling.Objects // ❌

// Audit regex
BinaryFormatter|ObjectStateFormatter|SoapFormatter|NetDataContractSerializer
TypeNameHandling\s*=\s*TypeNameHandling\.(All|Auto|Objects|Arrays)
LosFormatter|XmlSerializer\s*\(.*typeof\s*\(.*GetType

// Safe: Use System.Text.Json (safe by default)
var obj = JsonSerializer.Deserialize<MyModel>(json);  // ✓ No polymorphic type handling

// If Newtonsoft must be used, use SerializationBinder whitelist
var settings = new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.Auto,
    SerializationBinder = new KnownTypesBinder(allowedTypes)  // ✓
};
```

---

## Path Traversal (File Download)

```csharp
// Dangerous: Direct file path concatenation
[HttpGet("download")]
public IActionResult Download(string fileName)
{
    var path = Path.Combine(_uploadDir, fileName);
    return PhysicalFile(path, "application/octet-stream");  // ❌ ../../../etc/passwd
}

// Dangerous: Non-normalized path
var fullPath = _baseDir + "/" + userInput;  // ❌ Path traversal
System.IO.File.ReadAllText(fullPath);

// Audit regex
PhysicalFile\s*\(.*\+|Path\.Combine\s*\(.*fileName|File\.(Read|Open|Delete).*\+
SendFileAsync\s*\(.*\+

// Safe: Path validation
[HttpGet("download")]
public IActionResult Download(string fileName)
{
    if (fileName.Contains("..") || Path.IsPathRooted(fileName))
        return BadRequest();

    var path = Path.Combine(_uploadDir, fileName);
    var fullPath = Path.GetFullPath(path);

    if (!fullPath.StartsWith(Path.GetFullPath(_uploadDir)))  // ✓ Compare after normalization
        return BadRequest();

    return PhysicalFile(fullPath, "application/octet-stream");
}
```

---

## CORS Configuration Flaws

```csharp
// Dangerous: Allow all origins + credentials
builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowAll", policy =>
    {
        policy.AllowAnyOrigin()     // ❌
              .AllowAnyMethod()
              .AllowAnyHeader()
              .AllowCredentials();   // ❌ Conflicts with AllowAnyOrigin and dangerous
    });
});

// Dangerous: Dynamic Origin reflection in Program.cs/Startup.cs
app.UseCors(policy => policy
    .SetIsOriginAllowed(_ => true)   // ❌ Allows any origin
    .AllowCredentials());

// Audit regex
AllowAnyOrigin|SetIsOriginAllowed.*true|AllowCredentials

// Safe: Explicit whitelist
builder.Services.AddCors(options =>
{
    options.AddPolicy("Strict", policy =>
    {
        policy.WithOrigins("https://app.example.com")  // ✓
              .WithMethods("GET", "POST")
              .WithHeaders("Content-Type", "Authorization")
              .AllowCredentials();
    });
});
```

---

## Blazor Security Differences

```csharp
// Blazor Server: Server-side rendering, SignalR connection
// - Sensitive data is server-side, but SignalR messages may leak
// - Circuit hijacking: Ensure auth checks in every Hub call
// - Prevent JS Interop injection

// Dangerous: Direct database access without permission checks in Blazor Server
@code {
    private async Task LoadData()
    {
        data = await _dbContext.SensitiveData.ToListAsync();  // ❌ No user permission check
    }
}

// Dangerous: MarkupString is equivalent to Html.Raw
@((MarkupString)userInput)  // ❌ XSS

// Blazor WebAssembly: Client-side execution
// - All C# code runs in the browser, can be decompiled → Do not store secrets in WASM
// - API calls must be validated on the server → Do not rely solely on client-side authorization checks
// - Audit: Check for sensitive configurations in wwwroot/

// Audit regex
MarkupString\)|@\(\(MarkupString\)
NavigationManager\.NavigateTo\s*\(.*\+  // Open redirect
```

---

## Search Pattern Summary

```regex
# SQL Injection
FromSqlRaw\s*\(.*\+|ExecuteSqlRaw\s*\(.*\+|SqlCommand\s*\(.*\+
connection\.(Query|Execute)\s*\(.*\+

# XSS
Html\.Raw\s*\(|MarkupString\)

# CSRF
\[IgnoreAntiforgeryToken\]

# Authorization
\[AllowAnonymous\]
MapGet\(|MapPost\(  // Check Minimal API authorization

# Deserialization
BinaryFormatter|TypeNameHandling\.(All|Auto|Objects)

# Path Traversal
PhysicalFile\s*\(.*\+|Path\.Combine\s*\(.*fileName

# CORS
AllowAnyOrigin|SetIsOriginAllowed.*true

# Sensitive Configuration
"ConnectionStrings".*password|appsettings.*Development.*json
```

---

## Quick Audit Checklist

```markdown
[ ] Check ASP.NET Core version and known CVEs
[ ] Search for FromSqlRaw/ExecuteSqlRaw string concatenation
[ ] Search for Dapper Query/Execute concatenation
[ ] Search for Html.Raw / MarkupString (XSS)
[ ] Search for [IgnoreAntiforgeryToken] (CSRF bypass)
[ ] Search for [AllowAnonymous] on sensitive Controllers
[ ] Check FallbackPolicy authorization configuration
[ ] Search for BinaryFormatter / TypeNameHandling (deserialization)
[ ] Check file download endpoint path validation
[ ] Search for AllowAnyOrigin / SetIsOriginAllowed (CORS)
[ ] Check for hardcoded secrets/connection strings in appsettings.json
[ ] Check for MarkupString and JS Interop in Blazor
[ ] Verify Minimal API endpoints have RequireAuthorization
[ ] Check exception handling for stack trace leaks (app.UseDeveloperExceptionPage)
```

---

## Minimum PoC Examples
```bash
# SQL Injection (Dapper/Raw SQL)
curl "http://localhost:5000/api/users?name=admin'OR'1'='1"

# Path Traversal
curl "http://localhost:5000/download?fileName=../../../etc/passwd"

# Open Redirect
curl "http://localhost:5000/redirect?url=https://evil.com"
```

---

## Reference Resources

- [Microsoft ASP.NET Core Security Documentation](https://learn.microsoft.com/en-us/aspnet/core/security/)
- [OWASP .NET Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/DotNet_Security_Cheat_Sheet.html)
- [Blazor Security Considerations](https://learn.microsoft.com/en-us/aspnet/core/blazor/security/)
