AnointedAutomation.APIMiddlewares
1.0.9
dotnet add package AnointedAutomation.APIMiddlewares --version 1.0.9
NuGet\Install-Package AnointedAutomation.APIMiddlewares -Version 1.0.9
<PackageReference Include="AnointedAutomation.APIMiddlewares" Version="1.0.9" />
<PackageVersion Include="AnointedAutomation.APIMiddlewares" Version="1.0.9" />
<PackageReference Include="AnointedAutomation.APIMiddlewares" />
paket add AnointedAutomation.APIMiddlewares --version 1.0.9
#r "nuget: AnointedAutomation.APIMiddlewares, 1.0.9"
#:package AnointedAutomation.APIMiddlewares@1.0.9
#addin nuget:?package=AnointedAutomation.APIMiddlewares&version=1.0.9
#tool nuget:?package=AnointedAutomation.APIMiddlewares&version=1.0.9
AnointedAutomation.APIMiddlewares
Jesus is King ✝️
ASP.NET Core middleware and filters for protecting small APIs: API key authentication, an in memory IP blacklist, automatic banning of clients that probe for endpoints that do not exist, and optional garbage collection when the API has been idle.
Installation
dotnet add package AnointedAutomation.APIMiddlewares
- Target framework:
net10.0 - Framework reference:
Microsoft.AspNetCore.App(ASP.NET Core shared framework) - Dependencies:
Quick start
using System;
using AnointedAutomation.APIMiddleware;
using Microsoft.AspNetCore.Builder;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
WebApplication app = builder.Build();
// Reject blacklisted IPs with 403 before anything else runs.
app.UseMiddleware<IPBlacklistMiddleware>();
// Count 404/401/403 responses per IP and ban repeat offenders.
app.UseMiddleware<InvalidEndpointTrackerMiddleware>();
// Require an API key header on everything under /api (constant-time comparison).
app.UseMiddleware<ApiKeyAuthMiddleware>(
"X-Api-Key", // header name
Environment.GetEnvironmentVariable("MY_API_KEY"), // expected key
"/api"); // protected path prefix
// Track endpoint hits; run a GC when every endpoint has been idle for 10 minutes.
app.UseMiddleware<EndpointAccessMiddleware>((TimeSpan?)TimeSpan.FromMinutes(10), true);
app.MapControllers();
app.Run();
Note the namespace: the code lives in AnointedAutomation.APIMiddleware (singular), even though the package id is AnointedAutomation.APIMiddlewares.
API overview
Middleware (AnointedAutomation.APIMiddleware)
| Type | What it does |
|---|---|
ApiKeyAuthMiddleware |
Constructor (RequestDelegate next, string headerName, string expectedKey, string protectedPathPrefix). Requests whose path starts with the prefix must send the header with the exact key or get 401. Uses CryptographicOperations.FixedTimeEquals. Also exposes static bool IsAuthorized(string presentedKey, byte[] expectedKey). The prefix must start with /. |
IPBlacklistMiddleware |
Returns 403 for any client IP in IPBlacklist. Also owns the shared middleware log buffer: static AddLog(LogMessage), static GetLogs(), static ClearLogs(), and the static events LogAdded and LogCleared. |
InvalidEndpointTrackerMiddleware |
Returns 403 for blacklisted IPs. After the rest of the pipeline runs, records a failed attempt for a 404 with an empty body (except /), a 401 or a 403. An IP is banned after 10 failed attempts, or immediately if it requests a path ending in .env. Attempt counts and logs reset every 24 hours. static ClearFailedAttempts() resets counts. |
AttemptInfo |
Attempt record for one IP: int Count, HashSet<string> Paths. |
EndpointAccessMiddleware |
Constructor (RequestDelegate next, TimeSpan? timeout, bool cleanMem = false). Records the last access time per path. When cleanMem is true, a one minute timer runs AnointedAutomation.Optimization.Memory.GarbageCollection.PerformGarbageCollection once every endpoint has been idle longer than timeout. static bool HasBeenHitRecently(string path) reports whether a path was hit in the last 5 minutes. |
Filters (AnointedAutomation.APIMiddleware.Filters)
| Type | What it does |
|---|---|
APIKeyAttribute |
[APIKey] action filter for controllers or actions. Reads the header name from the API_KEY_NAME environment variable and the expected key from API_KEY; a missing or wrong key returns 401 and logs a warning with the client IP. |
Objects (AnointedAutomation.APIMiddleware.Objects)
| Type | What it does |
|---|---|
IPBlacklist |
Static in memory blacklist: AddBannedIP(string ip, string reason), RemoveBannedIP(string ip), IsIPBlocked(string ipAddress), GetBlockReason(string ipAddress), ClearBlacklist(), and the events IPBanned (EventHandler<BannedIP>) and IPUnbanned (EventHandler<string>). |
BannedIP |
_id, Ipv4, Ipv6, Reason. Handy as a persistence shape for bans. |
Utility (AnointedAutomation.APIMiddleware.Utility)
| Type | What it does |
|---|---|
APIUtility |
GetClientPublicIPAddress(HttpContext) and GetClientPublicIPAddress(ActionExecutingContext). Uses the first X-Forwarded-For value, then RemoteIpAddress. Returns 198.51.100.255 for ::1 or when no IP is available, and logs why. |
Using the [APIKey] filter
using AnointedAutomation.APIMiddleware.Filters;
using Microsoft.AspNetCore.Mvc;
[APIKey]
[ApiController]
[Route("[controller]")]
public class SecureController : ControllerBase
{
[HttpGet]
public IActionResult Get() => Ok("Secure data");
}
Required environment variables:
API_KEY_NAME=X-Api-Key
API_KEY=your-secret-key
ApiKeyAuthMiddleware is the newer option: it takes its values as arguments instead of reading environment variables, and compares keys in constant time.
Persisting and observing bans
The blacklist is in memory and is lost on restart. Use the events to persist bans and reload them at startup:
using System;
using AnointedAutomation.APIMiddleware;
using AnointedAutomation.APIMiddleware.Objects;
using AnointedAutomation.Optimization.Logging;
IPBlacklist.IPBanned += (sender, banned) =>
Console.WriteLine($"Banned {banned.Ipv4}: {banned.Reason}");
IPBlacklist.AddBannedIP("203.0.113.7", "Loaded from database");
IPBlacklistMiddleware.LogAdded += (sender, e) =>
Console.WriteLine($"[{e.log.messageType}] {e.log.message}");
IPBlacklistMiddleware.AddLog(LogMessage.Warning("Suspicious activity detected."));
Note that IPBlacklistMiddleware.ClearLogs() and IPBlacklist.ClearBlacklist() also remove every event subscriber, and InvalidEndpointTrackerMiddleware calls ClearLogs() during its 24 hour reset, so resubscribe to IPBlacklistMiddleware.LogAdded if you rely on it long term.
Notes
- The client IP comes from
X-Forwarded-Forwhen present. Only trust it behind a reverse proxy that sets it. - All state (blacklist, attempt counts, logs) is static and per process.
Related packages
- AnointedAutomation.Logging: the
LogMessagetype used for the middleware log buffer. - AnointedAutomation.Memory: the garbage collection helper used by
EndpointAccessMiddleware. - AnointedAutomation.Objects.API: ASP.NET Core helpers such as the shared JSON casing convention.
License
MIT. See LICENSE.
Support This Project
This library is free and open source. The best way to support the work is to shop with us:
- Christian items: https://store.anointed.company
- Pets: https://www.animaldander.com
- Everything else: https://www.mart.club
Found a bug or have a request? Open an issue at https://github.com/AnointedAutomation/AnointedAutomation/issues.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. 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. |
-
net10.0
- AnointedAutomation.Logging (>= 1.0.6)
- AnointedAutomation.Memory (>= 1.0.7)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.