Windows服务器上最容易被忽视的安全缺口,往往不是防火墙规则,也不是补丁更新,而是应用程序本身以什么身份、什么权限在运行。很多管理员习惯直接给应用配置管理员权限,或者让服务以SYSTEM账户启动,这种做法一旦应用被攻破,攻击者就直接拿到了整台服务器的控制权。权限管控的核心思路只有一条:让应用程序只拥有完成自身任务所需的最小权限,多一分都不给。
先看最常见的问题。IIS站点运行在应用程序池中,默认的应用程序池标识是ApplicationPoolIdentity,这是一个虚拟账户,本身权限很低,只能访问IIS临时目录和站点目录。但很多开发者在部署时会遇到“拒绝访问”错误,于是直接把应用程序池标识改成LocalSystem或者管理员账户,问题虽然解决了,但安全风险被无限放大。正确的做法是保持ApplicationPoolIdentity不变,然后针对站点需要访问的具体目录,单独给这个虚拟账户授权。比如站点需要读写D:\WebData\Uploads目录,就用icacls命令给IIS AppPool\YourAppPoolName这个账户授予读写权限,而不是把整个D盘交给它。
icacls "D:\WebData\Uploads" /grant "IIS AppPool\YourAppPoolName:(OI)(CI)M"
Windows服务是另一个重灾区。很多第三方服务安装后默认以LocalSystem运行,比如数据库代理、备份工具、监控组件。LocalSystem是Windows中权限最高的账户之一,几乎等同于操作系统内核权限。如果这类服务存在漏洞,攻击者可以利用服务提权直接拿下服务器。应该为每个服务创建独立的低权限域账户或本地账户,只授予服务运行所需的最小权限。在服务管理器中,把服务的“登录”选项卡从“本地系统账户”改为“此账户”,填写专用账户,然后在本地安全策略中给这个账户授予“作为服务登录”的权限。对于不需要网络访问的服务,还可以在防火墙层面直接阻止该账户的入站和出站连接。
文件系统权限的精细化配置Windows服务器上文件权限的默认继承关系经常被忽略。C:\inetpub、C:\Program Files这些目录的权限继承自父目录,如果管理员在根目录做过宽松授权,子目录会全部继承。检查权限时不要只看站点根目录,要用icacls命令查看完整ACL。一个实用的方法是先重置权限为只读,再按需开放写入目录。例如站点目录只需要读取和执行的权限,上传目录需要读写,配置文件可能需要读取但禁止写入。用icacls可以精确控制继承和覆盖。
icacls "D:\WebSite" /inheritance:r /grant:r "IIS AppPool\YourAppPoolName:(OI)(CI)RX"
这条命令会移除继承,只给应用程序池账户读取和执行权限。然后对上传子目录单独授权写入。很多管理员不知道的是,NTFS权限里“修改”和“完全控制”差别很大,“完全控制”包含更改权限和取得所有权的权利,普通应用账户根本不需要。给写入权限时优先用“修改”而不是“完全控制”,避免应用账户反过来修改ACL。
应用程序池隔离与账户分离同一台IIS上如果运行多个站点,必须为每个站点创建独立的应用程序池,并让每个池使用不同的虚拟账户。这样即使一个站点被入侵,攻击者也无法通过文件系统权限访问其他站点的目录。很多服务器上十几个站点共用一个应用程序池,一个站点的漏洞就能横向感染所有站点。隔离的代价几乎为零,只是多几个w3wp.exe进程,内存占用增加几十兆,但安全收益是数量级的。
除了IIS,计划任务也是权限管控的薄弱环节。很多自动化脚本以管理员账户运行,脚本内容可能包含明文密码或敏感操作。应该为计划任务创建专用的低权限账户,并且把脚本文件放在只有该账户和系统管理员能访问的目录。如果脚本需要调用某些需要高权限的操作,可以只把那个具体操作封装成单独的计划任务,用高权限账户运行,而不是整个脚本都用高权限。
注册表权限同样不能忽略应用程序运行时会读写注册表项,尤其是HKLM下的软件配置项。默认情况下HKLM\Software只有管理员和SYSTEM有写权限,普通账户只有读权限。有些应用在安装时被赋予了过宽的注册表权限,或者在运行时需要写入HKLM下的某个键值。正确的做法是找到应用需要写入的具体注册表键,单独给应用账户授予写权限,而不是开放整个HKLM\Software分支。可以用regedit定位到目标键,右键权限,添加应用账户并只授予“设置数值”和“创建子项”的最小权限。
服务账户的密码管理与审计低权限服务账户创建之后,密码管理往往失控。很多服务器上服务账户密码从不更换,甚至多个服务共用一个账户。一旦某个服务被攻破,攻击者提取出账户密码后可以用于其他服务。应该为每个服务账户设置强密码,并定期轮换。对于本地账户,可以用PowerShell脚本批量更新密码;对于域账户,可以结合组策略的托管服务账户功能,让系统自动管理密码轮换。同时开启审核策略,记录账户登录事件和特权使用事件,定期检查是否有异常的服务账户活动。
auditpol /set /subcategory:"Logon" /success:enable /failure:enable
这条命令开启登录审核,可以在安全日志中看到哪些账户在什么时间登录过服务器。对于服务账户,正常情况下不应该有交互式登录记录,如果发现某个服务账户有交互式登录,说明可能被滥用。
应用程序自身的权限声明与控制除了操作系统层面的权限管控,应用程序自身也需要声明运行所需的最小权限。比如.NET应用可以在Web.config中设置trust level,限制应用对文件系统、注册表、网络等资源的访问范围。把trust level设为Medium或更低的级别,应用就无法访问大部分系统敏感区域。对于需要更高权限的特定操作,可以通过代码层面的权限声明来精确控制,而不是整个应用运行在高信任级别。
这条配置放在Web.config的system.web节点下,可以把ASP.NET应用的信任级别限制为Medium,阻止应用访问除自身目录以外的大部分系统资源。很多开发框架默认使用Full trust,这意味着应用代码可以做任何账户权限允许的事情,一旦代码存在漏洞,攻击面非常大。
权限变更的审批与记录权限管控不是一次性配置完就结束了,后续的每次变更都需要记录和审批。很多服务器权限混乱的根源在于多个管理员随意授权,今天给一个账户加了权限,明天又给另一个账户加了完全控制,时间一长ACL变成一团乱麻。建议用PowerShell脚本定期导出关键目录和服务的权限配置,保存为基线文件。任何权限变更都要对比基线,确认变更内容符合预期。可以用以下命令导出目录ACL:
Get-Acl "D:\WebSite" | Format-List | Out-File "D:\ACL_Backup\WebSite_ACL.txt"
定期对比当前ACL和基线文件,能快速发现异常的权限变更。如果某个账户突然被授予了写入权限,而没有任何变更记录,就要立即排查是否存在入侵行为。
权限管控的最终目标是让攻击者在突破应用层防御之后,仍然被困在一个低权限的沙箱里,无法横向移动,无法提权,无法访问敏感数据。这需要管理员从服务账户、文件系统、注册表、应用程序信任级别、审核记录等多个维度同时入手,而不是只做其中一两项。每一项配置看起来都很简单,但组合起来就能形成有效的纵深防御。
