**摘要:**了解在使用 Microsoft SharePoint 2010 进行开发时的安全性最佳实践建议。
上次修改时间: 2015年3月9日
适用范围: Business Connectivity Services | Open XML | SharePoint Designer 2010 | SharePoint Foundation 2010 | SharePoint Online | SharePoint Server 2010 | Visual Studio
本文内容
SharePoint 的安全性最佳实践简介
SharePoint 安全性最佳实践:跨站点脚本
SharePoint 安全性最佳实践:跨站点请求伪造
SharePoint 安全性最佳实践:提升特权
SharePoint 安全性最佳实践:拒绝服务
SharePoint 安全性最佳实践:信息泄漏
结论
其他资源
**供稿人:**Matt Swann,Microsoft Corporation
目录
SharePoint 的安全性最佳实践简介
SharePoint 安全性最佳实践:跨站点脚本
SharePoint 安全性最佳实践:跨站点请求伪造
SharePoint 安全性最佳实践:提升特权
SharePoint 安全性最佳实践:拒绝服务
SharePoint 安全性最佳实践:信息泄漏
结论
其他资源
SharePoint 的安全性最佳实践简介
本文提供了您在使用 Microsoft SharePoint 2010 进行开发时需考虑的安全性最佳实践建议的列表。此列表说明了跨站点脚本 (XSS)、跨站点请求伪造(CSRF 或 XSRF)、提升特权、拒绝服务和信息泄漏的各个方面。
SharePoint 安全性最佳实践:跨站点脚本
跨站点脚本 (XSS) 攻击通过注入客户端脚本代码来攻击网页验证中的漏洞。导致您的 Web 应用程序易受跨站点脚本攻击的常见漏洞包括:无法适当验证输入、无法编码输出以及信任从共享数据库中检索到的数据。
以下各节描述了这些漏洞并提供了一些建议。有关 XSS 的更多详细信息,请参阅如何:阻止 ASP.NET 中的跨站点脚本(该链接可能指向英文页面)。
使用 SPHttpUtility 方法正确地编码输出
攻击者希望在您的网站上运行其 ECMAScript(JavaScript、JScript) 脚本,因为这将使他们能够窃取身份验证 Cookie,并诱使管理员执行恶意操作。
这些攻击者会查找未对用户输入进行正确编码的位置,这允许将用户输入解释为 HTML 或 JavaScript。例如,如果在客户端中呈现标记为 <script>alert()</script> 的列表项之前,未先使用 Microsoft.SharePoint.Utilities.SPHttpUtility.HtmlEncode 对该列表项进行 HTML 硬编码,则它将运行 JavaScript。
建议
在客户端上呈现用户数据之前,请使用 SPHttpUtility 类中的相应方法对该数据进行编码。用户数据的源包括:
SharePoint 对象模型中的数据(例如,字段值和列表标题)
查询字符串中的参数、标头、Cookie 或请求中的表单正文
Web 服务参数
您应使用的编码方法取决于您在客户端上使用数据的方式,如表 1 所示。
表 1. 用于编码的 SPHttpUtility 方法
类型 |
要使用的 Microsoft.SharePoint.Utilities.SPHttpUtility 类方法 |
|---|---|
HTML 标记内部文本 |
HtmlEncode |
HTML 标记内部文本,允许基本格式设置 |
HtmlEncodeAllowSimpleTextFormatting |
HTML 标记属性,非 URL |
HtmlEncode |
HTML 标记属性,URL |
HtmlUrlAttributeEncode |
JavaScript |
EcmaScriptStringLiteralEncode |
已知安全值(例如,查询参数的 GUID) |
NoEncode |
通过调用 SPHttpUtility.NoEncode,可帮助您审核源代码中的编码问题。一旦调用 SPHttpUtility.NoEncode,则表明您已考虑是否必须对此值进行编码。
是否能改用 Microsoft .NET Framework HttpUtility 编码器?
不能,您不应这样做。.NET Framework HttpUtility 编码库不会对所有字符进行完全编码。例如,SharePoint 中的 SPHttpUtility 将一个单引号编码为 ',而 .NET Framework HttpUtility 不对单引号进行编码。这将生成一个 XSS Bug,如以下代码所示。
Response.Write("<a href='http://contoso/" +
HttpUtility.HtmlAttributeEncode(Request.QueryString["target"]) +
"'>test</a>");
绝不允许参与者用户向网站添加脚本
SharePoint 2010 中的一些功能可能允许用户在网站上创作 JavaScript。例如:
内容编辑器 Web 部件可用来向页面中添加任意 JavaScript。
在下载时,一些文档类型可能会在浏览器中呈现为 HTML,这样便允许脚本运行。
不具有"添加和自定义网页"权限的用户将被视为不受信任,并且无法通过使用允许用户向网站添加脚本的这些功能向网站中添加脚本。具有"参与"权限级别的用户不具有此权限。
备注
有关 SharePoint 2010 中的权限的详细信息,请参阅第 11 章:用户和权限。
建议
如果您的功能允许用户向网站添加脚本,则您的功能必须检查当前用户的权限,并阻止不具有 SPBasePermissions.AddAndCustomizePages(该链接可能指向英文页面) 权限的用户,如以下代码所示。
if (!SPContext.Current.Web.DoesUserHavePermissions(SPBasePermissions.AddAndCustomizePages))
{
// User does not have permission to add script to the site.
// Either disable the feature or throw an access denied exception.
throw new UnauthorizedAccessException();
}
如果您的功能显示或下载用户上载的文档,则在返回文档时,它必须追加 X-Content-Type-Options: nosniff HTTP 响应标头。如果文档不必在浏览器中呈现,则您必须发送两个附加 HTTP 响应标头来阻止此情况发生。以下是要发送的 HTTP 响应标头:
X-Download-Options: noopen
Content-Disposition: attachment
总是在 Content-Type HTTP 响应标头中设置字符集
开发人员经常通过阻止包含特殊字符(例如,尖括号 < 和 >)的输入来防御 XSS。遗憾的是,如果攻击者可诱使浏览器将页面解释为 UTF-7 或其他字符集,则攻击者无需使用尖括号即可注入 <script> 标记。
建议
总是在 Content-Type HTTP 响应标头中指定字符集。通常,这应是 UTF-8 格式的,如以下示例所示。
Content-Type: text/html; charset=UTF-8
此字符集规范可阻止攻击者从页面内容中控制浏览器的字符集。
除了指定字符集外,最好是指定 nosniff 标头以阻止 MIME 探查。
样式和事件属性中不允许使用用户提供的值
虽然允许在样式和事件属性中使用用户提供的值是一个错误的做法,但此情况仍不时发生。我们建议您对用户提供的数据执行以下操作。
建议
不要在样式属性中反映用户提供的值。即使您使用 HTML 编码也不管用。使用 HTML 编码的缓解作用需视情况而定,如以下示例所示。
**示例 1:**虽然已对以下整个样式属性值进行了编码,但它仍运行脚本,因为所有浏览器 HTML 会先对属性值进行解码,然后再使用这些值。在此示例中,HTML 编码并不管用,并且没有用于减小其漏洞的合理方法。
<html>
<body>
<div style="w:expression(alert())"></div>
</body>
</html>
**示例 2:**对于 font-family,您可能只希望允许已确定安全的字体名称。对于 background-image,您可能需要验证所使用的协议处理程序,并需确保检查 URL 以验证是否发生了任何新样式标记注入。
不要在事件属性中反映用户提供的值。再次说明,即使您使用 HTML 编码也不管用。使用 HTML 编码的缓解作用需视情况而定。
相反,您应使用 JavaScript 编码,并且在一些情况下,会对用户提供的数据进行两次编码。以下是一些示例。
**示例 1:**对于以下内容,JavaScript 编码已足够。
<a href="#" onclick="alert(__USER_PROVIDED_DATA__)">
**示例 2:**对于以下内容,应依次对数据进行 HTML 编码和 JavaScript 编码。
<a href="#" onclick="document.write(__USER_PROVIDED_DATA_)">
**示例 3:**以下内容本来就是易受攻击的。没有用于减小其漏洞的合理方法。
<a href="#" onclick="__USER_PROVIDE_DATA__">
SharePoint 安全性最佳实践:跨站点请求伪造
跨站点请求伪造(CSRF 或 XSRF)是一个攻击,它会诱使受害人的浏览器代表受害人执行不需要的操作。例如,可利用此类攻击来转帐、更改密码或购买物品。
有关 CSRF 的详细信息,请参阅跨站点请求伪造攻击说明(该链接可能指向英文页面)。
在处理回发之前验证格式摘要 Canary
攻击者可在 SharePoint 中发布页面,即使其无法在域中运行脚本。如果您浏览到由攻击者所有的页面,则攻击者可使用您的凭据在 SharePoint 上执行操作。
例如,攻击者可以控制 http://contoso123 上的页面。当您浏览器到其页面时,攻击者将使用您的凭据发布到 http://wingtip/_layouts/deleteweb.aspx。如果您具有 http://wingtip 上的管理员权限,则这将删除 http://wingtip 网站。
建议
SharePoint 使用动态 canary 以确保 POST 请求来自与服务器相同的域。
您必须执行以下两个操作:
将 canary 与每个回发或 Web 服务请求一起发送。
在执行回发或 Web 服务请求之前验证 canary。
以下是要执行的步骤。
将 canary 与每个回发或 Web 服务请求一起发送。
Canary 值已显示在使用 SharePoint 母版页的每个页面上隐藏的 __REQUESTDIGEST 表单元素中。它自动随每个回发一起发送。
如果您不从 SharePoint 母版页继承,则必须使用 Microsoft.SharePoint.WebControls.FormDigest 控件以将值写入页面中。如果您要发出 Web 服务调用,则必须获取 __REQUESTDIGEST 值并将其包含在 X-RequestDigest HTTP 请求标头中,如以下示例所示。
var canaryValue = document.getElementById('__REQUESTDIGEST').value; request.SetRequestHeader("X-RequestDigest", canaryValue);浏览器外部的客户端应用程序必须通过调用 Sites.asmx SOAP Web 服务上的 GetUpdatedFormDigestInformation(该链接可能指向英文页面) Web 方法来手动获取 canary 值。此值应包含在任何 Web 服务调用的 X-RequestDigest 标头中。
备注
Canary 值将在 30 分钟后超时(它是可配置的,并由 GetUpdatedFormDigestInformation 返回),因此,如果服务器因该值太旧而将其拒绝,则客户端应用程序必须准备重新请求一个有效的 canary。浏览器中不会出现此问题,因为 FormDigest 控件会使用 JavaScript 适当地刷新 __REQUESTDIGEST 表单值。
在执行回发或 Web 服务请求之前验证 canary。
在执行作为回发或 Web 服务调用的结果的任何操作之前,您必须调用 ValidateFormDigest() 以验证 canary 是否有效。如果 canary 无效,则将引发一个异常。
备注
这包括非状态更改回发;例如,使用用户提交的内容刷新页面。
避免使用 AllowUnsafeUpdates(如果可能)
SharePoint 2010 阻止开发人员对 GET 请求执行状态更改操作。例如,在使用 GET 获取列表项或 Web 属性时,不允许 Microsoft ASP.NET 页更新列表项或 Web 属性的内容。
与 __REQUESTDIGEST canary 结合使用可阻止 CSRF 攻击。但是,有些功能可能需要执行迟缓初始化。
建议
如果您的功能必须对 GET 请求执行状态更改操作,则您应首先考虑该功能是否能正常运行。仅对 POST 请求执行状态更改操作更可取,因为 __REQUESTDIGEST canary 可保护您的功能免受 CSRF 攻击。
如果您的功能设计强制对 GET 请求执行状态更改操作,则您可通过将当前 Microsoft.SharePoint.SPWeb 类的 AllowUnsafeUpdates 属性设置为 true 以禁用此检查。请记住,在执行操作后重置该属性,并使用 try-catch-finally 块以确保异常不会将该属性保持为 true,如以下示例所示。
try
{
SPContext.Current.Web.AllowUnsafeUpdates = true;
// State-changing operation occurs here.
}
catch
{
// Handle or re-throw an exception.
}
finally
{
SPContext.Current.Web.AllowUnsafeUpdates = false;
}
AllowUnsafeUpdates 绝不用于纠正 canary 验证错误。如果您收到一个有关回发或 Web 服务请求的 canary 验证错误,您应正确地发送和验证该 canary。
使用 SPUtility 重定向到其他页面
在用户单击"确定"或"取消"后,窗体通常会重定向到其他页面。此页面的 URL 通常会传入一个查询参数(例如,Source 参数)。如果未验证此 URL,则攻击者可使用此参数将用户重定向到潜在恶意位置。
建议
调用 Microsoft.SharePoint.Utilities.SPUtility.Redirect 方法以将用户重定向到其他页面。Redirect 方法可确保目标 URL 位于当前域中,并(可选)确保重定向将转到当前网页上的其他 _layouts 页面。
SharePoint 安全性最佳实践:提升特权
提升特权是为攻击者提供超越最初授予的权限之外的授权权限来实现的。例如,具有"只读"权限特权集的攻击者以某种方式将该集合提升为包含"读写"权限。
有关详细信息,请参阅提升特权。
适当地检查用户权限
在某些情况下,您的功能无法自动检查权限。例如:
无论用户的权限如何,Microsoft.SharePoint.SPSecurity.Elevated RunWithElevatedPrivileges 操作始终会成功。
某些操作在不使用 SharePoint 对象模型的情况下无法执行权限检查。
在上述示例中,您的功能必须手动检查权限。
建议
首先,确定您应检查其权限的 Microsoft.SharePoint.SecurableObject(SPListItem 对象、SPList 对象或 SPWeb 对象)。如果您的功能影响 Web,则您的安全对象为当前上下文 Web。如果您的功能对每个列表执行操作,则您的安全对象为当前上下文列表。
接下来,确定您的功能是否应在用户不具有权限(如隐藏某些用户界面元素)时执行特殊操作或您的功能是否应拒绝访问。
如果您的功能应基于用户权限执行操作,则调用安全对象上的 DoesUserHavePermissions 并使用返回值。
备注
如果用户不具有所需权限,则 DoesUserHavePermissions 方法不引发异常。它只会返回 false。
如果您的功能应引发一个访问被拒绝异常,则调用安全对象上的 Microsoft.SharePoint.SPSecurableObject.CheckPermissions。它将引发一个访问被拒绝异常,并将此信息适当地返回给用户。
安全构建 SPSite 对象
Microsoft.SharePoint.SPSite 构造函数容易引发以下两个问题:
可使用完全限定的域名构造新的 SPSite 对象,例如,http://contoso1.example.com。如果此域名不同于当前请求上下文,则它会导致出现跨域安全性问题。
可使用网站标识符 (ID) 和可选用户标记构造新的 SPSite 对象,但不带有完全限定的 URL 或 Microsoft.SharePoint.Administration.SPUrlZone 枚举。如果当前请求上下文不是 Default 区域,则可绕过 Web 应用程序策略。
建议
您不得使用网站 ID 和用户标记来构造 SPSite 对象。相反,传递 Microsoft.SharePoint.Administration.SPUrlZone 枚举器值或网站的完全限定的域名。
如果您使用来自用户的 GUID 或完全限定的域名来构造新的 SPSite 对象,则您必须调用 Microsoft.SharePoint.SPSite.ValidateDomainCompatibility。这将确保新的 SPSite 的域名与当前请求上下文的域名完全相同,如以下示例所示。
String siteUrl = Page.Request.QueryString["site"];
// This can be a fully qualified domain names (FQDN), for example,
// http://contoso/sites/example.
if (!SPSite.ValidateDomainCompatibility(SPContext.Current.Site.Url, siteUrl))
{
// If the domain of siteUrl differs from the current context, block cross-domain operations.
throw new UnauthorizedAccessException();
}
有关安全构造 SPSite 对象的其他信息
不允许一个域上(例如,https://contoso.com)的脚本访问其他域(例如,http://wingtip.com)上的数据。这称为同源策略。
但是,如果两个域都承载于同一服务器场中,则 SharePoint 对象模型可同时访问这两个域。如果您未使用 Microsoft.SharePoint.SPSite.ValidateDomainCompatibility 阻止这一点,则攻击者可利用这一点访问不同域上的数据。
服务器场管理员可使用 Web 应用程序策略向用户单独授予网站的不同区域上的不同权限。例如,策略可以向用户授予对网站默认区域的"完全控制"权限以及对 Internet 区域的"读取"访问权限。
如果未使用 Microsoft.SharePoint.Administration.SPUrlZone 枚举器值或 FQDN 将区域信息传递到 SPSite 构造函数,则对象模型将假定要使用默认区域。这会导致出现本节前面所述方案中的特权提升问题。
慎重限制服务器端 HTTP 请求
一些功能可能会促使服务器代表用户发出传出 HTTP 请求。例如,Microsoft Business Connectivity Services (BCS) 允许用户指定要连接到的数据库和 Web 服务 URL。
由于这些请求源自在防火墙之面运行 SharePoint 的服务器,因此可使用这些请求连接到或攻击数据中心内的其他计算机。
建议
不允许用户为要连接到的 SharePoint 指定任何 URL。相反,允许服务器场管理员配置安全的 URL 列表。
或者,将用户输入的每个 URL 解析为其规范化 Internet 协议 (IP) 地址,并使服务器场管理员能够按 IP 和子网掩码(例如,192.168.0.0/255.255.0.0)阻止网络范围。
提升的对象必须保留在 RunWithElevatedPrivileges 块内
Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges 用于通过使用服务帐户凭据来运行代码块。这通常用来在用户无权直接执行某个操作时代表用户执行该操作。
例如,用户无权设置新的网站集,但 SharePoint 必须为用户设置一个网站。通过在 RunWithElevatedPrivileges 块中设置网站集可使操作获得成功。
通常,RunWithElevatedPrivileges 的用法如下。
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite elevatedSite = new SPSite(SPContext.Current.Site.Id))
{
using (SPWeb elevatedWeb = elevatedSite.OpenWeb(SPContext.Current.Web.Id))
{
// Perform administrative actions by using the elevated site and web objects.
}
}
});
通过使用提升的 Microsoft.SharePoint.SPSite 对象和 Microsoft.SharePoint.SPWeb 对象创建或访问的 SharePoint 对象模型对象将保留创建它们时所使用的权限。在 RunWithElevatedPrivileges 块的外部返回这些对象会导致出现特权提升问题。
建议
不得在 RunWithElevatedPrivileges 块的外部返回在 Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges 对象中创建或访问的 SharePoint 对象模型对象。
其他信息
在实例化一个 SPSite 对象时,SharePoint 会通过本机代码中的基础 SPRequest 对象为该对象提供支持。SPRequest 对象可记住实例化该对象的用户。SPRequest 由通过 SPSite 对象访问的所有对象共享。
备注
有关 SPRequest 对象的详细信息,请参阅释放对象。
在 RunWithElevatedPrivileges 块中设置 SPSite 对象时,SPRequest 对象发现当前用户为"系统帐户"。如果使用 SPSite 对象访问 Microsoft.SharePoint.SPListItem 对象,则列表项将共享同一 SPRequest 对象(该对象是一个类似于 SPSite 对象的"提升"对象)。
如果在 RunWithElevatedPrivileges 块的外部传递 SPListItem 对象,则它将保留其基础 SPRequest 对象并继续进行提升。如果应在当前用户的凭据下运行的代码使用此 SPListItem 对象,则此代码将出现特权提升问题。
SharePoint 安全性最佳实践:拒绝服务
当系统的负载过大以致于无法处理消息或处理消息的速度极慢时,将会发生拒绝服务情况。有关详细信息,请参阅拒绝服务。
限制单个请求可完成的工作量
一些功能将分析用户输入,并为请求中的每个元素执行一个操作。如果功能不限制其分析的元素的数目,则会导致遭受拒绝服务攻击。
建议
为您的功能在单个请求上处理的用户输入量强制设定一个合理上限。
SharePoint 安全性最佳实践:信息泄漏
攻击者可利用信息泄漏获取有关系统的有用信息。因此,请始终考虑您透露的信息以及这些信息是否可供恶意用户使用。
有关详细信息,请参阅信息泄漏。
在将 Web 服务异常返回给调用方之前进行清理
Web 服务调用可能会引发一个异常,返回过多有关服务器的数据(例如,内部服务器名称或文件系统路径)。
攻击者可使用此信息来描述 SharePoint 服务器场并发起针对性的攻击。
建议
在实现 SOAP Web 方法时,捕获所有异常,并在将这些异常返回到用户之前将其传递给 SoapServerException.HandleException,如以下示例所示。
try
{
return new SoapXml.SoapXmlElement(
m_ListSchemaImpl.GetListAndView(listName, viewName));
}
catch (Exception e)
{
throw SoapServerException.HandleException(e);
}
结论
您通过本文了解了使用 SharePoint 2010 开发解决方案时的安全性最佳实践建议。这些建议描述了解决跨站点脚本 (XSS)、跨站点请求伪造(CSRF 或 XSRF)、提升特权、拒绝服务和信息泄漏的各个方面。
其他资源
有关详细信息,请参阅以下资源: