Authentication & Authorization: Windows Auth, Request Filtering & IP Restrictions

IIS In Depth

Chapter 8 · Authentication & Authorization: Windows Auth, Request Filtering & IP Restrictions

Web Servers Fundamentals Chapter 8 named IP/Domain Restrictions and Basic/Windows Authentication, without real directive depth. This chapter goes deep on all three — and Windows Authentication in particular has two genuine operational gotchas (a Kerberos SPN requirement, and the "double-hop" problem) that catch real deployments often enough to be worth understanding before they show up as a confusing, intermittent failure in production.

Windows Authentication: Two Providers, Not One

<security> <authentication> <windowsAuthentication enabled="true"> <providers> <clear /> <add value="Negotiate" /> <add value="NTLM" /> </providers> </windowsAuthentication> </authentication> </security>

Negotiate attempts Kerberos first, falling back to NTLM only if Kerberos isn't available. NTLM is a genuinely older, weaker challenge-response protocol with no support for delegation (below) and no mutual authentication of the server to the client. Listing both providers, with Negotiate first, is the modern default — but the fallback to NTLM is silent and easy to miss, which is exactly what makes the SPN gotcha below so confusing when it happens.

This section lives under system.webServer/security/authentication — the same parent path as the windowsAuthentication element Chapter 3 used as its own overrideModeDefault="Deny" example. That's not a coincidence: authentication is exactly the kind of security-sensitive section that ships locked to applicationHost.config by default, and enabling Windows Authentication for a specific site or application generally requires either a machine-level change or a <location> override — not a change from that application's own web.config.

The SPN Gotcha: "It Works by IP but Fails by Hostname"

Kerberos requires a Service Principal Name (SPN) registered in Active Directory for the exact hostname the client is using to reach the site, bound to the account the Application Pool runs under:

setspn -A HTTP/www.example.com DOMAIN\svc-webapp

Without a matching SPN, Kerberos authentication for that specific hostname fails — and because Negotiate silently falls back to NTLM, the site keeps working, just under the weaker protocol, with no obvious error telling anyone Kerberos never actually succeeded. This produces a real, genuinely confusing pattern: the site "works" when accessed by the server's raw machine name or IP address (where a default SPN often already exists), but a custom public hostname added later — exactly the kind Chapter 5's virtual directories and Chapter 7's SNI bindings both deal with routinely — was never registered, so Kerberos quietly fails for it while NTLM masks the failure entirely.

The Double-Hop Problem

Windows Authentication happily identifies who's making the request to IIS itself — but by default, that identity cannot be forwarded a second hop, to a separate backend server the application then needs to call (a SQL Server on its own machine, a separate API server). NTLM has no delegation mechanism at all. Kerberos can support delegation, but only if explicitly configured — the Application Pool's identity account needs to be trusted for delegation in Active Directory, and the target service needs its own registered SPN as well. Without that explicit configuration, a call to the second server either fails authentication entirely or silently falls back to the Application Pool's own service identity rather than the original user's — a distinction that matters a great deal if the second server's own access control was meant to be based on the actual end user, not on the web application's service account.

Request Filtering: A Native Pre-Handler Defense Layer

<security> <requestFiltering> <requestLimits maxAllowedContentLength="30000000" maxUrl="4096" maxQueryString="2048" /> <fileExtensions> <add fileExtension=".config" allowed="false" /> </fileExtensions> <hiddenSegments> <add segment="bin" /> </hiddenSegments> </requestFiltering> </security>

Request Filtering is a native module (Chapter 4) hooking one of the earliest pipeline events, rejecting a request outright before any handler or authentication module ever runs against it. requestLimits caps request-body size, total URL length, and query-string length — a genuine defense against oversized-upload abuse and certain buffer-related exploits, independent of anything the application code itself does. fileExtensions denies requests for specific extensions outright — .config here, so a misconfigured static-file handler can never accidentally serve a web.config's own contents to a visitor. hiddenSegments denies any request whose path contains a named segment anywhere — bin here, so /app/bin/secret.dll is blocked regardless of what physically exists there, since compiled application binaries have no legitimate reason to ever be served as a direct HTTP response.

IP and Domain Restrictions: Evaluation Order Matters

<security> <ipSecurity allowUnlisted="false"> <add ipAddress="203.0.113.0" subnetMask="255.255.255.0" allowed="true" /> <add ipAddress="203.0.113.50" allowed="false" /> </ipSecurity> </security>

allowUnlisted sets the default action for any client IP that matches none of the explicit entries — false here means deny-by-default, an allowlist model. Individual entries are evaluated most-specific-match-wins, not strictly top-to-bottom: a single-IP rule (203.0.113.50, denied) takes precedence over a broader subnet rule that also happens to contain it (203.0.113.0/24, allowed) — letting one specific address be carved out as an exception to an otherwise-allowed range, or vice versa, without needing the entries listed in any particular order.

Where this points forward in this course
Chapter 9's Failed Request Tracing can capture exactly which module rejected a request and why — including a Request Filtering denial or an authentication failure — turning "the request just got a 403/401" into a concrete, attributable cause rather than a guess.
"It works, just slower" can mean Kerberos silently failed
A Windows Authentication setup that appears to work but "feels wrong" — extra prompts, or authentication that works locally but not from certain client machines — is worth checking for a missing or mismatched SPN before assuming a broader configuration problem. Negotiate's silent fallback to NTLM means Kerberos can be failing constantly with no visible error at all, simply working around it every time via the weaker protocol.

Hands-On Exercises

Exercise 1

A site using Windows Authentication with the Negotiate provider works fine when accessed via the server's raw machine name, but authentication behaves oddly when accessed via a new public hostname added last week. Using this chapter's own material, explain the most likely cause and the command that would confirm/fix it.

📄 View solution
Exercise 2

An ASP.NET application authenticates users with Windows Authentication, then tries to run a query against a separate SQL Server machine using the authenticated user's own Windows identity, expecting the database to enforce per-user permissions. This consistently fails. Explain what's happening, using this chapter's own terminology.

📄 View solution
Exercise 3

An ipSecurity section sets allowUnlisted="false", allows the subnet 203.0.113.0/24, and separately denies the single address 203.0.113.50, which falls inside that subnet. Will a request from 203.0.113.50 be allowed or denied? Explain using the actual evaluation rule.

📄 View solution

Chapter 8 Quick Reference

  • Negotiate tries Kerberos first, silently falls back to NTLM — the fallback is invisible unless specifically checked for
  • SPN gotcha: Kerberos needs a registered SPN for the exact hostname used; missing it fails Kerberos silently (masked by the NTLM fallback)
  • Double-hop problem: a user's Windows identity doesn't forward to a second backend server by default — NTLM never supports it; Kerberos needs explicit delegation configuration
  • Request Filtering — a native module rejecting oversized/malformed/forbidden requests before any handler or auth module runs (size limits, file extensions, hidden path segments)
  • IP/Domain RestrictionsallowUnlisted sets the default for unmatched IPs; specific entries win over broader ones regardless of listed order
  • Authentication is a security-sensitive section, typically Deny-locked at the machine level (Chapter 3) — expect a <location> override or machine-level change, not a per-application web.config edit