Most production ASP.NET incidents on Windows hosts still start with something boring: an outdated ASP.NET Core Hosting Bundle, a skipped Windows cumulative update, or a SQL Server instance months behind its latest cumulative update. Fancy response headers will not save an app whose Kestrel/ANCM stack or database engine is known-vulnerable.
If you run ASP.NET Core or Framework apps on IIS with SQL Server on the back end, treat patch discipline as a first-class ops task—same priority as backups. Below is a practical cadence you can run on shared, reseller, or Windows VPS layouts without turning every deploy into an emergency.
#Know what actually needs patching
On a typical IIS + .NET site, four layers matter. Miss any one and you still have an open window.
- Windows Server OS and IIS (Servicing Stack + cumulative updates; IIS remains 10.0 on Server 2025).
- ASP.NET Core Module / .NET Hosting Bundle matching your app’s target framework (for new work, .NET 10 LTS).
- Your published app bits and NuGet dependencies (framework references, Identity, EF Core 10, YARP, etc.).
- SQL Server engine + client libraries (industry current is SQL Server 2025; many hosts still run 2022—patch whatever you actually connect to).
Shared hosting customers usually control the app and its packages; the host owns OS, Hosting Bundle, and SQL engine patching. On a Windows VPS you own all four. Write that split down so nobody assumes “someone else” applied last month’s runtime fix.
#Inventory versions before you change anything
Guessing versions from a desktop SDK install is how teams patch the wrong machine. Check the server that serves traffic.
# On the IIS box (elevated PowerShell)
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\ASP.NET Core\Shared Framework" -ErrorAction SilentlyContinue
# Hosting Bundle / ANCM presence
Get-WebGlobalModule | Where-Object { $_.Name -match 'AspNetCore' }
# Installed .NET runtimes (hosting machine)
& "$env:ProgramFiles\dotnet\dotnet.exe" --list-runtimes
# SQL Server version from a jump box or the DB host
# SELECT @@VERSION; -- run in SSMS or sqlcmd against the app database
Record target framework monikers from each site’s project or published runtimeconfig.json (.NET 8 vs 10 is a different Hosting Bundle train). For classic ASP.NET on full Framework, note the installed 4.8.x release and whether the app pool is integrated pipeline. Mismatched bitness (32-bit pool vs 64-bit runtime) still shows up after “successful” patches—confirm Enable 32-Bit Applications only when you mean it.
#A cadence that survives real calendars
Monthly is enough for most brochure and line-of-business sites; high-traffic or internet-facing APIs should review Microsoft’s .NET and SQL release notes weekly and accelerate when a remote-code or auth bypass lands in your stack. Use this loop:
- Week 1: Read .NET security/release notes and SQL CU notes; map CVEs or fix descriptions to packages you actually reference.
- Week 1–2: Patch non-prod identically to prod (same Hosting Bundle build, same SQL CU). Run smoke tests: login, a write path, a background job, file upload if you have one.
- Week 2: Prod maintenance window. Drain or recycle app pools after Hosting Bundle install; ANCM does not always pick up native module changes until w3wp recycles.
- Same day: Redeploy apps only when package updates require it; do not conflate runtime patching with a feature release.
- Afterward: Confirm HTTP 200 on a health endpoint, event log free of ANCM 500.0/502.5 storms, and SQL connectivity with the app’s least-privilege login.
Pin CI restore to package versions you have tested. Floating * or always-latest private feeds feel modern until a transitive package ships a breaking change the night before Black Friday. Prefer explicit versions, update deliberately, and keep the previous published artifact so you can roll forward the runtime while rolling back app bits if needed.
#App-level hygiene that multiplies OS patches
Runtime patches close holes in the host; your code still chooses weak defaults. Keep these on the same checklist as Windows Update:
// Program.cs — fail closed on cookie auth in production
builder.Services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = "__Host-app";
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Lax; // Strict if you can live without cross-site GET logins
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromHours(8);
});
// Never ship secrets in appsettings.json on the server
// Prefer environment variables or a file outside the site root, ACL'd to the app-pool identity
Rotate SQL passwords when people leave the team. Prefer Windows auth or a dedicated SQL login with db_datareader/db_datawriter (or narrower) on one database—not sysadmin “so restore works.” After SQL CUs, re-validate connection strings that force encryption and certificate trust; a CU should not be the first time you discover the app was quietly falling back to unencrypted TDS on a misconfigured box.
For multi-site IIS boxes, stagger pool recycles after a Hosting Bundle install so you do not cold-start every customer at once. Watch WAS and ASP.NET Core Module events under Applications and Services Logs. If you use Web Deploy from CI, keep the remote agent or handler patched on the same schedule as IIS—deployment endpoints are internet-adjacent more often than people admit.
#What “done” looks like each month
You are done when inventory matches reality: production `dotnet --list-runtimes` shows the Hosting Bundle build you intended, `@@VERSION` matches the CU you approved, app pools recycle cleanly, and your smoke checklist (auth, a write, a report query) passed on the same build you left running. Document the versions in the ticket—not in a wiki page nobody opens during an incident.
Practical takeaway: put a recurring half-day on the calendar labeled “runtime + SQL CU,” not “security theater.” Check Hosting Bundle and SQL versions on the box that serves traffic, patch non-prod first, recycle IIS after native module updates, and redeploy apps only when packages demand it. Headers and cookie flags still matter—but they sit on top of a stack that must be current. On Windows hosting, boring patch discipline is still the highest-leverage control you control.
Comments
No comments yet