适用环境:Windows Server 2016 / 2019 / 2022 / 2025(IIS 10),部分内容适用于 Windows 10 / 11 自带 IIS。
1. IIS 的架构:从 HTTP.sys 到 w3wp
1.1 一条请求的旅程
text
客户端
│
▼
HTTP.sys(内核模式)
├─ 监听端口 / URL 注册
├─ 内核响应缓存
├─ 请求队列
└─ 传递给用户态
│
▼
WAS(Windows Process Activation Service)
├─ 读取配置
├─ 管理应用程序池
└─ 启动 / 回收 worker
│
▼
w3wp.exe(每个应用程序池一个 worker)
├─ 加载 IIS 模块
├─ 托管 ASP.NET / ASP.NET Core / ISAPI / FastCGI
└─ 执行请求管道
│
▼
应用程序代码、文件系统、数据库、后端服务
理解这张图,很多现象就顺了:
- HTTP.sys 在内核态,它能在 worker 不工作时仍然返回缓存内容,所以"首页 200"不代表应用正常;
- WAS 负责进程管理,应用池状态、快速失败保护、空闲超时都发生在这里;
- w3wp 是真正执行请求的进程,它的令牌就是应用池身份;
- 请求管道里的
AuthenticateRequest、AuthorizeRequest等阶段,决定了身份和权限在哪一步生效。
1.2 核心组件与职责
| 组件 | 位置 | 主要职责 |
|---|---|---|
| HTTP.sys | 内核 | 监听、URL 注册、缓存、请求排队、TLS 终止(部分场景) |
| WAS | 用户态服务 | 配置读取、应用池管理、worker 启动/回收 |
| W3SVC | 用户态服务 | WWW 服务,依赖 WAS,提供 HTTP 特定功能 |
| w3wp.exe | 用户态进程 | 执行请求、加载模块、运行应用 |
| IIS Admin Service / 管理 API | 用户态 | 配置管理、IIS Manager、appcmd、PowerShell |
| Windows 进程激活 | 用户态 | 非 HTTP 协议(net.tcp、net.pipe、net.msmq)的激活 |
经验:IIS 问题先分层。先确认请求有没有到 worker,再确认 worker 用什么身份,最后才看应用代码。
1.3 请求管道
IIS 7 以后是集成管道(Integrated Pipeline),ASP.NET 模块和 IIS 原生模块共享同一个请求生命周期。关键阶段:
text
BeginRequest
AuthenticateRequest ← 认证模块在这里工作
AuthorizeRequest ← 授权模块在这里工作
ResolveRequestCache ← 输出缓存 / 内核缓存
MapRequestHandler ← 选择 handler
AcquireRequestState ← 会话状态
PreExecuteRequestHandler
ExecuteRequestHandler ← 真正执行页面 / 静态文件 / 代理
ReleaseRequestState
UpdateRequestCache
LogRequest
EndRequest
错误页里的"通知(Notification)"字段会告诉你失败发生在哪个阶段。例如:
AuthenticateRequest失败:认证/模拟/身份问题;AuthorizeRequest失败:URL 授权或 ACL;ExecuteRequestHandler失败:应用代码、模块、handler;ResolveRequestCache之后:缓存层。
1.4 HTTP.sys 内核缓存:一个容易被骗的地方
HTTP.sys 会缓存静态响应,后续请求可能根本不进 w3wp。于是会出现:
- 站点应用已经 500,但首页仍然 200;
- 改了配置、重启了应用池,某个 URL 仍然返回旧内容;
- 用浏览器刷新也没用,因为缓存键没变。
排障时养成习惯:
text
GET /?cb=20261101
GET /app/page.aspx?cb=20261101
加一个随机查询串,绕开缓存。也可以用:
cmd
netsh http show cachestate
查看内核缓存状态。重启 IIS 或应用池不一定能清掉 HTTP.sys 缓存;必要时用 IIS 管理器/netsh 清缓存,或者直接换 URL。
2. 安装、功能选择与管理入口
2.1 安装方式
Windows Server 上常用的三种方式:
powershell
# Server Manager(图形)
# 添加角色和功能 → Web Server (IIS)
# PowerShell
Install-WindowsFeature Web-Server, Web-Static-Content, Web-Default-Doc,
Web-Dir-Browsing, Web-Http-Errors, Web-Http-Logging, Web-Stat-Compression,
Web-Filtering, Web-Net-Ext45, Web-Asp-Net45, Web-ISAPI-Ext, Web-ISAPI-Filter,
Web-Scripting-Tools
# Server Core / 无人值守
# 使用 Install-WindowsFeature 或 DISM
按需安装,不要"全选"。功能越多,攻击面越大:
- 静态站点:
Web-Server+Web-Static-Content+Web-Default-Doc+Web-Http-Errors+Web-Http-Logging; - ASP.NET Framework:
Web-Net-Ext45+Web-Asp-Net45+Web-ISAPI-Ext+Web-ISAPI-Filter; - 管理:
Web-Mgmt-Console、Web-Scripting-Tools; - 反向代理 / URL 重写:URL Rewrite、ARR(需单独安装);
- ASP.NET Core:安装 .NET Hosting Bundle,IIS 侧只需要基础的
Web-Server+Web-WebSockets(如需)。
2.2 安装后的第一件事
- 确认服务状态:
W3SVC、WAS、W3LOGGING(如启用); - 确认默认站点绑定和物理路径;
- 确认日志目录
%SystemDrive%\inetpub\logs\LogFiles; - 删除或替换默认页;
- 按最小权限配置应用池和目录 ACL;
- 关闭不需要的功能(目录浏览、WebDAV、详细错误、旧协议)。
2.3 管理入口
| 入口 | 适用场景 | 特点 |
|---|---|---|
| IIS Manager | 单机、可视化 | 直观,但不利于自动化和审计 |
appcmd.exe |
命令行、脚本 | 快速、适合排障和批处理 |
PowerShell WebAdministration |
自动化、批量 | 对象化、可管道、可远程 |
Microsoft.Web.Administration |
.NET 程序 | 编程方式读写配置 |
| WMI / CIM | 监控、自动化 | 适合采集状态 |
| IIS Administration API(REST) | 远程管理 | 需要额外安装,注意安全 |
命令行工具位置:
text
%windir%\System32\inetsrv\appcmd.exe
3. 配置体系:applicationHost.config、web.config 与继承
3.1 配置文件层级
text
machine.config ← .NET 全局配置(ASP.NET 相关)
applicationHost.config ← IIS 全局配置(站点、应用池、绑定、模块)
administration.config ← 管理工具相关配置
web.config ← 站点/应用级配置
applicationHost.config 默认位于:
text
C:\Windows\System32\inetsrv\config\applicationHost.config
web.config 可以出现在站点根目录、应用目录、子目录,IIS 会按目录层级合并配置。
3.2 继承、location 与锁定
IIS 的配置是继承的:父级配置会向下传递,子级可以覆盖(如果允许)。
xml
<location path="Default Web Site/releases">
<system.webServer>
<security>
<authentication>
<anonymousAuthentication enabled="true" userName="" />
</authentication>
</security>
</system.webServer>
</location>
overrideModeDefault 决定某个配置节能否在 web.config 中被覆盖:
Allow:允许子级覆盖;Deny:子级不能覆盖,尝试覆盖会得到500.19 / 0x80070021。
查看/修改锁定:
cmd
appcmd list config /section:system.webServer/security/authentication/anonymousAuthentication
appcmd unlock config /section:system.webServer/security/authentication/anonymousAuthentication
经验:
500.19很多时候不是"配置写错了",而是"这个节被锁了"。先看错误页的 Config Error / Config File 字段,再决定是解锁还是改父级配置。
3.3 appcmd 与 PowerShell 示例
cmd
:: 列出站点、应用、应用池
appcmd list sites
appcmd list apps
appcmd list apppools
:: 查看应用池完整配置(含身份、登录类型、回收设置)
appcmd list apppool "MyPool" /text:*
:: 查看某个应用的有效配置
appcmd list config "Default Web Site/app" /section:system.webServer/security/authentication/anonymousAuthentication
:: 设置匿名认证使用应用池身份
appcmd set config "Default Web Site/app" /section:system.webServer/security/authentication/anonymousAuthentication /userName: /commit:apphost
PowerShell 等价写法:
powershell
Import-Module WebAdministration
Get-Website
Get-WebApplication
Get-WebAppPoolState "MyPool"
Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Location 'Default Web Site/app' `
-Filter 'system.webServer/security/authentication/anonymousAuthentication' `
-Name userName -Value ''
3.4 备份、恢复与配置历史
改配置前先备份:
cmd
appcmd add backup "before-change-20261101"
appcmd list backups
appcmd restore backup "before-change-20261101"
IIS 10 还提供配置历史(默认位于 %SystemDrive%\inetpub\history),可以在 IIS Manager 的"配置编辑器"里查看/恢复,或者直接读取历史文件。生产环境建议:
- 变更前
appcmd add backup; - 用版本控制管理
applicationHost.config和站点的web.config; - 变更后做一次
appcmd list config对比; - 把变更脚本和回滚脚本放在一起。
4. 站点、应用、虚拟目录与绑定
4.1 三者的关系
text
Site(站点)
├─ Binding:协议 + IP + 端口 + 主机头
├─ Root Application(根应用)
│ └─ Virtual Directory → 物理路径
└─ Application(子应用,如 /releases)
├─ 独立的应用程序池(可选)
└─ Virtual Directory → 物理路径
- 站点是监听端点和根路径的组合;
- 应用是站点内的一个 URL 路径,可以绑定独立的应用池;
- 虚拟目录把 URL 路径映射到物理路径,可以指向站点目录之外。
应用池的隔离边界在"应用"这一层:不同应用可以共享池,也可以各自独立。安全上建议按信任级别隔离。
4.2 绑定与 SNI
绑定字符串格式:
text
协议/IP:端口:主机头
示例:
text
http/*:80:
https/*:443:www.example.com
同一台服务器上多个 HTTPS 站点,需要 SNI 或者不同的 IP/端口。SNI 下每个站点可以绑定不同证书:
cmd
netsh http show sslcert
netsh http add sslcert hostnameport=www.example.com:443 certhash=<thumbprint> appid={<guid>} certstorename=MY
常见问题:
- 主机头不匹配 → 请求落到默认站点;
- 证书绑定错误 → TLS 握手失败或返回错误站点;
- 端口冲突 → 站点启动失败;
- URL ACL 缺失 → HTTP.sys 拒绝注册(
netsh http show urlacl)。
4.3 默认文档、MIME 与目录浏览
xml
<system.webServer>
<defaultDocument enabled="true">
<files>
<clear />
<add value="Default.aspx" />
<add value="index.html" />
</files>
</defaultDocument>
<staticContent>
<remove fileExtension=".nupkg" />
<mimeMap fileExtension=".nupkg" mimeType="application/zip" />
</staticContent>
<directoryBrowse enabled="false" />
</system.webServer>
defaultDocument决定目录请求返回哪个文件;staticContent决定 IIS 是否把某扩展名当静态文件返回;directoryBrowse默认关闭,生产环境不要随便打开;- 自定义 MIME 时,先
remove再add,避免重复定义导致 500.19。
5. 应用程序池:IIS 的心脏
5.1 应用池是什么
应用池是 IIS 的隔离单元:
- 一个应用池对应一个
w3wp.exe(Web garden 除外); - 池内所有应用共享进程、令牌、内存和配置;
- 池崩溃/回收会影响池内所有应用;
- 池的身份决定进程令牌。
5.2 身份类型
| 身份类型 | 说明 | 适用场景 |
|---|---|---|
ApplicationPoolIdentity |
虚拟账户 IIS APPPOOL\<池名> |
默认推荐,低权限、自动管理 |
NetworkService |
内置网络服务账户 | 兼容旧应用,权限比虚拟账户大 |
LocalSystem |
本地系统账户 | 不推荐用于 Web |
LocalService |
本地服务账户 | 较少使用 |
SpecificUser |
指定的域/本地账户 | 需要访问域资源、SQL、文件共享时 |
自定义账户的关键配置:
cmd
appcmd set apppool "MyPool" /processModel.identityType:SpecificUser
appcmd set apppool "MyPool" "/processModel.userName:DOMAIN\svc_web" "/processModel.password:<password>"
appcmd set apppool "MyPool" /processModel.loadUserProfile:true
同时要给账户授予:
SeBatchLogonRight(LogonBatch登录必需);- 视登录类型可能还需要
SeServiceLogonRight; - 加入
IIS_IUSRS(如果 ACL 依赖该组); - 站点目录的最小读权限;
- 不要给交互登录、RDP、本地管理员。
5.3 LogonBatch 与 LogonService
processModel.logonType 影响令牌的登录类型和组:
LogonBatch:默认,令牌带S-1-5-3 BATCH;LogonService:服务方式登录,令牌可能带S-1-5-6 SERVICE;LogonInteractive、LogonNetwork、ClearText:特殊场景。
实测提醒:
ApplicationPoolIdentity是虚拟账户,即使配置里显示LogonBatch,worker 令牌仍可能带SERVICESID。这是虚拟账户的登录路径决定的。自定义域用户走LogonBatch时,令牌是BATCH,不带SERVICE。
为什么这很重要?因为 Windows 把 SeImpersonatePrivilege 强制分配给 SERVICE,带 SERVICE 的令牌会继承这个敏感特权。
5.4 回收、空闲与启动
xml
<processModel
identityType="SpecificUser"
userName="DOMAIN\svc_web"
loadUserProfile="true"
idleTimeout="00:20:00"
startMode="OnDemand" />
<recycling>
<periodicRestart time="00:00:00">
<schedule>
<clear />
<add value="02:00:00" />
</schedule>
</periodicRestart>
</recycling>
关键设置:
idleTimeout:空闲多久回收 worker;periodicRestart:定时回收;rapidFailProtection:短时间内多次崩溃会停止池,表现为 503;startMode=AlwaysRunning+preloadEnabled:预热,适合高并发;disallowOverlappingRotation:回收时是否允许新旧 worker 并存。
5.5 32/64 位、CLR 与管道
enable32BitAppOnWin64:32 位应用池,注意本机是否装了 32 位组件;managedRuntimeVersion:v4.0或空(无托管代码);managedPipelineMode:Integrated(推荐)或Classic(旧应用兼容);- 同一站点下不同应用可以使用不同应用池和不同 CLR 版本。
5.6 队列、CPU 与内存
queueLength:请求队列长度,满了返回 503;cpu.limit:CPU 限制,按百分比;memory/privateMemory:回收阈值;maxProcesses:Web garden,进程数 > 1。除非应用完全无状态且使用外部会话,否则不建议。
5.7 监控与常见问题
cmd
appcmd list apppools
appcmd list wp
appcmd list requests
- 503 Service Unavailable:池停止、快速失败、队列满、应用初始化失败;
- 应用池频繁回收:内存泄漏、配置变更、健康检查失败;
- CPU 飙高:线程池饥饿、死循环、GC 压力;
- 启动慢 :
loadUserProfile、预热、依赖服务。
6. 身份、认证与授权:IIS 最容易被误解的部分
6.1 四个身份,不要混
| 身份 | 含义 | 由什么决定 |
|---|---|---|
| 进程身份 | w3wp.exe 的令牌 |
应用池身份 |
| 匿名身份 | 匿名请求使用的账户 | anonymousAuthentication userName |
| 客户端身份 | Windows 认证时登录的用户 | 认证方式 + 客户端凭据 |
| 线程模拟身份 | 请求处理时线程 impersonate 的身份 | 认证模块 / ASP.NET impersonation |
文件访问、网络访问、注册表访问,最终看的是线程当前令牌,而不一定是进程令牌。
6.2 应用池身份的令牌差异
| 应用池身份 | 典型令牌组 | SeImpersonate | 备注 |
|---|---|---|---|
| ApplicationPoolIdentity | SERVICE、IIS APPPOOL\<池>、IIS_IUSRS |
有 | 虚拟账户,服务方式登录 |
| NetworkService | NETWORK SERVICE |
有 | 传统服务账户 |
| LocalSystem | SYSTEM、Administrators |
有 | 权限过大 |
| 自定义用户 + LogonBatch | BATCH、指定用户组 |
没有 | 本指南案例的身份 |
| 自定义用户 + LogonService | 可能带 SERVICE |
视情况 | 需实测,不建议随意切换 |
6.3 匿名认证:IUSR 还是应用池身份
xml
<system.webServer>
<security>
<authentication>
<anonymousAuthentication enabled="true" userName="IUSR" />
</authentication>
</security>
</system.webServer>
两种关键取值:
userName="IUSR":使用内置 IUSR 账户。匿名模块需要为 IUSR 建立令牌,并在请求期间模拟它。这要求 worker 具备完成模拟的能力;自定义LogonBatch账户没有SeImpersonate时,可能返回500.0 / 0x80070542。userName="":匿名请求直接使用应用程序池标识,不做 IUSR 模拟。文件访问使用 w3wp 的进程令牌,因此要确保应用池账户对站点目录有读权限。
另外还有应用池的 processModel.passAnonymousToken,它决定匿名令牌是否由 HTTP.sys 传递到 worker。实际行为取决于登录类型、认证模块和令牌状态;排查时以"有效配置 + 实际令牌"为准。
选择建议:
- 应用不需要按客户端身份访问资源 → 优先
userName="",减少模拟依赖; - 应用确实需要模拟客户端(如按用户身份访问网络资源)→ 使用 Windows 认证,并接受
SeImpersonate带来的安全影响; - 不要为了"让 IUSR 能工作"就无条件给应用池加
SeImpersonate。
6.4 Windows 认证与 Kerberos
Windows 认证常见于内网应用:
- NTLM:简单,但不利于双跳;
- Kerberos:支持委派,适合多跳访问后端;
useKernelMode:内核模式认证,避免 user-mode 的 double-hop 问题;- SPN:
setspn -S HTTP/www.example.com DOMAIN\svc_web; - 委派:约束委派(Constrained)、资源型约束委派(RBCD)、非约束委派(危险);
- 扩展保护(Extended Protection):防御凭据中继;
- 双跳问题:IIS → 后端 SQL/文件共享时,NTLM 不能直接转发凭据,需要 Kerberos 委派或改用服务账户/应用池身份访问。
6.5 其他认证方式
| 方式 | 说明 | 注意 |
|---|---|---|
| Basic | Base64 用户名密码 | 必须配 TLS,否则明文 |
| Digest | 摘要认证 | 依赖域,现代应用少用 |
| 客户端证书 | 双向 TLS | 需要证书映射配置 |
| Forms | ASP.NET 表单登录 | 应用层实现,注意 Cookie 安全 |
| OIDC/OAuth | 通过模块/中间件 | ASP.NET Core 常用 |
6.6 ASP.NET impersonation 与 IIS 认证的区别
xml
<system.web>
<identity impersonate="false" />
</system.web>
- IIS 匿名认证决定"匿名请求用什么身份";
- ASP.NET
<identity impersonate="true">决定"ASP.NET 线程是否模拟当前用户"; - 两者叠加时,最终文件访问身份可能既不是应用池身份,也不是 IUSR。
排查时一定要把 IIS 配置和 web.config 一起看。
6.7 授权:URL 授权与 NTFS
xml
<system.webServer>
<security>
<authorization>
<deny users="?" />
<allow users="*" />
</authorization>
</security>
</system.webServer>
- URL 授权在
AuthorizeRequest阶段生效; - NTFS ACL 在真正访问文件时生效;
- 两者的主体可能不同:URL 授权看的是客户端身份,NTFS 看的是线程令牌;
- NTFS 是最终边界,不要只依赖 URL 授权。
6.8 用户权限与特权
IIS 相关的常见用户权限:
| 权限 | 作用 | 风险 |
|---|---|---|
SeImpersonatePrivilege |
模拟客户端令牌 | Potato 提权到 SYSTEM |
SeAssignPrimaryTokenPrivilege |
分配主令牌 | 令牌滥用 |
SeBatchLogonRight |
批处理登录 | 自定义应用池账户必需 |
SeServiceLogonRight |
服务登录 | 服务账户必需 |
SeNetworkLogonRight |
网络登录 | 影响匿名/网络访问 |
SeChangeNotifyPrivilege |
绕过遍历检查 | 通常默认启用 |
Windows 对 SeImpersonatePrivilege 有强制分配要求:Administrators 和 SERVICE 必须拥有该权限 。用安全模板删除 SERVICE 时,secedit 会提示"此设置已被调整"并自动加回 S-1-5-6。
这意味着:
- 想通过全局策略让所有 IIS worker 都没有 SeImpersonate,基本不可行;
- 正确做法是改变身份模型,让 worker 不带 SERVICE SID,或者不需要模拟;
- 任何带 SeImpersonate 的 Web 进程,都应视为潜在的系统提权入口(GodPotato、JuicyPotato、PrintSpoofer、RoguePotato 等)。
6.9 服务账户与 gMSA
- 专用域账户:便于访问域资源,但要管理密码、登录权限、ACL;
- gMSA(Group Managed Service Account):自动轮换密码,适合多服务器场景;
- 支持的 IIS 版本可以评估 gMSA 应用池身份,但要在测试环境验证登录类型、权限和兼容性;
- 无论哪种账户,都遵循最小权限:只给必要的登录权限和目录权限。
6.10 令牌检查方法
- 在 worker 里执行
whoami /all(临时测试页); - 用管理员权限读取 w3wp 进程令牌(
OpenProcessToken+GetTokenInformation); - Process Explorer 的 Security 标签;
- 以相同账户跑一个计划任务,观察令牌组和特权(近似但不完全等价);
- 检查
secedit /export的用户权限分配。
重点看:令牌组里是 SERVICE 还是 BATCH,有没有 SeImpersonate,以及进程 owner 是否与配置一致。
7. 静态内容、动态内容与 handlers/modules
7.1 静态文件
StaticFileModule处理静态文件;- MIME 类型必须正确,否则可能 404.3/500.19;
- 静态压缩(
httpCompression/urlCompression)能降低带宽,但增加 CPU; - 缓存头(
clientCache、Cache-Control、ETag)影响浏览器和 CDN; - HTTP.sys 内核缓存对静态文件效果显著。
7.2 ASP.NET Framework
- 安装
Web-Asp-Net45/Web-Net-Ext45; - 应用池
managedRuntimeVersion=v4.0; - 集成管道下,ASP.NET 模块与 IIS 模块共享请求生命周期;
machineKey、ViewState、sessionState、customErrors是常见配置点;ViewState反序列化是真实攻击面,生产环境要保护 machineKey、开启 MAC,必要时禁用 ViewState。
7.3 ASP.NET Core
- 安装 .NET Hosting Bundle(包含 ANCM);
- IIS 通过
AspNetCoreModuleV2反向代理到 Kestrel; - 进程内托管(in-process)性能更好,进程外(out-of-process)隔离更好;
- 常见错误:
500.30:应用启动失败;500.31:ANCM 找不到;500.32:目标框架不匹配;502.5:进程外模式进程崩溃;
- 排查:启用 stdout 日志、看事件日志、检查
web.config的aspNetCore节点和环境变量。
7.4 PHP / FastCGI / ISAPI
- PHP 通常通过 FastCGI 运行;
- 需要
Web-CGI、FastCGI 模块、php-cgi.exe; cgi.force_redirect=0、fastcgi.impersonate等配置要匹配;- ISAPI/CGI 是旧技术,安全风险高,非必要不启用。
7.5 handlers 与 modules
xml
<system.webServer>
<handlers>
<add name="MyHandler" path="*.foo" verb="*"
type="MyNamespace.MyHandler, MyAssembly"
resourceType="Unspecified" preCondition="integratedMode" />
</handlers>
<modules>
<add name="MyModule" type="MyNamespace.MyModule, MyAssembly" />
</modules>
</system.webServer>
- handler 决定"谁来处理请求";
- module 决定"请求管道里插入什么逻辑";
- 注意
preCondition、resourceType、模块顺序; - 移除不用的 handler/module,减少攻击面。
7.6 URL Rewrite、ARR、WebSocket、HTTP/2
- URL Rewrite:重写、重定向、出站规则;
- ARR:反向代理、负载均衡、健康检查;
- WebSocket:需要
Web-WebSockets功能,代理场景要确保 ARR/负载均衡支持; - HTTP/2:IIS 10 + TLS,部分认证/代理场景会回退到 HTTP/1.1。
8. 日志、监控与诊断
8.1 IIS 日志
默认 W3C 格式,关键字段:
text
date time s-ip cs-method cs-uri-stem cs-uri-query s-port
cs-username c-ip cs(User-Agent) cs(Referer)
sc-status sc-substatus sc-win32-status sc-bytes cs-bytes time-taken
重点:
sc-status:HTTP 状态码;sc-substatus:子状态,例如 500.19 的 19;sc-win32-status:Windows 错误码,例如 1346(0x542);time-taken:处理时间,单位毫秒;cs-bytes/sc-bytes:请求/响应体大小。
日志位置:
text
%SystemDrive%\inetpub\logs\LogFiles\W3SVC<SiteID>
配置日志滚动:
xml
<siteDefaults>
<logFile logFormat="W3C" directory="%SystemDrive%\inetpub\logs\LogFiles"
enabled="true" period="Daily" truncateSize="1048576" />
</siteDefaults>
8.2 Failed Request Tracing(FRT)
FRT 是排查 500/401/403 的利器。web.config 示例:
xml
<system.webServer>
<tracing>
<traceFailedRequests>
<add path="*">
<traceAreas>
<add provider="WWW Server" areas="Authentication,Security" verbosity="Verbose" />
</traceAreas>
<failureDefinitions statusCodes="500" />
</add>
</traceFailedRequests>
</tracing>
</system.webServer>
日志默认在:
text
%SystemDrive%\inetpub\logs\FailedReqLogFiles
FRT 能看到每一步的模块、耗时、身份、错误信息。排障完记得关闭,避免日志膨胀。
8.3 事件日志、ETW 与 HTTPERR
- Event Viewer → Windows Logs → Application:
IIS-W3SVC-WP、ASP.NET、Application Error; - ETW:
Microsoft-Windows-IIS-*提供程序; - HTTPERR:
%SystemRoot%\System32\LogFiles\HTTPERR,记录 HTTP.sys 层的错误(如 400、503、Timer); - Windows 事件日志里的"应用程序池已停止""快速失败保护"能解释 503。
8.4 性能计数器
常用:
Web Service(_Total)\Current Connections;Web Service(_Total)\Get Requests/sec;W3SVC_W3WP(*)\Active Requests;APP_POOL_WAS(*)\Current Application Pool State;HTTP Service Request Queues(*)\CurrentQueueSize;Process(w3wp*)\Private Bytes、% Processor Time。
用 logman 或性能监视器采集,结合应用日志看高峰。
8.5 诊断命令
cmd
appcmd list sites
appcmd list apps
appcmd list apppools
appcmd list wp
appcmd list requests
netsh http show servicestate
netsh http show urlacl
netsh http show sslcert
wevtutil qe Application /q:"*[System[Provider[@Name='IIS-W3SVC-WP']]]" /f:text
8.6 常见状态码
| 状态码 | 常见原因 |
|---|---|
401.1 |
登录失败(凭据/账户) |
401.2 |
认证配置不允许 |
401.3 |
ACL 拒绝 |
403.14 |
目录浏览被禁用 |
404.0 |
文件不存在 |
404.7 |
请求过滤拒绝扩展名 |
404.15 |
查询字符串过长 |
413.1 |
请求体过大 |
500.19 |
配置错误/配置节锁定 |
500.0 |
泛型处理错误,需看 HRESULT |
500.30 |
ASP.NET Core 启动失败 |
500.31 |
ANCM 未找到 |
500.32 |
目标框架不匹配 |
502.5 |
进程外应用启动失败 |
503 |
应用池不可用/队列满 |
9. 性能与容量
9.1 缓存层次
text
浏览器缓存
↓
CDN / 反向代理
↓
HTTP.sys 内核缓存
↓
IIS 输出缓存 / 应用缓存
↓
数据库 / 后端
- 静态资源:强缓存 + 版本化文件名;
- 动态页面:输出缓存要谨慎,注意用户相关数据;
- 内核缓存适合静态文件,但会掩盖后端故障;
- 分布式缓存(Redis)适合多实例/无状态应用。
9.2 压缩
- 静态压缩:预先压缩,CPU 开销低;
- 动态压缩:每次请求压缩,CPU 开销高;
- 对已压缩格式(图片、视频、zip)不要再压缩;
- 注意 BREACH 等侧信道风险(敏感响应 + 压缩 + 反射输入)。
9.3 连接、队列与 keep-alive
maxConnections、queueLength影响并发;- keep-alive 减少握手开销;
- 队列满 → 503;
- 反向代理场景注意后端连接池和超时。
9.4 预热与 AlwaysRunning
xml
<applicationPools>
<add name="MyPool" startMode="AlwaysRunning" />
</applicationPools>
<site name="MySite" preloadEnabled="true" />
配合应用初始化模块(Application Initialization)做预热,避免首次请求慢。
9.5 Web garden
maxProcesses > 1 会启动多个 worker:
- 适合完全无状态、可并行的应用;
- 会话状态必须外置;
- 进程内缓存、锁、定时任务会出问题;
- 大多数 ASP.NET 应用不建议 Web garden。
9.6 会话状态
- InProc:快,但 worker 回收/多实例会丢失;
- StateServer / SQL Server:跨进程,但有网络/序列化开销;
- Redis / 分布式缓存:现代推荐;
- ASP.NET Core:使用分布式缓存 + Data Protection 密钥共享。
9.7 容量规划
- 用真实流量做压测,不要只看 CPU;
- 关注队列长度、响应时间 P95/P99、GC、线程池;
- 应用池回收策略与流量高峰错开;
- 预留突发容量,监控 503/队列满。
10. 安全加固
10.1 最小权限
- 应用池使用低权限账户;
- 不给
SeImpersonate、SeAssignPrimaryToken、SeBackup等不必要特权; - 不给本地管理员、RDP、交互登录;
- 站点目录只给读+执行,写目录单独授权;
- 不同信任级别应用使用不同应用池。
10.2 TLS 与证书
- 禁用 SSL 2.0/3.0、TLS 1.0/1.1(视兼容性);
- 启用 TLS 1.2/1.3;
- 使用强密码套件;
- 证书私钥 ACL 只给必要账户;
- 配置 HSTS、HTTP → HTTPS 重定向;
- 证书到期监控。
10.3 请求过滤
xml
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="104857600" maxUrl="8192" maxQueryString="2048" />
<hiddenSegments>
<add segment="App_Data" />
</hiddenSegments>
<fileExtensions allowUnlisted="false">
<add fileExtension=".aspx" allowed="true" />
<add fileExtension=".html" allowed="true" />
</fileExtensions>
</requestFiltering>
</security>
</system.webServer>
- 生产环境尽量用允许列表;
- 限制请求体、URL、查询串长度;
- 隐藏
.config、.bak、.git等敏感路径。
10.4 错误页与版本头
customErrors mode="RemoteOnly"或On;- ASP.NET Core 关闭开发者异常页;
- 移除
X-Powered-By、X-AspNet-Version、X-AspNetMvc-Version; Server头视 IIS 版本可用注册表或 URL Rewrite 出站规则移除;- 错误页不要泄露堆栈、路径、服务器版本。
10.5 上传与可写目录
- 上传目录不要给执行权限;
- 文件类型、大小、内容校验;
- 存储到 Web 根之外或独立目录;
- 防病毒扫描;
- 文件名随机化,避免路径穿越;
- 不要把用户上传目录配置成应用程序。
10.6 WebDAV、FTP 与远程管理
- 不用的 WebDAV、FPSE 关闭;
- FTP 使用 FTPS 或 SFTP,限制目录和账户;
- 远程管理(IIS Manager、Web Deploy)只在内网/VPN,启用强认证;
- 管理端点不要暴露到公网。
10.7 ASP.NET machineKey 与 ViewState
- machineKey 泄露可能导致 ViewState 反序列化 RCE(真实案例非常多);
- 使用强随机 validation/decryption key;
- 开启
enableViewStateMac,不要使用弱算法; - 不需要 ViewState 的页面禁用 ViewState;
- 定期轮换密钥,注意多服务器一致性;
- ASP.NET Core 使用 Data Protection,密钥要持久化并保护。
10.8 安全响应头
建议:
text
Strict-Transport-Security
X-Content-Type-Options: nosniff
X-Frame-Options: DENY / SAMEORIGIN
Referrer-Policy
Content-Security-Policy(按应用定制)
Permissions-Policy
10.9 补丁与基线
- Windows / IIS / .NET / ASP.NET Core 运行时及时更新;
- 删除不用的功能、模块、虚拟目录;
- 定期用基线脚本检查应用池身份、ACL、认证配置、TLS;
- 变更前备份,变更后回归。
11. 高可用、部署与运维
11.1 负载均衡与 ARR
- 多台 IIS 前面加 NLB、ARR、F5、HAProxy 等;
- 应用尽量无状态,会话外置;
- 健康检查要覆盖真实应用路径,不要只看首页;
- 注意
X-Forwarded-For、X-Forwarded-Proto的处理; - WebSocket、长连接、超时要单独验证。
11.2 共享配置
IIS 支持把 applicationHost.config 放到 UNC 共享,集中管理多台服务器:
- 需要配置
redirection.config和共享路径凭据; - 共享存储是单点,要做好高可用;
- 配置变更要原子化,避免多台服务器读到半成品;
- 敏感配置(如应用池密码)注意加密和权限。
11.3 备份与恢复
appcmd add backup/appcmd restore backup;- 配置历史
%SystemDrive%\inetpub\history; - 站点内容、证书、日志、应用配置分开备份;
- 定期演练恢复,不要只备份不验证。
11.4 部署
- Web Deploy / MSDeploy:适合传统 IIS;
- CI/CD:打包、发布、回滚;
- 容器:Windows 容器 + IIS 镜像,注意镜像大小和许可;
- 配置即代码:DSC、Ansible、Packer;
- 发布前检查 ACL、应用池身份、绑定、证书。
11.5 证书生命周期
- 到期监控;
- 自动续期;
- 绑定更新(SNI 多站点要逐个更新);
- 私钥权限;
- 旧证书清理。
12. 故障排查方法论与真实案例
12.1 分层排查方法
按下面的顺序排除,效率最高:
text
1. DNS / 网络 / 端口 / 防火墙
2. HTTP.sys:监听、URL ACL、证书绑定、缓存
3. WAS / 应用池:状态、快速失败、队列、身份
4. worker:进程是否存在、owner、令牌
5. 认证 / 授权:匿名身份、Windows 认证、ACL
6. 配置:继承、锁定、web.config
7. 应用 / handler / 模块 / 后端
8. 日志:IIS、FRT、事件日志、HTTPERR
常用检查:
cmd
net start W3SVC
net start WAS
appcmd list sites
appcmd list apppools
appcmd list wp
netsh http show servicestate
netsh http show urlacl
icacls C:\inetpub\wwwroot
12.2 真实案例:500.0 / 0x80070542
现象
一台 Windows Server 2025 域控制器上,IIS 承载一个 ASP.NET 应用 /releases,应用池使用自定义域用户 svc_feed。访问应用返回:
text
HTTP 错误 500.0 - Internal Server Error
模块: IIS Web Core
通知: AuthenticateRequest
处理程序: StaticFile
错误代码: 0x80070542
IIS 日志:
text
GET /releases/ - 500 0 1346
根路径一度返回 200,但加上随机查询串后同样是 500------HTTP.sys 缓存掩盖了真实状态。
诊断
0x80070542=ERROR_BAD_IMPERSONATION_LEVEL(Win32 1346),说明失败发生在身份模拟阶段;- 应用池
LatticeFeed的身份是MAZE\svc_feed,SpecificUser+LogonBatch; - 匿名认证是
userName="IUSR"; - 读取 w3wp 令牌,
svc_feed只有SeMachineAccount、SeChangeNotify、SeIncreaseWorkingSet,没有 SeImpersonate; - 令牌组是
BATCH,不是SERVICE; - 日志/令牌/配置三者对上:匿名模块需要以
svc_feed模拟 IUSR,但 worker 没有完成模拟的能力。
为什么不能简单"加权限"
给 svc_feed 加 SeImpersonatePrivilege 通常能让 IUSR 模拟恢复,但 Web 进程一旦拥有 SeImpersonate,就可以被 GodPotato/JuicyPotato 等工具利用,从 Web 漏洞直接升级到 SYSTEM。对安全敏感的站点,这是把攻击面扩大了一个数量级。
同时,Windows 强制 SeImpersonatePrivilege 包含 Administrators 和 SERVICE,想用安全模板把它从 SERVICE 里删掉也会被自动加回:
text
SeImpersonatePrivilege 必须被指派给管理员。此设置被调整。
必须将 SeImpersonatePrivilege 分配给 SERVICE。此设置已被调整。
正确的修复
把匿名认证改为使用应用程序池标识,不再模拟 IUSR:
powershell
appcmd set config "Default Web Site" /section:system.webServer/security/authentication/anonymousAuthentication /userName: /commit:apphost
appcmd set config "Default Web Site/releases" /section:system.webServer/security/authentication/anonymousAuthentication /userName: /commit:apphost
再把根站点挂到同一个 LogonBatch 应用池,避免 DefaultAppPool(虚拟账户带 SERVICE SID)产生一个带 SeImpersonate 的 worker:
powershell
appcmd set app "Default Web Site/" /applicationPool:LatticeFeed
appcmd stop apppool /apppool.name:DefaultAppPool
验证:
- 根路径、应用、静态文件全部 200;
- 临时 ASPX 输出
MAZE\svc_feed,确认请求身份不变; - w3wp 令牌仍然没有 SeImpersonate;
- IIS 重启后配置持久;
- 清理测试文件和日志。
案例启示
500.0不是"服务器内部错误"这么简单,HRESULT 才是关键;- 静态文件缓存会掩盖故障,排障要用 cache-busting;
- 自定义应用池账户 + IUSR 匿名认证是一个常见的隐藏坑;
- "加权限"不一定是最佳修复,移除"需要模拟"的设计往往更安全;
- Windows 的强制用户权限分配意味着有些 hardening 目标只能通过身份模型实现。
12.3 常见错误速查
| 现象 | 优先检查 |
|---|---|
500.19 / 0x8007000d |
web.config XML 格式、节顺序、重复定义 |
500.19 / 0x80070021 |
配置节被锁定、overrideMode |
503 |
应用池停止、快速失败、队列满、应用初始化失败 |
401.3 |
NTFS ACL、匿名身份、应用池身份 |
401.1 / 401.2 |
认证方式、账户、Kerberos/SPN |
404.7 |
请求过滤、扩展名不允许 |
413.1 |
maxAllowedContentLength、前端代理限制 |
500.30 / 502.5 |
ASP.NET Core 启动失败、运行时缺失、环境变量 |
500.0 / 0x80070542 |
身份模拟/匿名认证/应用池令牌 |
| 站点 200 但应用 500 | HTTP.sys 缓存、默认文档、路径映射 |
12.4 排障清单
- 用 cache-busting 复现;
- 看错误页的模块/通知/HRESULT;
- 看 IIS 日志的
sc-win32-status; - 确认应用绑定哪个池、worker 真实 owner;
- 读 w3wp 令牌特权/组;
- 看有效匿名认证配置(站点级 + 应用级);
- 看 NTFS ACL;
- 开 FRT;
- 看事件日志/HTTPERR;
- 变更前备份,修复后回归。
13. 常用命令速查
13.1 appcmd
cmd
appcmd list sites
appcmd list apps
appcmd list apppools
appcmd list wp
appcmd list requests
appcmd list config "Default Web Site" /section:system.webServer/security/authentication/anonymousAuthentication
appcmd list apppool "MyPool" /text:*
appcmd set config "Default Web Site/app" /section:system.webServer/security/authentication/anonymousAuthentication /userName: /commit:apphost
appcmd set app "Default Web Site/" /applicationPool:MyPool
appcmd start apppool /apppool.name:MyPool
appcmd stop apppool /apppool.name:MyPool
appcmd recycle apppool /apppool.name:MyPool
appcmd add backup "backup-name"
appcmd restore backup "backup-name"
13.2 PowerShell
powershell
Import-Module WebAdministration
Get-Website
Get-WebApplication
Get-WebAppPoolState "MyPool"
Restart-WebAppPool "MyPool"
Get-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Location 'Default Web Site/app' `
-Filter 'system.webServer/security/authentication/anonymousAuthentication' `
-Name userName
Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Location 'Default Web Site/app' `
-Filter 'system.webServer/security/authentication/anonymousAuthentication' `
-Name userName -Value ''
13.3 权限与策略
cmd
icacls C:\inetpub\wwwroot
icacls C:\inetpub\app /grant "IIS_IUSRS:(OI)(CI)(RX)" "DOMAIN\svc_web:(OI)(CI)(RX)"
secedit /export /cfg C:\Windows\Temp\userrights.inf /areas USER_RIGHTS
gpresult /h C:\Windows\Temp\gpo.html
whoami /all
13.4 网络与 TLS
cmd
netsh http show servicestate
netsh http show urlacl
netsh http show sslcert
netsh http show cachestate
13.5 日志与事件
cmd
wevtutil qe Application /q:"*[System[Provider[@Name='IIS-W3SVC-WP']]]" /f:text
logman query
14. 结语:IIS 配置的底层原则
IIS 看似是"点点鼠标就能跑"的服务器,真正的复杂度在配置继承和身份边界。把下面几句话记住,能避开大部分坑:
- 配置是继承的:看有效配置,不要只看某一层。
- 身份是分层的:进程身份、匿名身份、客户端身份、线程模拟身份,各有各的来源。
- 权限是组合的:用户权限 + 令牌组 + ACL + 认证方式,缺一不可。
- 缓存会骗人:HTTP.sys、输出缓存、浏览器缓存都可能返回旧结果。
- 最小权限不是口号:自定义应用池账户、独立池、读写分离、禁用不用的功能。
- 不要用危险权限修功能问题:SeImpersonate 能让 IUSR 模拟工作,也能让 Web 漏洞变成 SYSTEM。
- Windows 有强制规则:SeImpersonate 必须包含 Administrators 和 SERVICE;与其对抗,不如换身份模型。
- 排障先分层:DNS → HTTP.sys → WAS/池 → worker → 认证/授权 → 配置 → 应用 → 日志。
IIS 的"深度",最终体现在你能否清楚地回答三个问题:
- 这个请求是以谁的身份执行的?
- 这个身份为什么拥有(或没有)某个权限?
- 出问题时,哪一层的配置在起作用?
回答清楚这三个问题,500.0、401.3、503、配置锁定、Kerberos 双跳,都会变成可以解释、可以复现、可以修复的问题。