msdn----web.config
保护 ASP.NET 应用程序的安全发布日期: 1/21/2005 | 更新日期: 1/21/2005
本单元概要保护 ASP.NET Web 应用程序依赖于完全受到保护的网络、主机和平台基础结构。如果果真如此,攻击者将试图利用 Web 应用程序和 Web 服务(它们通常侦听端口 80)中的漏洞。如果 Web 应用程序没有有效配置,攻击者就能够获取访问权限并利用系统。 作为管理员,您必须审查默认的机器级配置和单独的应用程序配置,以处理和删除任何脆弱或者不安全的设置。 本单元从系统管理员的观点描述了 ASP.NET 的新功能,以及如何配置机器范围的和特定于应用程序的安全设置。还提出了一种保护 ASP.NET Web 应用程序和 Web 服务的方法,它对我们已经提出的保护网络、应用程序服务器、数据库服务器和 Web 服务器的方法是一种补充。 目标使用本单元可以:
适用范围本单元适用于下列产品和技术:
如何使用本单元本单元的注意力主要集中在 ASP.NET 应用程序的关键安全注意事项。要想充分利用本单元:
相关的指导,请阅读“寄宿多个 ASP.NET 应用程序”单元,该单元说明了如何将多个运行在同一服务器上的 Web 应用程序与关键系统资源隔离,以及将应用程序互相隔离。有关为部分信任 Web 应用程序和 Web 服务配置代码访问安全 (CAS) 策略的更多信息,请参阅“在 ASP.NET 中使用代码访问安全”单元。 本页内容方法要保护 ASP.NET 应用程序的安全,首先要有加固的操作系统和 .NET Framework 安装基础,然后应用安全应用程序配置设置,以减少应用程序的受攻击面。 这些内容包括:
须知在开始保护 Web 应用程序和 Web 服务之前,应该知道一些涉及全局的注意事项和细节。 ASP.NET 进程模型 在 Microsoft Windows 2000 上,Internet 信息服务 (IIS) 5.0 在 ASP.NET 辅助进程 (Aspnet_wp.exe) 中运行所有 Web 应用程序和 Web 服务。隔离的单位是应用程序域,每个虚拟目录都有自己的应用程序域。进程级配置设置是通过 Machine.config 中的 <processModel> 元素维护的。 在 Microsoft Windows Server™ 2003 操作系统上,IIS 6.0 应用程序池使您能够使用不同的进程隔离应用程序。有关更多信息,请参阅单元“寄宿多个 ASP.NET 应用程序”。 ASP.NET 帐户 ASPNET 帐户是安装 .NET Framework 时创建的最低特权本地帐户。默认时,它运行 ASP.NET 辅助进程和 ASP.NET 状态服务。 如果您决定使用自定义帐户运行 Web 应用程序,确保配置帐户只具有最低特权。这将能够减少与攻击者使用应用程序的安全上下文设法执行代码相关联的风险。您还必须在 <processModel> 元素上指定帐户的凭据。千万不要以明文存储凭据。相反,应该使用 Aspnet_setreg.exe 工具在注册表中存储加密凭据。自定义帐户还必须被授予适当的 NTFS 权限。 Aspnet_setreg.exe 和进程、会话和身份 Aspnet_setreg.exe 允许在注册表中以加密格式存储凭据和连接字符串。此工具允许您加密以下属性:
下例显示了运行 Aspnet_setreg.exe 保护凭据前后,自定义帐户的 <processModel> 元素的变化: <!--Before-->
<processModel userName="CustomAccount" password="Str0ngPassword" />
<!--After-->
<processModel
userName="registry:HKLM\SOFTWARE\YourApp\process\ASPNET_SETREG,userName"
password="registry:HKLM\SOFTWARE\YourApp\process\ASPNET_SETREG,password"/>
您可以选择用来存储加密数据的注册表位置,虽然必须在 HKEY_LOCAL_MACHINE 之下。除了使用数据保护 API (DPAPI) 加密数据,并将其存储在注册表中之外,此工具还能够应用安全 ACL 限制对注册表项的访问。注册表项的 ACL 将向 System、Administrator 和 Creator Owner 授予完全控制权限。如果使用此工具加密 <identity> 元素的凭据或者 <sessionState> 元素的连接字符串,您还必须授予 ASP.NET 进程帐户的读访问权限。 要获取 Aspnet_setreg.exe 工具和有关的更多信息,请参阅 Microsoft 知识库文章 329290,“How To:Use the ASP.NET Utility to Encrypt Credentials and Session State Connection Strings”。 模拟不是默认的 默认时,ASP.NET 应用程序不进行模拟。因此,资源访问是使用 ASP.NET 辅助进程标识进行的。您必须通过创建适当配置的 ACL 授予进程标识(至少)对 Windows 资源的读访问权限(这是应用程序要求的)。 如果您确实需要启用模拟,可以模拟原始调用方 — 即经 IIS 身份验证的标识或者 <identity> 元素中指定的固定标识。有关更多信息,请参阅本单元后面的“模拟”。 通常,ASP.NET 应用程序不使用模拟,因为这对设计、实现和可伸缩性有负面影响。例如,使用模拟妨碍有效的中间层连接池的使用,这限制了应用程序的可伸缩性。在特定的场景例如当应用程序使用匿名用户帐户的安全上下文进行资源访问时,模拟可能非常重要。这是一个常用技术,经常在多个应用程序寄宿在同一服务器时使用。有关更多信息,请参阅单元“寄宿多个 ASP.NET 应用程序”。 HttpForbiddenHandler、Urlscan 和 404.dll 您可以用许多技术来防止访问受限资源。ASP.NET 提供了 HttpForbiddenHandler,可以将不应该通过 HTTP 下载的 ASP.NET 文件类型映射为此处理程序。映射是使用 <httpHandlers> 元素应用的。 IISLockdown.exe 提供了404.dll。通过这个工具,您可以配置 IIS 将不希望的文件扩展名映射到 404.dll,这样在请求这些文件类型时,会导致“HTTP 404–File not found”消息。 最后,URLScan ISAPI 筛选器可以用来阻塞对受限文件类型和可执行程序文件的请求。URLScan 是 IISLockdown 工具中附带的,但是也可以单独获得。有关更多信息,请参阅 Microsoft 知识库文章 307608,“ INFO:Using URLScan on IIS”,和本指南“如何……”部分中的“如何使用 URLScan”。 有关 IISLockdown 和 URLScan 的更多信息,请参阅“保护 Web 服务器的安全”单元。 AppSettings Web.config 中的 <appSettings> 元素允许应用程序存储配置数据,如数据库连接字符串或者服务帐户凭据。此元素的优点是,它允许开发人员将配置数据的存储和检索集中和标准化。Web.config 中的单一位置还能够简化管理和部署。 敏感数据,如连接字符串和凭据,不应该以明文形式存储在配置文件中。相反,开发人员应该在存储之前使用 DPAPI 加密机密。 有关 AppSettings 的更多信息,请参阅 MSDN® TV 上的“AppSettings in ASP.NET”节目,网址是:http://msdn.microsoft.com/msdntv。 Machine.Config 和 Web.Config 详解.NET Framework 提供的配置管理包括范围很广的一组设置,可以供管理员用来管理 Web 应用程序及其环境。这些设置存储在 XML 配置文件中,其中一些控制着机器范围的设置,而其他设置控制着特定于应用程序的配置。 XML 配置文件可以使用任何文本编辑器(如记事本或者 XML 编辑器)编辑。XML 标记是区分大小写的,因此需要确保使用正确的大小写。 图 1 显示了用来配置管理员能够访问的 ASP.NET Web 应用程序的配置文件。 ![]() 图 1. ASP.NET 配置文件 Machine.Config 和 Web.Config 文件共享许多相同的配置节和 XML 元素。Machine.config 用来将机器范围的策略应用于本地计算机上运行的所有 .NET Framework 应用程序。开发人员还可以使用特定于应用程序的 Web.config 文件为单独应用程序自定义设置。 注 Windows 可执行文件,如 WinForm 应用程序,是使用配置文件配置的。这些文件的名称从应用程序可执行文件名称派生而来,例如,App.exe.config,其中的 App 是应用程序名称。 对配置文件所做的更改是动态应用的,通常不需要您重启服务器或者任何服务,除非如果更改 Machine.config 中的 <processModel> 元素,这将在本单元后面讨论。 表 1 显示了配置文件的位置。
有关 ASP.NET Web 应用程序 CAS 配置文件的更多信息,请参阅“在 ASP.NET 中使用代码访问安全”单元。 层次化策略评估 为了集中化管理,设置可以在 Machine.config 中应用。Machine.config 中的设置定义了机器范围的策略,还可以通过 <location> 元素使用 Machine.config 中的设置应用特定于应用程序的配置。开发人员可以提供应用程序配置文件以改写机器策略的各个方面。对于 ASP.NET Web 应用程序,Web.config 文件位于应用程序的虚拟根目录,也可以选择在虚拟根下的子目录。考虑如图 2 中所示的安排。 ![]() 图 2. 层次化配置 在图 2 中,AppRoot Web 应用程序在其虚拟根目录中有一个 Web.config 文件。SubDir1(而非虚拟目录)还包含它自己的 Web.config 文件,在 HTTP 请求定向到 http://AppRoot/SubDir1 时会用到。如果请求通过 AppRoot 定向到 SubDir2(一个虚拟目录),例如,http://Server/AppRoot/SubDir2,则应用来自 AppRoot 目录中 Machine.config 和 Web.config 的设置。但是,如果请求绕过 AppRoot 而定向到 SubDir2,例如,http://Server/SubDir2,则只应用来自 Machine.config 的设置。 无论如何,基本设置都是从 Machine.config 获取的。接下来,从任何相关 Web.config 文件应用改写和添加。 如果在 Machine.config 中和一个或者多个 Web.config 文件中使用同一个配置元素,来自层次中最低的文件的设置将改写较高级的设置。在机器级没有应用的新配置设置也可能应用于 Web.config 文件,而且某些元素能够使用<clear >元素清除父级设置。 下表显示了为了应用于图 2 的 Web 请求的组合,从何处获取组合的配置设置。
<location> 元素用于三个主要目标:
<location> 标记可以用于 Machine.config 中或者 Web.config 中。对于 Machine.config,如果您指定了路径,则必须是完全限定的,包括 Web 站点名称、虚拟目录名称和可选的子目录和文件名。例如: <location path="Web Site Name/VDirName/SubDirName/PageName.aspx" >
<system.web>
. . .
</system.web>
</location>
注 当使用来自 Machine.config 的位置标记时,您必须包括 Web 站点名称。对于 Web.config,路径相对于应用程序的虚拟目录。例如: <location path="SubDirName/PageName.aspx" >
<system.web>
. . .
</system.web>
</location>
对特定的文件应用配置设置 使用 path 属性以应用特定文件的配置设置。例如,为了将授权规则应用于来自 Web.config 中的文件 Pagename.aspx,应该使用以下 <location> 元素: <location path="SubDirName/PageName.aspx" >
<system.web>
<authorization>
<deny roles="hackers" />
</authorization>
</system.web>
</location>
应用 Machine.config 中的应用程序配置设置 您还可以通过使用指定到应用程序目录路径的 <location>语句,应用 Machine.config 中的特定于应用程序的设置。这将使您得到集中化管理的优点。例如,以下片段显示了如何强制使用 Windows 身份验证和防止在特定的应用程序中使用模拟。 <location path="Default Web Site/YourApp">
<system.web>
<authentication mode="Windows"/>
<identity impersonate="false"/>
</system.web>
</location>
锁定配置设置 为了防止各个应用程序改写机器级策略配置,将设置置于 Machine.config 中的一个 <location> 元素里,并设置 allowOverride="false" 属性。 例如,为了应用无法在应用程序级改写的机器范围策略,使用以下 <location> 元素: <location path="" allowOverride="false">
<system.web>
… machine-wide defaults
</system.web>
</location>
通过将 path 属性置为空,可以指示设置应用于机器,而 allowOverride="false" 确保了 Web.config 设置不会改写指定的值。只要企图在 Web.config 中添加元素都将生成异常,即使 Machine.config 中的元素与 Web.config 中的元素匹配。 Machine.Config 和 Web.Config 准则Machine.config 中的设置应用服务器的机器级默认值。如果需要强制服务器上所有应用程序应用一个特殊的配置,使用 <location> 元素的 allowOverride="false",如上所述。这对于寄宿场景尤其适合,其中需要强制服务器上所有应用程序应用安全策略的各个方面。 对于可以对单独应用程序配置的设置,通常应用程序会提供一个 Web.config 文件。虽然可能从 Machine.config 使用多个 <location> 元素配置单独应用程序,但是通过不同的 Web.config 文件对部署来说更佳,而且能够使 Machine.config 文件更小。 主要要考虑的项是哪些设置应该通过机器策略强制。这取决于特定的场景如何。以下是一些常见的场景:
ACL 和权限 配置文件包含敏感数据,所以需要适当地配置 ACL 以限制访问。 Machine.config 默认时,Machine.config 使用以下 ACL 配置: Administrators: Full Control
System: Full Control
Power Users: Modify
Users: Read and Execute
LocalMachine\ASPNET (process identity): Read and Execute
注 在 Windows Server 2003 上,本地服务和网络服务帐户也被授予读访问权限。 Users 组的成员默认时被授予读访问权限,因为所有运行在计算机上的托管代码必须能够读取 Machine.config。 Machine.config 上的默认 ACL 是一个安全默认值。但是,如果只有一个 Web 应用程序运行在服务器上,或者所有 Web 应用程序都使用同一个进程标识,可以通过删除用户的访问控制项 (ACE) 进一步限制 ACL。如果您确实从 DACL 中删除了“users”,需要显式地添加 Web 进程标识。 Web.config .NET Framework 不安装任何 Web.config 文件。如果您安装了一个提供自己 Web.config 的应用程序,通常从 inetpub 目录继承其 ACL,它默认时授予 Everyone 组的成员读访问权限。要锁定特定于应用程序的 Web.config,使用以下的一个 ACL。 对于 .NET Framework 1.0 版: Administrators: Full control
System: Full control
ASP.NET process identity: Read
UNC Identity: Read
Impersonated Identity (Fixed Identity): Read
Impersonated Identity (Original Caller): Read
对于 .NET Framework 1.1 版: Administrators: Full control
System: Full control
ASP.NET process identity: Read
UNC Identity: Read
Impersonated Identity (Fixed Identity): Read
如果您的应用程序使用一个 显式帐户的模拟(也就是说,如果它们模拟一个固定标识),如 <identity impersonate="true" userName="WebUser" password="Y0urStr0ngPassw0rd$"/>£¬那么帐户(在这里是 WebUser)和进程都需要读访问权限。 如果您的基本代码使用通用命名约定 (UNC) 共享,您必须为 IIS 提供的 UNC 标记标识授予读访问权限。 如果您模拟但是没有使用显式凭据,如 <identity impersonate="true"/>£¬而且没有 UNC,那么在 .NET Framework 1.1 中只有进程需要访问权限。对于 .NET Framework 1.0,必须另外配置 ACL,授予任何将被模拟的标识读访问权限(也就是说,您必须授予原始调用方读访问权限)。 ASP.NET 中的信任级应用程序的信任级决定 CAS 策略授予它何种权限。这决定了多大程度上应用程序能够访问安全资源和执行特权操作。 <trust> 使用 <trust>元素配置应用程序的信任级。默认时,配置级设置为 Full,如下所示: <!-- level="[Full|High|Medium|Low|Minimal]" -->
<trust level="Full" originUrl=""/>
这意味着应用程序将被授予完全和无限的 CAS 权限。使用此配置,应用程序进行的任何资源访问的成败只取决于操作系统安全。 如果您将信任级更改为 Full 之外的其他级,可能破坏现有 ASP.NET Web 应用程序,具体情况取决于它们所访问的资源和它们所执行的操作的类型。应用程序应该彻底地在每个信任级进行测试。 有关构建使用 CAS 的部分信任 Web 应用程序的更多信息,请参阅“在 ASP.NET 中使用代码访问安全”单元。有关使用信任级提供应用程序隔离的更多信息,请参阅“寄宿多个 ASP.NET Web 应用程序”单元。 ASP.NET 的进程标识ASP.NET Web 应用程序和 Web 服务运行在共享的 ASP.NET 辅助进程 (Aspnet_wp.exe) 实例中。进程级设置,包括进程标识,是使用 Machine.config 中的 <processModel> 元素配置的。 <processModel> ASP.NET 辅助进程的标识是使用 <processModel> 元素的 userName 和 password 属性配置的。当配置进程标识时,应该:
使用默认 ASPNET 帐户 特别对于运行 ASP.NET Web 应用程序和 Web 服务来说,本地 ASPNET 帐户是默认最低特权帐户。如果能够,就通过使用以下默认配置使用此帐户: <processModel enable="true" userName="machine" password="AutoGenerate" ... />
使用最低特权自定义帐户 如果您必须使用另一个标识运行 ASP.NET 辅助进程,确保所使用的帐户配置为最低特权帐户。这限制了设法使用进程安全上下文执行代码的攻击者所能造成的破坏。 您可能因为需要使用 Windows 身份验证连接远程Microsoft SQL Server_ 数据库或者网络资源,而决定使用另一个帐户。注意您可以将本地 ASPNET 帐户用于此目的。有关更多信息,请参阅本单元后面的“数据访问”。 有关 ASP.NET 进程帐户要求的 NTFS 权限的更多信息,请参阅本单元后面的“ACL 和权限”。 您还应该给 ASP.NET 进程帐户授予以下用户权限:
加密凭据 如果您需要使用自定义帐户,不要在 Machine.config 中存储明文凭据。使用 Aspnet_setreg.exe 实用工具在注册表中存储加密凭据。 加密<processModel> 凭据
有关更多信息,请参阅 Microsoft 知识库文章 329290“如何:使用 ASP.NET 实用工具加密凭据和会话状态连接字符串”。 不以 SYSTEM 运行 ASP.NET 不要使用 SYSTEM 帐户运行 ASP.NET,不要授予 ASP.NET 进程帐户“Act as part of the operating system”用户权限。如果这样做将违反最低特权的原则,能够使用 Web 应用程序的进程安全上下文执行代码的攻击者所带来的破坏将增加。 模拟默认时,ASP.NET 应用程序并不模拟。在应用程序访问 Windows 资源的时候将使用 ASP.NET 辅助进程帐户(默认时是 ASPNET)的安全上下文。 <identity> <identity> 元素用于启用模拟。您可以模拟:
模拟原始调用方 要模拟原始调用方,使用以下配置: <identity impersonate="true" />
模拟使用 IIS 提供的表示经身份验证的调用方的访问标记。这可以是匿名 Internet 用户帐户(例如,如果您的应用程序使用窗体身份验证),也可以是表示原始调用方的 Windows 帐户(如果您的应用程序使用 Windows 身份验证)。 如果您确实启用了原始调用方模拟,注意以下问题:
有关更多信息,请参阅“如何:为 Windows 2000 实现 Kerberos 委托”,在“ Microsoft patterns & practices 第 I 卷,构建安全的 ASP.NET Web 应用程序:身份验证、授权和安全通讯”的“如何……”部分,网址是:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/SecNetHT05.asp。 模拟固定标识 要模拟固定标识,使用 userName 和 <identity> 元素的密码属性指定标识: <identity impersonate="true" userName="MyServiceAccount"
password="Str0ng!Passw0rd"/>
不要像这样以明文存储凭据。相反,使用 Aspnet_setreg.exe 工具加密凭据并将它们存储在注册表中。 加密<identity>凭据
有关更多信息,请参阅 Microsoft 知识库文章 329290,“如何:使用 ASP.NET 实用工具加密凭据和会话状态连接字符串”。 Act as Part of the operating system 权限 当您通过指定 userName 和 password 属性模拟固定标识时,ASP.NET 1.0 版进程帐户要求 Windows 2000 上的“Act as part of the operating system”用户权限。因为这实际上将提升 ASP.NET 进程帐户到接近本地 System 帐户的特权级,模拟固定标识在 ASP.NET 1.0 版中是不推荐的。 注 如果您是在 Windows 2000 或者 Windows 2003 Server 上运行 ASP.NET 1.1 版,这种用户权限是不需要的。 NTFS 权限需求 NTFS 权限必须为模拟标识进行适当的配置。有关更多信息,请参阅本单元后面的“ACL 和权限”。 身份验证<authentication>元素能够配置应用程序使用的身份验证模式。 <authentication> 适当的身份验证模式取决于您的应用程序或者 Web 服务如何设计。默认 Machine.config 设置将应用安全 Windows 身份验证默认值,如下所示。 <!-- authentication Attributes:
mode="[Windows|Forms|Passport|None]" -->
<authentication mode="Windows" />
窗体身份验证准则 要使用窗体身份验证,设置 <authentication> 元素的 mode="Forms"。接下来,使用 <forms> 子元素配置窗体身份验证。以下片段显示了一个安全的 <forms> 身份验证元素配置: <authentication mode="Forms">
<forms loginUrl="Restricted\login.aspx" Login page in an SSL protected folder
protection="All" Privacy and integrity
requireSSL="true" Prevents cookie being sent over http
timeout="10" Limited session lifetime
name="AppNameCookie" Unique per-application name
path="/FormsAuth" and path
slidingExpiration="true" > Sliding session lifetime
</forms>
</authentication>
使用以下推荐实践提高窗体身份验证安全性:
为 Web 站点分区 将 Web 站点的公共和受限访问区域分离。将应用程序的登录页和其他页,以及应该只由经过身份验证的用户访问的资源置于与公共访问区域不同的文件夹中。通过在 IIS 上配置以要求 SSL 访问保护受限子文件夹,然后使用 <authorization>元素限制访问并强制登录。例如,以下 Web.config 配置允许任何人访问当前目录(这能够提供公共访问),但是要防止未身份验证的用户访问受限子文件夹。任何这样做的企图都将强制窗体登录。 <system.web>
<!-- The virtual directory root folder contains general pages.
Unauthenticated users can view them and they do not need
to be secured with SSL. -->
<authorization>
<allow users="*" />
</authorization>
</system.web>
<!-- The restricted folder is for authenticated and SSL access only. -->
<location path="Restricted" >
<system.web>
<authorization>
<deny users="?" />
</authorization>
</system.web>
</location>
更多编程方面的注意事项,如如何在受限和非受限页之间导航,请参阅“构建安全的 ASP.NET Web 页和控件”单元中的“窗体身份验证”。 设置 Protection="All" 这个设置能够确保窗体身份验证 cookie 加密,从而提供私密性和完整性。用于 cookie 加密的密钥和算法是在 <machineKey> 元素上指定的。 加密和完整性检查能够防止 cookie 篡改,虽然如果攻击者设法捕获 cookie 的话,它们并不能降低 cookie 重放攻击的风险。还应该使用 SSL 防止攻击者通过使用网络监视软件捕获 cookie。尽管使用了 SSL,cookies 还是能够通过跨站点脚本 (XSS) 攻击窃取。应用程序必须通过适当地输入验证策略,采取足够防范措施,以降低这种风险。 使用小的 cookie 超时值 使用小的超时值限制会话生存期,并减少 cookie 重放攻击的可能性。 考虑使用固定的到期时间 考虑设置 <forms> 元素的 slidingExpiration="false" 以修改 cookie 到期时间,不要在每次 Web 请求之后重新设置到期时间。如果您不使用 SSL 保护 cookie 的话,这是很重要的。 注 此功能在 .NET Framework 1.1 版中提供。 在窗体身份验证中使用 SSL 使用 SSL 保护凭据和身份验证 cookie。SSL 能够防止攻击者捕获凭据或者用来向应用程序标识您的窗体身份验证 cookie。窃取的身份验证 cookie 是一个窃取的登录。 设置 requireSSL="true"这将设置 cookie 中的 Secure 属性,从而确保 cookie 不会从浏览器通过 HTTP 链路传输到服务器。HTTPS (SSL) 是必需的。 注 这是一个 .NET Framework 1.1 版设置。在 1.0 版上构建的应用程序中,设置 cookie Secure属性是通过显式的编程进行的。有关更多信息和示例代码,请参阅“构建安全的 ASP.NET Web 页和控件”单元。 如果您不使用 SSL,设置 slidingExpiration = "false" 通过将 slidingExpiration 设置为 false,可以将 cookie 超时时间修改为距离 cookie 最初创建时的分钟数。否则,超时将在每次向 Web 服务器请求时更新。如果 cookie 被捕获了,将为攻击者提供所需要的足够时间以经过身份验证的用户标识访问应用程序。 注 此功能在 .NET Framework 1.1 版中提供。 不要在产品服务器上使用元素 在 XML 配置文件中存储用户凭据的能力是为了支持快速开发和有限测试而提供的。不要使用实际的最终用户凭据。最终用户凭据不应该存储在产品服务器上的配置文件中。产品应用程序应该实现自定义用户凭据存储区,例如,在一个 SQL Server 数据库中。 配置 machineKey <machineKey> 元素定义了用来加密窗体身份验证 cookie 的加密算法。此元素还保留着加密密钥。有关更多信息,请参阅本单元中的“机器密钥”部分。 使用唯一的 cookie 名称和路径 使用唯一的 name 和 path 属性值。通过确保名称的唯一,能够防止在同一个服务器上寄宿多个应用程序时可能出现的问题。 授权除非用户有显式的访问某个资源的权限,如一个特定的 Web 页、一个资源文件、一个目录等等,配置默认时应该拒绝访问。ASP.NET 提供了两个可配置的网关守卫,可以用来控制对受限资源的访问。这两个网关守卫是:
文件授权 只有使用 Windows 身份验证和具有以下配置的应用程序,才能够使用这个网关守卫: <authentication mode="Windows"/>
这个网关守卫在使用 Windows 身份验证和无需模拟的情况下,自动生效。为配置此网关守卫,需要配置文件和文件夹上的 Windows ACL。请注意网关守卫只控制对 IIS 映射到以下ASP.NET ISAPI 扩展的文件类型的访问:Aspnet_isapi.dll。 URL 授权 任何应用程序都能够使用这个网关守卫。它是通过 <authorization> 元素(它控制着哪些用户和哪些用户组应该访问应用程序)来配置的。来自 Machine.config 的默认元素如下所示: <authorization>
<!-- allow/deny Attributes:
users="[*|?|name]"
* - All users
? - Anonymous users
[name] - Named user
roles="[name]" -->
<allow users="*"/>
</authorization>
URL 授权注意事项 使用以下措施有助于成功地配置 URL 授权:
会话状态依赖于每个用户会话状态的应用程序可以将会话状态存储在以下位置:
<sessionState> 相关的位置,与连接详细信息一起,存储在 Machine.config 中的 <sessionState> 元素中。这是默认设置: <sessionState mode="InProc"
stateConnectionString="tcpip=127.0.0.1:42424"
stateNetworkTimeout="10" sqlConnectionString="data
source=127.0.0.1;Integrated Security=SSPI"
cookieless="false" timeout="20"/>
注 如果您不使用 Web 服务器上的 ASP.NET 状态服务,使用 MMC 服务管理单元禁用它。 保护 SQL Server 会话状态存储区 如果您使用 SQL Server 会话状态存储区,使用以下推荐实践以辅助会话状态的保护:
有关设置 SQL Server 会话状态存储区数据库的更多信息,请参阅 Microsoft 知识库文章 311209,“How To:Configure ASP.NET for Persistent SQL Server Session State Management”。 对数据库使用 Windows 身份验证 如果使用 mode="SQLServer",应该使用 Windows 身份验证连接状态数据库,而且应该使用最低特权帐户,如重复的本地 ASPNET 帐户。这意味着您可以使用可信的连接,连接字符串中不再有凭据,而且凭据不再通过网络传递给数据库。 加密 sqlConnectionString 使用 Aspnet_setreg.exe 工具加密 sqlConnectionString 属性值。如果您使用SQL 身份验证连接状态数据库,这一点尤其重要,因为凭据在连接字符串中,但是即使您使用 Windows 身份验证,也仍然推荐这样做。 加密 sqlConnectionString
限制数据库中的应用程序登录 数据库中的应用程序登录应该被限制,使其只能用来访问必要的状态表和 ASP.NET 用来查询数据库的存储过程。 要限制状态数据库中的应用程序登录
保护信道 要保护 Web 服务器的安全和远程状态存储区之间在网络上传输的敏感会话状态,应该使用 IPSec 或者 SSL 保护到两个服务器的信道。这能够为网络上的会话状态数据提供私密性和完整性。如果使用 SSL,则您必须在数据库服务器上安装服务器证书。有关在 SQL Server 中使用 SSL 的更多信息,请参阅“保护数据库服务器”单元。 保护进程外状态服务 如果您使用 mode=StateServer£¬使用以下推荐实践辅助保护会话状态:
使用最低特权帐户运行状态服务 状态服务默认时使用 ASPNET 本地最低特权帐户运行。您应该不需要更改这一配置。 保护信道 如果状态服务位于远程服务器上,应该使用 IPSec 以确保用户状态保持私密和未经修改,保护到远程状态存储区的信道。 考虑更改默认端口 ASP.NET 状态服务侦听端口 42424。要避免使用这个默认的、众所周知的端口,您可以通过编辑以下注册表项更改端口: HKLM\SYSTEM\CurrentControlSet\Services\aspnet_state\Parameters
端口号是通过 Port 命名值定义的。如果您更改注册表中的端口号,例如,改为 45678,还必须更改 <sessionState> 元素的连接字符串,如下所示: stateConnectionString="tcpip=127.0.0.1:45678"
加密 stateConnectionString 加密 stateConnectionString 属性值可以隐藏状态存储区的 IP 地址和端口号。使用 Aspnet_setreg.exe 工具。 加密 stateConnectionString
查看状态如果您的应用程序使用查看状态,确保它使用消息身份验证码 (MAC) 进行了保护,确保没有在客户端更改。可使用 Machine.config 中的 <pages> 元素为机器上所有应用程序启用或者禁用查看状态和 MAC 保护。 <pages> 默认时,Machine.config 中 <pages> 元素的 enableViewStateMac 属性能够确保查看状态使用 MAC 进行了保护。 <pages buffer="true" enableSessionState="true"
enableViewState="true" enableViewStateMac="true"
autoEventWireup="true" validateRequest="true"/>
如果您使用查看状态,确保 enableViewStateMac 设置为 true。<machineKey> 元素定义了用来保护查看状态的算法。 机器密钥<machineKey> 元素是用来指定保护窗体身份验证 cookie 和页级查看状态的加密密钥、验证密钥和算法的。以下代码示例显示了来自 Machine.config 的默认设置: <machineKey validationKey="AutoGenerate,IsolateApps"
decryptionKey="AutoGenerate,IsolateApps" validation="SHA1"/>
在配置 <machineKey> 时考虑以下推荐实践:
在多个应用程序中使用唯一加密密钥 如果您在一个 Web 服务器上寄宿多个应用程序,机器上的每个应用程序都使用唯一密钥,而不是对所有应用程序都使用一个密钥。这消除了一个应用程序能够欺骗寄宿环境中查看状态或者已加密窗体身份验证 cookie 的可能性。 还需要使用 IsolateApps 设置。这是一个新的 .NET Framework 1.1 版设置,指示 ASP.NET 自动生成加密密钥,使每个应用程序都有唯一的密钥。 设置 validation="SHA1" validation 属性指定了用于完整性检查和页级查看状态的算法。可能的值是“SHA1,”、“MD5,”和“3DES”。 如果您使用 <forms> 元素上的 protection="All",则窗体身份验证 cookie 将加密,这也能确保完整性。无论 validation 属性设置如何,窗体身份验证使用 TripleDES (3DES) 加密 cookie。 注 窗体身份验证 cookie 加密与 validationkey 设置无关,而且密钥是以 decryptionKey 属性为基础的。 如果您设置 <machineKey> 上的 validation="SHA1",则应该使用 SHA1 算法对页级查看状态进行完整性检查,假定 <pages> 元素为查看状态 MAC 进行了配置。有关更多信息,请参阅本单元前面的“查看状态”。 您还可以设置验证属性为 MD5。您应该使用 SHA1,因为它能够生成比 MD5 更大的散列,所以可以认为更加安全。 如果您设置 <machineKey> 的 validation="3DES" £¬然后使用 3DES 算法加密页级查看状态(还提供完整性检查),即使 <pages> 元素为查看状态 MAC 进行了配置。 为 Web 服务器场手工生成密钥 在 Web 服务器场中,必须设置显式密钥值,且 Web 服务器场中所有机器都使用同样的密码。请参阅本单元后面的“Web 服务器场注意事项”。 调试<compilation> 元素控制着用于动态页编译的编译器设置,这是在客户端请求 Web 页(.aspx 文件)或者 Web 服务(.asmx 文件)时启动的。不要在产品服务器上使用调试版本,这一点很重要,因为调试信息对于攻击者很有价值,可能暴露源代码详细信息。 <compilation> 此元素控制着编译进程。确保产品服务器上禁用调试编译。如下设置 debug="false": <compilation debug="false" explicit="true" defaultLanguage="vb" />
默认时,临时文件在以下目录创建和编译: %winnt%\Microsoft.NET\Framework\{version}\Temporary ASP.NET Files
您可以使用 tempDirectory 属性指定每个应用程序的位置,虽然这不能提供安全优势。 注 <processModel> 元素指定的 ASP.NET 进程标识要求临时编译目录的完全控制访问权限。 千万不要将调试文件(带有 .pdb 扩展名)存储在程序集所在的产品服务器上。 跟踪跟踪不应该在产品服务器上启用,因为系统级跟踪信息能够极大地帮助攻击者分析应用程序和探查弱点。 <trace> 跟踪是使用 <trace> 元素配置的。在产品服务器上设置 enabled="false",如下所示: <trace enabled="false" localOnly="true" pageOutput="false"
requestLimit="10" traceMode="SortByTime"/>
如果您确实需要跟踪活动应用程序的问题,在测试环境中模拟问题更好,或者如果必要,可以启用跟踪并设置 localOnly="true",以防止跟踪详细信息返回远程客户端。 异常管理不要允许异常详细信息从您的 Web 应用程序传播回客户端。恶意用户可能使用系统级诊断信息了解您的应用程序,并探查弱点,供未来攻击中利用。 <customErrors> <customErrors> 元素可以用来配置自定义的、一般性错误消息,这类消息将在应用程序出现异常情形时返回客户端。错误页应该包括一个合适的一般性错误消息,还有可选的更多支持详细信息。您还可以使用此元素根据异常情形返回不同的错误页。 确保模式属性设置为 "On",而且已经指定了默认的重定向页,如下所示: <customErrors mode="On" defaultRedirect="YourErrorPage.htm" />
defaultRedirect 属性使您能够在应用程序中使用自定义错误页,例如可以包括支持联系人详细信息。 注:不要使用 mode="Off",因为它会导致包含系统级信息的详细的错误页被返回给客户端。 如果您想不同类型的错误有不同的错误页,使用一个或者更多 <error> 元素,如下所示。在此例中,“404 (not found)”错误重定向到一个页,“500 (internal system errors)”重定向到另一页,而所有其他错误都定向到 defaultRedirect 属性指定的页。 <customErrors mode="On" defaultRedirect="YourErrorPage.htm">
<error statusCode="404" redirect="YourNotFoundPage.htm"/>
<error statusCode="500" redirect="YourInternalErrorPage.htm"/>
</customErrors>
远程处理不要公开面向 Internet 的 Web 服务器上的 .NET 远程处理终结点。要禁用远程处理,需要通过将这些文件扩展名的请求映射到 HttpForbiddenHandler 禁用 .rem 和 .soap 扩展名的请求。使用 <httpHandlers> 下的以下元素: <httpHandlers>
<add verb="*" path="*.rem" type="System.Web.HttpForbiddenHandler"/>
<add verb="*" path="*.soap" type="System.Web.HttpForbiddenHandler"/>
. . .
</httpHandlers>
注 这不能防止 Web 服务器上的 Web 应用程序通过使用远程处理基础结构连接下游对象。但是,它可防止客户端能够连接 Web 服务器上的对象。 Web 服务使用 <webServices> 元素配置 Web 服务。要建立安全的 Web 服务配置:
如果 Web 服务不是必需的就将其禁用 如果您不使用 Web 服务,通过将 .asmx(Web 服务)文件扩展名的请求映射到 Machine.config 中的 HttpForbiddenHandler 禁用它们,如下所示: <httpHandlers>
<add verb="*" path="*.asmx" type="System.Web.HttpForbiddenHandler"/>
. . .
</httpHandlers>
禁用未用的协议 <protocols> 元素定义了 Web 服务支持的协议。默认时,HttpPost 和 HttpGet 在 .NET Framework 1.1 版上禁用,如下所示: <webServices>
<protocols>
<add name="HttpSoap1.2"/>
<add name="HttpSoap"/>
<!-- <add name="HttpPost"/> -->
<!-- <add name="HttpGet"/> -->
<add name="HttpPostLocalhost"/>
<add name="Documentation"/>
</protocols>
</webServices>
通过禁用不必要的协议,包括 HttpPost 和 HttpGet£¬可以减少受攻击面。例如,外部攻击者可能通过在电子邮件中嵌入一个恶意的链接,从而使用最终用户的安全上下文执行内部 Web 服务。禁用 HttpGet 协议是一种有效的对策。在许多方面,这与 XSS 攻击是类似的。这种攻击的变体是使用一个 <img src="..." /> 标记在能够公开访问的 Web 页中嵌入对 intranet Web 服务的 GET调用。这两种攻击都允许外人调用内部的 Web 服务。禁用协议可以降低风险。 如果您的产品服务器提供了可以公开发现的 Web 服务,则必须启用 HttpGet 和 HttpPost 以允许服务通过这些协议被发现。 禁用 WSDL 的自动生成 文档协议是用来动态生成 Web 服务描述语言 (WSDL) 的。WSDL 说明了一个 Web 服务的特性,如它的方法签名和所支持的协议。客户端使用这种信息适当地构造格式化的消息。默认时,Web 服务公开 WSDL,这能够使它为任何能够通过 Internet 连接 Web 服务器的人所访问。 有时候,您可能需要手工将 WSDL 文件分发给您的合作伙伴并防止公开访问。通过这种方式,开发小组能够提供每个 Web 服务的单独 .wsdl 文件给操作小组。操作小组然后能够将它们分发给想使用这些 Web 服务的指定合作伙伴。 要禁用文档协议,在 Machine.config 中将其注释掉,如下所示: <webServices>
<protocols>
<add name="HttpSoap"/>
<!-- <add name="Documentation"/> -->
</protocols>
</webServices>
禁止的资源为了防止受保护的资源和文件通过 HTTP 下载,将它们映射到 ASP.NET HttpForbiddenHandler. 将受保护资源映射到 HttpForbiddenHandler HTTP 处理程序位于 <httpHandlers> 元素之下的 Machine.config 中,。HTTP 处理程序负责处理特定文件扩展名的 Web 请求。远程处理不应该在前端 Web 服务器上启用;只应该在与 Internet 隔离的中间层应用程序服务器上启用远程处理。
对于 .NET Framework 资源,如果您不使用文件扩展名,那么应该将扩展名映射到 Machine.config 中的 System.Web.HttpForbiddenHandler£¬如下例所示: <add verb="*" path="*.vbproj" type="System.Web.HttpForbiddenHandler" />
在这种情况下,.vbproj 文件扩展名映射到 System.Web.HttpForbiddenHandler如果客户端请求的路径以 .vbproj 结束,那么 ASP.NET 将返回一条消息,说明“This type of page is not served”。
Bin 目录ASP.NET 应用程序的虚拟根目录下的 Bin 目录包含应用程序的私有程序集,如果在开发期间使用了代码隐藏文件的话,还包括应用程序的页类实现。 保护 Bin 目录 要保护应用程序的 Bin 目录,并保护您的业务逻辑免遭无意的下载:
删除 Web 权限 使用 IIS 管理单元,确保 Bin 目录没有 Read£¬、Write£¬或者 Directory 浏览权限。还要确保 Execute 权限设置为 None. 删除所有身份验证设置 使用 IIS 管理单元从 Bin 目录删除身份验证设置。这将导致所有访问被拒绝。 事件日志最低特权帐户,如 ASPNET,有足够权限,能够使用现有事件源向事件日志中写入记录。但是,它们没有足够权限创建新的事件源。要实现这一点,您必须在以下注册表项下放置新的项: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\
要避免这一问题,您可以在安装时有管理员特权时创建事件源。您可以使用 .NET 安装程序类,它可以由 Windows 安装程序(如果您使用 .msi 部署)或者由 InstallUtil.exe 系统实用工具(如果您没有使用 .msi 部署)实例化。有关使用事件日志安装程序的更多信息,请参阅“构建安全的 ASP.NET Web 页和控件”单元。 如果您不能在安装时创建事件源,必须在以下注册表项中添加权限,并授予 ASP.NET 进程帐户或者任何模拟帐户(如果您的应用程序使用模拟)访问权限。 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog
最低限度,这类帐户必须有以下权限:
文件访问应用程序所访问的任何文件的 ACL 中,必须有一个访问控制项 (ACE) 至少授予 ASP.NET 进程帐户或者模拟标识读访问权限。通常,ACL 在目录上配置,文件将继承设置。 除了使用 NTFS 权限限制文件和目录访问外,您还可以使用 ASP.NET 信任级对 Web 应用程序和 Web 服务实施约束,以限制它们能够访问文件系统的哪些区域。例如,中度信任的 Web 应用程序只能访问它们自己虚拟目录层次中的文件。 有关 ASP.NET CAS 策略的更多信息,请参阅“在 ASP.NET 中使用代码访问安全”单元。 ACL 和权限ASP.NET 进程帐户,对于一些目录而言,还包括任何模拟标识(如果您的应用程序使用模拟的话)要求以下 NTFS 权限。表 3 中列出了除了应用程序访问特定于应用程序的文件系统资源所需的任何权限之外,还应该使用的权限。
注册表应用程序访问的任何注册表项的 ACL 中,必须有一个 ACE 至少授予 ASP.NET 进程帐户或者模拟标识读访问权限。 数据访问为了从 ASP.NET 应用程序使用 Windows 身份验证访问远程数据库,有以下选择:
为 ASP.NET 应用程序配置数据访问 无论使用何种方法,都应该限制数据库中应用程序的帐户。要实现这一点,为帐户创建一个 SQL Server 登录,授予它访问必需的数据库的权限,并限制其权限,从而使其只能访问最少的必需数据库对象。理想情况下,您应该限制权限从而使登录只能访问应用程序或者 Web 服务使用的存储过程。 以下过程假设您使用一个镜像本地帐户,但是也可以通过一个域帐户使用同样的方法限制数据库中帐户的功能。 为 ASP.NET 应用程序配置数据库访问
UNC 共享ASP.NET 应用程序可以使用 UNC 共享的两个主要方式是:
访问 UNC 共享上的文件 如果您的应用程序访问 UNC 共享上的文件,ASP.NET 进程帐户或者任何模拟标识必须有适当的访问权限(由共享上和基础目录或者文件上的 ACL 所定义)。 如果您使用本地 ASPNET 进程帐户,则没有网络标识,因此必须使用一个匹配的用户名和密码在远程服务器上创建一个镜像帐户,或者您必须使用有两个服务器访问权限的最低特权域帐户。在 Windows Server 2003 上,用来运行 ASP.NET Web 应用程序的 NetworkService 帐户可以在网络上进行身份验证,因此所有要做的就是授予机器帐户访问权限。 在 UNC 共享上寄宿应用程序 您可以使用 IIS 配置虚拟目录指向位于另一个计算机上的 UNC 共享,例如 \\remoteserver\appname。当这样做的话,IIS 将提示您提供帐户凭据,使用这一凭据建立与远程计算机的连接。 注 帐户凭据以加密格式存储在 IIS 元数据库中,但是能够通过 API 访问。您应该确保使用最低特权帐户。有关更多信息,请参阅 Microsoft 知识库文章 280383,“IIS Security Recommendations When You Use a UNC Share and UserName and Password Credentials”。 如果您的应用程序位于一个 UNC 共享上,ASP.NET 模拟 IIS 提供的 UNC 标记(从您提供给 IIS 的帐户凭据创建)来访问共享,除非您已经启用模拟,使用一个固定模拟标识,如以下配置所示: <identity impersonate="true"
userName="registry:HKLM\SOFTWARE\YourApp\identity\ASPNET_SETREG,userName"
password="registry:HKLM\SOFTWARE\YourApp\identity\ASPNET_SETREG,password"/>
如果通过 userName 和 password 属性提供一个固定模拟帐户,ASP.NET 应该使用该帐户而不是 IIS UNC 标记来访问共享。应用程序所进行的任何资源访问也使用固定模拟帐户。 注 上例中,Aspnet_setreg.exe 用来在注册表中存储加密帐户凭据。 如果您通过使用以下配置启用对原始调用方(经 IIS 身份验证的标识)的模拟,ASP.NET 仍然使用 UNC 提供的标记访问共享中应用程序的文件,虽然应用程序进行的任何资源访问都使用模拟标记。 <identity impersonate="true" />
注 用于 UNC 共享的帐户必须还能够读取 Machine.config。 代码访问安全注意事项 代码访问安全策略授予 UNC 共享上的应用程序 intranet 权限集。intranet 权限集不包含 AspNetHostingPermission£¬而这是 ASP.NET Web 应用程序运行所要求的,因此应用程序如果不显式修改策略就无法运行。 这时您有两种选择:
COM/DCOM 资源应用程序在调用基于 COM 的资源如服务组件时,需要使用进程或者模拟标识。客户端身份验证和模拟级是使用 Machine.config 中 <processModel> 元素的 comAuthenticationLevel 和 comImpersonation 级别属性配置的。 有关更多信息和推荐实践,请参阅“保护应用程序服务器的安全”单元中的“企业服务注意事项”。 拒绝服务注意事项ASP.NET 具有以下功能,有助于对抗以 ASP.NET 应用程序为目标的拒绝服务攻击:
<httpRuntime> 配置值在 Machine.config 的 <httpRuntime> 元素中维护。以下代码示例显示了来自 1.1 版 Machine.config 的默认设置: <httpRuntime executionTimeout="90"
maxRequestLength="4096"
useFullyQualifiedRedirectUrl="false"
minFreeThreads="8"
minLocalRequestFreeThreads="4"
appRequestQueueLimit="100"
enableVersionHeader="true"/>
可能需要减少 maxRequestLength 属性的值以防止用户上传非常大的文件。最大允许值是 4 MB。在 Open Hack 竞赛中,maxRequestLength 约束为 1/2 MB,如在下例所示: <system.web>
<!-- 1/2 MB Max POST length -->
<httpRuntime maxRequestLength="512"/>
</system.web>
注 ASP.NET 不能应对数据包级攻击。您必须通过加固 TCP/IP 堆栈解决这一问题。有关配置 TCP/IP 堆栈的更多信息,请参阅本指南的“如何……”部分中的“如何加固 TCP/IP 堆栈”。 Web 服务器场注意事项如果您的 ASP.NET Web 应用程序运行在 Web 服务器场中,将无法保证来自同一客户端的后续请求会由同一台 Web服务器服务。这对以下方面都有影响:
会话状态 要避免服务器关系,应该在 ASP.NET SQL Server 状态数据库中维护进程外 ASP.NET 会话状态,或者在运行在远程机器上的进程外状态服务中维护它。有关在远程状态存储区中保护会话状态的更多信息,请参阅本文档前面的“会话状态”部分。 加密和验证 用来加密和验证窗体身份验证 cookie和查看状态的密钥,在 Web 服务器场中所有服务器必须是一样的。<machineKey> 元素的 AutoGenerate 设置必须用公共的密钥值代替。 有关生成和配置密钥的更多信息,请参阅 Microsoft 知识库文章 312906,“How To:Create Keys by Using Visual C# .NET for Use in Forms Authentication”。 DPAPI 为了加密数据,开发人员有时会使用 DPAPI。如果您使用带机器密钥的 DPAPI 存储机密,则已加密的字符串是特定于给定计算机的,不能在 Web 服务器场或者群集中的计算机复制已加密数据。 如果您使用带用户密钥的 DPAPI,可以解密任何带有漫游用户配置文件的计算机上的数据。但是,因为数据可以被网络上任何能够使用加密数据的帐户执行代码的机器解密,这种做法是不推荐的。 DPAPI 理想情况下适合用于存储配置机密,例如,Web 服务器上的数据库连接字符串。在已加密数据存储在远程服务器例如在数据库中时,应该使用其他加密技术。有关在数据库中存储加密数据的更多信息,请参阅“构建安全的数据访问”单元。 安全 ASP.NET 应用程序的快照以下快照显示了安全 ASP.NET 应用程序的属性,可以用来快速和容易地将此设置与您自己的配置进行比较。
小结本单元通过将注意力集中在一些配置类别(包括在 Machine.config 和 Web.config 文件中维护的帐户、服务、协议、文件和目录和配置数据)上,说明了如何保护 ASP.NET Web 应用程序或者 Web 服务的安全。本单元还说明了如何保护 ASP.NET Web 应用程序的安全和 Web 服务所依赖的各种功能区域,包括身份验证、授权、会话状态和数据访问。 有关相关的核对表,请参阅“核对表:保护 ASP.NET 的安全”。 其他资源有关更多信息,请参阅以下资源和文章:
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||




浙公网安备 33010602011771号