保护 Documentum REST 服务中的数据传输
在本文中,我们将讨论哪些与 Web 安全相关的配置参数有助于保护 REST API 中的传输数据,以及如何配置它们。
概述
Web 安全是一个包含许多威胁模型和反攻击技术的领域。自 Documentum REST Services 7.3 版本以来,我们为用户提供了许多与 Web 安全相关的配置参数,以帮助保护 REST API 中的传输数据。在本文中,我们将讨论这些参数是什么,以及如何配置它们。
本文涵盖的安全模型包括:
- 跨站请求伪造 (CSRF)
- 跨源资源共享 (CORS)
- 请求清理 (Request sanitization)
- HTTP 严格传输安全 (HSTS)
- 可缓存的 HTTPS 响应
- 浏览器跨站脚本 (XSS)
- 跨框架脚本 (XFS)
- 响应内容嗅探 (Response content sniffing)
跨站请求伪造 (CSRF)
跨站请求伪造 (CSRF) 是一种攻击,它迫使用户在当前已认证的 Web 应用程序上执行非自愿的操作。CSRF 攻击专门针对改变状态的请求,而不是窃取数据,因为攻击者无法看到伪造请求的响应。这里有一个简单的场景。
- Alice 通过网页浏览器登录她的银行账户,浏览器记住了会话 cookie(没有登出)。
- Alice 访问了另一个网站,该网站告诉她可以免费赚钱。
- Alice 点击了下面显示“赢钱!”的表单。
<form action="https://bank.example.com/transfer.json" method="post">
<input type="hidden" name="payee" value="MARIA"/>
<input type="hidden" name="amount" value="100000"/>
<input type="submit" value="Win Money!" />
</form>
- 该请求实际上被重定向到她的银行网站,并使用 POST 方法将她的
- 钱从账户中转出。
- Alice 对钱款损失毫无察觉,因为她没有被要求二次登录。
在 Documentum REST Services 中,我们为一些特定的认证方案支持客户端令牌 (Client Token) cookie 认证,例如,中央认证服务 (CAS)、SAML2 Web SSO 等。客户端令牌 cookie 可以被网页浏览器记住,直到它超时。因此,cookie 保存的弱点是存在的。为了保护客户端令牌 cookie 免受 CSRF 攻击,我们从 Documentum REST 7.3 版本开始启用了 CSRF 防护。
使用 CSRF 令牌的客户端令牌认证
在 REST 服务器运行时属性文件中,有一个参数可以开启或关闭针对客户端令牌 cookie 的 CSRF 保护。默认值为 true,表示需要 CSRF 令牌。
rest.security.csrf.enabled=true
当启用时,客户端令牌认证请求必须携带一个 CSRF 令牌,可以通过请求头或请求查询参数的方式。Documentum REST Services 中的 CSRF 保护设计遵循同步器令牌模式。
这是一个在请求中包含 CSRF 令牌的示例,其中 DOCUMENTUM-CSRF-TOKEN 默认被设置为 CSRF 令牌的请求头名称。
GET /dctm-rest/repositories/REPO1 DOCUMENTUM-CSRF-TOKEN: zMp3O7u5mOu37Z1SsiuN9he/OtA7YZlf5cqcMXt9HFA= Host: localhost:8080 cookie: DOCUMENTUM-CLIENT-TOKEN=J9Z3J0IZHfy8ib7GB8SsxbRGTBP4UJtr49…
这是一个在查询参数中包含 CSRF 令牌的请求示例,其中 csrf-token
被定义为参数名。
GET /dctm-rest/repositories/REPO1?csrf-token=zMp3O7u5mOu37Z1SsiuN9he/OtA7YZlf5cqcMXt9HFA= Host: localhost:8080 cookie: DOCUMENTUM-CLIENT-TOKEN=J9Z3J0IZHfy8ib7GB8SsxbRGTBP4UJtr49…
配置 CSRF 令牌生成
CSRF 令牌是在客户端和服务器之间协商的。Documentum REST Services 支持从客户端或服务器端生成 CSRF 令牌。有一个服务器运行时属性可以控制这一点。
rest.security.csrf.generation.method= server | client
当 CSRF 令牌从服务器端生成时,初始认证(Basic、CAS、SAML2 等)的响应会在响应头中返回 CSRF 参数和令牌。客户端应使用这些参数进行后续的客户端令牌认证。
// Client's initial sign on GET /dctm-rest/repositories/REPO1 HTTP/1.1 Host: localhost:8080 Authorization: Basic QWRtaW5pc3RyYXRvcjpQYXNzd29yZEAxMjM= // Server response HTTP/1.1 200 DOCUMENTUM-CSRF-HEADER-NAME: DOCUMENTUM-CSRF-TOKEN DOCUMENTUM-CSRF-QUERY-NAME: csrf-token DOCUMENTUM-CSRF-TOKEN: 54msohR+0TSZbC6wrUnVUaqAOWpM2eQ7PhOEb8au+F8= Set-Cookie: DOCUMENTUM-CLIENT-TOKEN=cblcwodoxcK_MybUbNSejQYeX7U8dgS4F9SZeM4YIjh48…
当 CSRF 令牌从客户端生成时,初始认证(Basic、CAS、SAML2 等)的请求会在请求头中提供 CSRF 参数和令牌。Documentum REST 服务器会将这些参数用于生成客户端令牌 cookie。客户端应使用这些参数进行后续的客户端令牌认证。
// Client's further request GET /dctm-rest/repositories/REPO1 HTTP/1.1 Host: localhost:8080 Authorization: Basic QWRtaW5pc3RyYXRvcjpQYXNzd29yZEAxMjM= DOCUMENTUM-CSRF-HEADER-NAME: {csrfHeaderName} DOCUMENTUM-CSRF-QUERY-NAME: {csrfQueryName} DOCUMENTUM-CSRF-TOKEN: {csrfTokenValue} // Server response HTTP/1.1 200 Set-Cookie: DOCUMENTUM-CLIENT-TOKEN=cblcwodoxcK_MybUbNSejQYeX7U8dgS4F9SZeM4YIjh48…
为 CSRF 配置 HTTP 方法
由于 CSRF 攻击通常针对更改服务器数据的请求,因此安全的 HTTP 方法(GET、OPTIONS 等)可以在客户端令牌认证期间被排除在要求 CSRF 令牌之外。Documentum REST 提供了另一个服务器运行时属性来控制哪些 HTTP 方法需要 CSRF 令牌。
rest.security.csrf.http_methods= POST,PUT,DELETE
Documentum REST Services 还支持其他属性,用于指定令牌头名称、查询名称和令牌长度。您可以参考《Documentum REST Services 开发指南》或模板文件 rest-api-runtime.properties.template 获取更多详情。
跨源资源共享 (CORS)
通常情况下,JavaScript 客户端应该在同一来源 (same origin) 内对网站发起请求。这个限制阻止了 JavaScript 跨域发起请求,并催生了各种用于实现跨域请求的技巧。跨源资源共享 (CORS) 引入了一种标准机制,所有浏览器都可以使用它来实现跨域请求。这里有一个简单的场景。
- Alice 访问了位于 domain-a.acme.com 的一个网站
- 服务器返回一个主 HTML 网页,其中包含对另一个
- 域名 domain-b.acme.com 的资源访问
<button onclick="callOtherDomain()">
ClickMe
</button>
<script>
var invocation = new XMLHttpRequest();
var url = 'http://domain-b.acme.com';
var callOtherDomain = function() {
invocation.open('GET', url);
invocation.send();
}
</script>
- Alice 在点击 ClickMe 按钮时遇到错误
失败是正常的,因为 CORS 阻止了从 domain-a.acme.com 到 domain-b.acme.com 的跨域访问。Documentum REST Services 提供了细粒度的 CORS 配置,以控制来自其他域的 REST 请求访问。默认情况下,跨域访问是被拒绝的。
发起跨源请求
大多数现代 Web 浏览器已经在客户端支持 CORS。CORS 使用 HTTP 头来控制对远程资源的访问。在客户端,Web 浏览器会在请求中添加一个请求头 Origin: http://other-domain.com。服务器则在响应中返回一个 CORS 特定的头 Access-Control-Allow-Origin: http://some-domains.com,,它表示允许向该 API 发起请求的来源域。“*” 表示允许所有域。
这是一个例子。REST 服务器被配置为接受来源为 http://opentext.com 的请求。如果请求不包含指定的来源,服务器将返回一个 403 Forbidden 错误。
// A cross-origin request GET /dctm-rest/repositories Host: localhost:8080 Origin: http://opentext.com // Server response HTTP/1.1 200 Access-Control-Allow-Origin: http://opentext.com Access-Control-Expose-Headers: Location, DOCUMENTUM-CSRF-TOKEN Access-Control-Allow-Credentials: true
许多 JavaScript 客户端为了安全,在执行实际请求之前会先发送 HTTP 方法为 OPTIONS 的 CORS 预检请求。其机制类似,只是在前面多了一个预检请求。
// A preflight request OPTIONS /dctm-rest/services Host: localhost:8080 Origin: http://opentext.com Access-Control-Request-Method: PUT Access-Control-Request-Headers: Location // Server response HTTP/1.1 200 Access-Control-Allow-Origin: http://opentext.com Access-Control-Allow-Methods: PUT Access-Control-Allow-Headers: Location Access-Control-Expose-Headers: Location, DOCUMENTUM-CSRF-TOKEN Access-Control-Allow-Credentials: true
在服务器端配置 CORS
如前所述,跨域访问默认是被拒绝的。要启用它,Documentum REST 服务器提供了运行时属性,以允许特定域或所有域的 CORS 访问。该值默认为 false,表示 CORS 已禁用。有关这些参数的详细用法,请参考《Documentum REST Services 开发指南》。
rest.cors.enabled=false rest.cors.allowed.origins= rest.cors.allowed.methods= rest.cors.allowed.headers= * rest.cors.allow.credentials=true rest.cors.exposed.headers=Location rest.cors.max.age=3600
请求清理 (Request sanitization)
存储型跨站脚本允许永久性地注入 JavaScript 代码。这是一个安全漏洞,因为这些 JavaScript 代码可能导致用户会话被盗。Documentum REST Services 中的请求清理功能会清理用户输入,以防范此漏洞。请求清理会过滤输入中的两个部分:
- 输入对象的元数据
- 输入的 HTML 内容
这是一个在对象元数据中注入脚本的例子。当存储的 object_name
属性值被渲染到 HTML 页面时,会弹出意想不到的警报框。
{
"name" : "document",
"type" : "dm_document",
"properties" : {
"object_name" : "The<script>alert('****')</script>Document",
}
}
在 Documentum REST Services 中创建文档或其他对象时,服务器可以对元数据和 HTML 内容进行清理,并从文本中移除任何可疑的脚本代码。以下是上述 JSON 对象元数据清理后的结果。
{
"name" : "document",
"type" : "dm_document",
"properties" : {
"object_name" : "TheDocument",
}
}
对于 HTML 内容清理,默认只对 html 和 pub_html 格式进行清理。但你仍然可以通过配置运行时属性,将其他格式添加到内容清理中。
在服务器端配置清理
由于清理功能会影响性能,因此默认是关闭的。要开启它,Documentum REST 服务器提供了运行时属性来分别配置元数据和内容的清理。关于这些参数的详细用法,请参考《Documentum REST Services 开发指南》。
rest.sanitize.type.metadata=false rest.sanitize.type.content=false rest.sanitize.content.max.size=500000 rest.sanitize.content.default.charset=UTF-8 rest.sanitize.content.format=html,pub_html
请注意,Documentum REST Services 使用 OWASP AntiSamy 库来清理文本。清理策略也可以通过创建自己的策略文件进行配置。《Documentum REST Services 开发指南》中有所有详细信息。
自定义清理
在使用 Documentum REST SDK 进行自定义 REST 服务开发时,你可能希望为你的资源模型启用清理功能。REST 提供了一个注解 com.emc.documentum.rest.binding.SanitizeConfig,它允许你在自己的资源模型上自定义清理。以下是一些示例。
// Skip sanitization despite the global configuration
@SerializableType(...)
public class MyModel {
@SerializableField
@SanitizeConfig(skipSanitize=true)
private String wontBeSanitized;
...
}
// Enforce sanitization despite the global configuration
@SerializableType(...)
public class MyModel {
@SerializableField
@SanitizeConfig(enforeceSanitize=true)
private String mustBeSanitized;
...
}
HTTP 严格传输安全 (HSTS)
一些网站要求使用 HTTPS/SSL 协议访问,但允许客户端请求从 HTTP 重定向到 HTTPS。这可能导致中间人攻击漏洞。下面是一个例子。
- Alice 使用她的凭据以明文形式访问网站 http://mybank.acme.com。
- 服务器发送一个重定向 (302) 响应到 https://mybank.acme.com。
- Alice 使用她的凭据以加密文本形式访问 https://mybank.acme.com。
如你所见,如果一个中间人正在劫持 Alice 的套接字数据包,他可以轻易地获取到 Alice 的明文凭据。在这种情况下,尽管主机 mybank.acme.com 已将其通信协议加强到 HTTPS,但这并不能保护用户免于向攻击者泄露机密信息。
HTTP 严格传输安全 (HSTS) 是一种 Web 安全机制,有助于保护网站免受协议降级攻击和 cookie 劫持。如果服务器要求 HTTPS 连接,它会在其响应中发送一个头 Strict-Transport-Security: max-age=,告诉客户端在该时间段内的所有后续请求都应以 HTTPS 发出。如果客户端支持 HSTS,它会连同过期策略一起存储此设置。由于超时时间可能很长(例如一年),Web 浏览器通常会将此设置保存在离线存储中。
Documentum REST Services 添加了服务器配置参数,以便在协议为 HTTPS 时启用 HSTS 响应头。Strict-Transport-Security 头默认是启用的。有关这些参数的详细用法,请参考《Documentum REST Services 开发指南》。
rest.security.headers.hsts.disabled=false rest.security.headers.hsts.include_sub_domains=true rest.security.headers.hsts.max_age_in_seconds=31536000
当 HSTS 开启时,来自 REST 服务器的 HTTPS 响应将包含 Strict-Transport-Security 头。下面是一个例子。
GET /dctm-rest/services Host: localhost:8443 … // Server response HTTP/1.1 200 Strict-Transport-Security: max-age=31536000 ; includeSubDomains …
可缓存的 HTTPS 响应
除非另有指示,浏览器可能会存储从 Web 服务器接收到的内容的本地缓存副本。如果通过 https 传输的内容存储在本地缓存中,将来有权访问同一台计算机的其他用户可能会检索到这些内容。为消除此漏洞,响应应通过头 Cache-Control、Pragma 和 Expires 包含指令,以指示客户端不要存储本地副本。
自 7.3 版本起,Documentum REST Services 增加了一个服务器运行时参数来控制缓存相关的头。默认值为 false,表示启用了 HTTPS 上的缓存预防。
rest.security.headers.cache_control.disabled=false
当禁用缓存时,REST 服务器的响应如下所示。
GET /dctm-rest/services Host: localhost:8080 // Server response HTTP/1.1 200 Cache-Control: no-cache, no-store, max-age=0, must-revalidate Pragma: no-cache Expires: 0 …
浏览器跨站脚本 (XSS)
跨站脚本 (XSS) 攻击是一种注入攻击,恶意脚本被注入到原本良性且受信任的网站中。当攻击者利用 Web 应用程序将恶意代码(通常是浏览器端脚本的形式)发送给其他终端用户时,就会发生 XSS 攻击。
- Alice 经常访问搜索网站 http:// acme.com/find?q= 进行网页搜索。
- 如果搜索项没有结果,网站会返回错误 “404 Not Found for
- URL /find?q=”。
- 一天,Alice 收到一封邮件,提供了一个指向该搜索网站的隐藏链接,其
- URL 为 http://acme.com/find?q= 。
- Alice 点击了链接,弹出了一个提示“xss”的消息框。
- 如果这是一个由攻击者编写的恶意脚本,Alice 的机密信息可能
- 会被悄悄窃取。
防止 XSS 攻击的一种方法是,当收到潜在的恶意响应时,阻止 Web 浏览器在客户端运行脚本。XSS 过滤器是实现这一目标的一种方法。当设置了专用的响应头 X-XSS-Protection 时,客户端将对收到的响应消息进行规范化,甚至停止渲染页面。
Documentum REST Services 提供了服务器配置参数来启用 XSS 保护。默认值为 false,表示 XSS 保护已启用。有关这些参数的详细用法,请参考《Documentum REST Services 开发指南》。
rest.security.headers.xss_protection.disabled=false rest.security.headers.xss_protection.explicit_enable=true rest.security.headers.xss_protection.block=true
当启用 XSS 保护时(值为 false),REST 响应消息会添加 HTTP 头 X-XSS-Protection。
// Server response HTTP/1.1 200 X-XSS-Protection: 1; mode=block …
跨框架脚本 (XFS)
跨框架脚本 (XFS) 是一种攻击,它将恶意 JavaScript 与一个加载了合法页面的 iframe 结合起来,试图从未起疑心的用户那里窃取数据。这里有一个简单的例子。
- Alice 经常访问她的银行网站 http://mybank.acme.com。
- 一个攻击者创建了一个恶意网站,并向 Alice 发送一封邮件,让她通过一个
- 隐藏链接 http://mybank.evil.com 来检查她的账户,该链接指向一个隐藏了如下 iframe 的页面。
<iframe style="position:absolute;top:-9999px" src="http://mybank.acme.com/flawed-page.html"></iframe>
- Alice 点击了链接,她看到的一切都和 mybank.acme.com 一样。
- Alice 登录并检查了她的银行账户。
- Alice 的机密信息被窃取了。
消除框架脚本漏洞的一种方法是阻止 Web 浏览器在 <frame>
、<iframe>
或 <object>
中渲染页面。Mozilla 提出了响应头 X-Frame-Options 来实现这一点。
Documentum REST Services 添加了服务器运行时配置参数来禁用框架脚本。默认值为 false,表示已启用框架脚本预防。
rest.security.headers.x_frame_options.disabled=false rest.security.headers.x_frame_options.policy=DENY
当启用框架脚本预防时,REST 响应消息会添加头 X-Frame-Options。
// Server response HTTP/1.1 200 X-Frame-Options: DENY …
响应内容嗅探 (Response content sniffing)
内容嗅探 (Content sniffing) 是指检查字节流的内容以试图推断其中数据的文件格式的做法。当客户端和服务器之间没有合适的机制来检测内容 MIME 类型时,这很有用。然而,它也带来了一个严重的安全漏洞,即通过混淆 MIME 嗅探算法,浏览器可能被操纵以一种允许攻击者执行网站运营者或用户都未预料到的操作的方式来解释数据,例如跨站脚本攻击。
在 Documentum REST Services 中,客户端和服务器通过 Accept 和 Content-Type 头来协商内容 MIME 类型,因此在大多数情况下,不需要内容嗅探。此外,Documentum REST Services 提供了一个服务器运行时参数来阻止内容嗅探。它默认为 false,表示已启用内容嗅探预防。
rest.security.headers.content_type_options.disabled=false
当内容嗅探被阻止时,来自 REST 服务器的响应将包含头 X-Content- Type-Options。
// Server response HTTP/1.1 200 X-Content-Type-Options: nosniff …