http://www.ibm.com/developerworks/cn/websphere/techjournal/1303_lansche/1303_lansche.html
简介
第 1 部分 解释了 IBM WebSphere Application Server V7.0 和更高版本在设计时如何考虑到默认安全性安全原则。目标是在最常见的配置和比较简单的环境中,让这个产品在默认情况下具有合理的安全水平(尽管这个目标还没有完美地实现)。前一篇文章最后介绍了 WebSphere Application Server 中已经采用的许多重要的基于基础架构的预防性安全措施。本文将介绍基于应用程序的其他预防性措施,然后讨论一些重要的注意事项。
尽管本文中的信息基于 IBM WebSphere Application Server V7、V8.0 和 V8.5,但是讨论的大多数问题同样适用于 V6.1。对于某个版本特有的问题,我们会专门指出。如果您使用以前的 WebSphere Application Server 版本,请参考 早期文章,因为具有显著的版本差异,这些差异进而会导致文章之间的差异。
配置文件备注
如果熟悉 V8.5 之前的 WebSphere Application Server,您就会知道必须要创建一个或多个配置文件。该配置文件可能是一个 Application Server(或 Base)配置文件、一个 Deployment Manager 配置文件等。在本文的剩余部分中,这些配置文件被称为 “完整” 配置文件。进行这样的区分是为了将这些配置文件与 V8.5 中新增的配置文件(Liberty 配置文件)进行对比。本文还提供了特定于新的 Liberty 配置文件的一些建议。
基于应用程序的预防性措施
配置措施
到目前为止,本文的重点是介绍创建安全的 IBM WebSphere Application Server 基础架构可以采取的基本步骤。这显然很重要,但只关注基础架构是不够的。既然基础架构已经加强了,那么现在有必要研究一下应用程序需要做哪些事情来确保安全性。当然,应用程序必须利用 WebSphere Application Server 提供的基础架构,但应用程序开发人员还必须执行(或避免)其他一些操作来尽可能地提高应用程序的安全性。
- 不要将 Web 服务器的文档根设置为 WAR
- 仔细检查每个 servlet 别名是否安全
- 不要通过类名提供 servlet
- 不要将敏感信息放在 WAR 根目录中
- 定义默认的错误处理程序
- 考虑禁用文件服务和目录浏览
- 启用会话安全性
- 注意定制的 JMX 网络访问
为了帮助您将这些措施与特定的攻击类别联系起来,每个措施都使用了 第 1 部分 介绍的标记来表示攻击的类别。
1. 不要将 Web 服务器的文档根设置为 WAR
WAR 文件包含应用程序代码和大量敏感信息。其中只有一部分信息可以向 Web 提供的内容。因此,将 Web 服务器文档根设置为 WAR 根目录是不合适的。如果这样做,Web 服务器会不加解释地提供 WAR 文件的所有内容。这会导致将代码、未经处理的 JSP 和其他内容提供给最终用户。(这项措施只在 Web 服务器和应用程序服务器放在一起时才有用,如果您遵循 第 1 部分 中给出的指南,通常不会出现此情况。)
2. 仔细检查每个 servlet 别名是否安全
WebSphere Application Server 通过 URL 保护 servlet。每个要保护的 URL 都必须在描述应用程序的 web.xml 文件中指定。如果 servlet 不止有一个别名(也就是说,多个 URL 访问相同的 servlet 类),或者有许多 servlet,那么很容易遗忘对某个别名的保护。需要谨慎处理这一点。由于 WebSphere Application Server 保护的是 URL,而不是底层类,所以即使只有一个 servlet URL 是不安全的,入侵者也能够绕过您的安全措施。为了减少这种威胁,应该尽可能地使用通配符来保护 servlet。如果那样做不合适,则应该在部署前再次仔细检查 web.xml 文件。
在为 servlet 指定授权约束时,要确保不列出任何方法,或者非常仔细地列出 servlet 的所有方法(很可能在多个约束中),比如 GET、POST、PUT、HEAD 等。对于每个 Java™ EE 规范,如果授权约束显式地列出方法,没有提到的方法就没有授权约束!这对于 HEAD 等方法尤为危险,HEAD 在默认情况下通过调用 GET 获取所需的标头,这实际上会调用 GET 方法,但不会检查它的授权约束。清单 1 中的 web.xml 代码是不安全的,而清单 2 中的代码是安全的。
清单 1. web.xml – 不安全
<security-constraint> <web-resource-collection> <web-resource-name>myservlet</web-resource-name> <url-pattern>/myservlet</url-pattern> <http-method>GET</http-method> </web-resource-collection> <auth-constraint> <role-name>arole</role-name> </auth-constraint> </security-constraint>
清单 2. web.xml – 安全
<security-constraint> <web-resource-collection> <web-resource-name>myservlet</web-resource-name> <url-pattern>/myservlet</url-pattern> </web-resource-collection> <auth-constraint> <role-name>arole</role-name> </auth-constraint> </security-constraint>
下面的方法可从每个 web.xml 中获取,以确保权限是受限的:
- 定义一个初步安全约束,比如清单 3 中所示的约束。这表明所有 URL(除非被更具体的模式覆盖)仅限于 NoAccess 角色。NoAccess 角色名称可预先绑定到 None 特殊角色。
清单 3
<security-constraint> <web-resource-collection> <web-resource-name>DefaultDeny</web-resource-name> <url-pattern>/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>NoAccess</role-name> </auth-constraint> </security-constraint>
- 如果使用基于表单的登录,则应定义宽松的约束,允许 Web 容器向任何用户显示登录页面(和必要的图形等),甚至未经过身份验证的用户也能看到这些(清单 4)。AllUsers 角色名称可预先绑定到 Everyone 特殊角色。
清单 4
<security-constraint> <web-resource-collection> <web-resource-name>LoginForm</web-resource-name> <url-pattern>/Login.jsp</url-pattern> <url-pattern>/images/*.gif</url-pattern> <url-pattern>/css/*.css</url-pattern> <http-method>GET</http-method> </web-resource-collection> <auth-constraint> <role-name>AllUsers</role-name> </auth-constraint> </security-constraint>
- 添加上述两个 “默认” 约束后,需要为每个添加到应用程序中的适合该 URI 的 servlet 添加 “真正的” 安全约束元素。
此方法的优势是,添加到 Web 应用程序的新端点默认无法访问。如果添加了新的 servlet,则必须为这个新的 URL 添加一个安全约束,以便任何人都可以使用它。使用 “默认拒绝” 设计是开发安全应用程序的基础。
如果使用 Java 注释定义安全性约束,那么您可以利用 ServletSecurity.EmptyRoleSemantic.DENY 约束。例如,/simple URL 允许使用针对 AnyUser 角色的 POST 和 GET 方法,但剩余方法会遭到拒绝(清单 5)。
清单 5. 使用注释为 HTTP 方法提供安全的默认设置的示例
@WebServlet(description = "Returns msg and user info", urlPatterns = { "/simple*" }) @ServletSecurity( value = @HttpConstraint(ServletSecurity.EmptyRoleSemantic.DENY), httpMethodConstraints = {@HttpMethodConstraint (value = "GET", rolesAllowed = "AnyUser"), @HttpMethodConstraint (value = "POST", rolesAllowed = "AnyUser")}) public class Simple extends HttpServlet { …
依据 Java Servlet Specification 3.0(2009 年 12 月)第 13.4.1 条,HttpConstraint(ServletSecurity.EmptyRoleSemantic.DENY) 的 value 属性具有以下效果:
“HttpConstraint 定义要应用到所有未在 httpMethodConstraints 返回的数组中表示的 HTTP 方法的保护措施。”
3. 不要通过类名提供 servlet
可以通过类名或一般的 URL 别名来提供 servlet。通常应用程序会选择后者。也就是说,开发人员在 web.xml 文件中手动定义从每个 URL 到每个 servlet 类的准确映射,或者使用各种 WebSphere Application Server 开发工具来定义该映射。
然而,WebSphere Application Server 也允许通过类名提供 servlet。通用的 URL(例如 /servlet)并没有为每个 servlet 定义映射,而是提供所有 servlet。假设基本路径后面的路径成分是 servlet 的类名。例如,"/servlet/com.ibm.sample.MyServlet" 引用 servlet 类 "com.ibm.sample.MyServlet"。
通过类名提供 servlet 是通过在 ibm-web-ext.xmi 文件中将 serveServletsByClassnameEnabled 属性设置为 true 完成的,或者在 IBM Rational® Application Developer 的 WAR 编辑器中选中 servlets by classname。请不要启用这个特性,Rational Application Developer 在默认情况下已经启用它,所以如果您正在使用 Rational Application Developer,那么您必须显式禁用它(图 1)。
图 1. 不要通过类名提供 servlet
这个特性使得知道 servlet 类名的任何人都可以直接调用它。即使 servlet URL 是受保护的,攻击者也能绕过正常的基于 URL 的安全措施。另外,根据类装载器的结构,攻击者可能能够调用您的 Web 应用程序之外的 servlet,比如 IBM 提供的 servlet。(请注意,此处 没有将示例应用程序安装在生产环境中作为第一步,以预防出现这种情况。)
作为管理员,您可能不确定各个应用程序的设置是否错误地通过类名提供了 servlet。您可以 通过在 JVM 上设置一个定制属性com.ibm.ws.webcontainer.disallowAllFileServing,将应用服务器配置为忽略应用程序中的这个属性,从而禁止通过类名提供 servlet。Liberty 配置文件的等效结构已在清单 6 中给出。
清单 6
<webcontainer disallowAllFileServing=”true”/>
4. 不要将敏感信息放在 WAR 根目录中
WAR 文件包含可提供的内容。Web 容器将提供 WAR 文件根目录中的任何文件。只要仅在根目录中放置应该提供的内容,就不会出现问题。因此,决不要将不应该显示给最终用户的内容放在 WAR 的根目录中。例如,不要将属性文件、类文件或其他重要信息放在根目录中。如果您必须将这种信息放在 WAR 中,则应将其放在 WEB-INF 目录中,这是 servlet 规范所允许的。Web 容器决不会提供那里的信息。
5. 定义默认的错误处理程序
当 Web 应用程序中出现错误时,即使是在应用程序分派之前(例如 WebSphere Application Server 无法发现目标 servlet),也会向用户显示错误消息。在默认情况下,WebSphere Application Server 会显示错误的原始异常堆栈转储。这不仅对最终用户非常不友好,还会暴露应用程序的相关信息(堆栈信息中有类和方法的名称)。同时还显示异常消息,其中可能包含敏感信息。
最好定义默认的错误页面,每当出现未处理的异常时就显示它,从而确保最终用户永远看不到原始错误消息。此页面可以是对用户友好的错误消息,但不是堆栈跟踪。默认的错误页面是在 ibm-web-ext.xmi 中使用 defaultErrorPage 属性定义的,也可以在 Rational Application Developer 中使用 Web 部署描述符编辑器(Extensions 选项卡)进行设置。
图 2. 定义默认的错误页面
6. 考虑禁用文件服务和目录浏览
可以通过禁用 Web 应用程序中的文件服务和目录浏览,进一步限制提供不适当内容的风险。
图 3. 禁用文件服务和目录浏览
当然,如果 WAR 包含可提供的静态内容,则必须启用文件服务。很少存在需要启用目录浏览的原因。
7. 启用会话安全性(仅适用于 V7.0)
不同于 WebSphere Application Server V8.0(和更高版本),WebSphere Application Server V7.0 通常并不强制对 HTTP 会话访问进行任何授权。因此,任何具有有效会话标识符的请求都可以访问会话。虽然会话标识符是不可猜测的,但也可能通过其他手段获得会话标识符。结果,从更低的 WebSphere Application Server 版本迁移的、未启用会话安全性的用户的应用程序可能被破坏。请注意,这也是 Liberty 配置文件的默认行为。
要想降低这种攻击形式的风险,应该考虑启用会话安全性。这一设置是在 Application server > <server name> > Web container > Session management 面板中配置的。只需选择 Security integration 选项即可,如图 4 所示。
图 4. 会话安全性集成
WebSphere Application Server 将跟踪哪个用户(根据用户提供的 LTPA 凭证来确定)拥有哪个会话,确保只有针对该用户的请求才能访问该会话。
在极少数情况下,这种设置会破坏 Web 应用程序。如果应用程序同时包含安全的(具有授权约束)和不安全的 servlet,那么不安全的 servlet 将无法访问会话对象。一旦某个安全的 servlet 访问启用了该会话,它会被标记为归该用户 “所拥有”。如果以匿名方式运行的不安全的 servlet 试图访问这些页面,就会遭遇授权失败。从 WebSphere Application Server V6.1 开始,可以更改授权行为,确保没有授权约束的 servlet 能够继承当前用户身份(如果已经有的话),这样就解决了这个问题。图 5 说明如何设置这个特性。
图 5. 保留现有的身份
在 WebSphere Application Server V8.0 中,服务器默认设置进行了两方面的更改。将启用会话安全性集成与 “在访问不受保护的 URI 时使用可用的身份验证数据” 相结合,避免之前描述的安全/不安全的 servlet 破坏场景。
防止攻击
启用会话安全性实际上可以防止一种攻击。假设网站在用户执行身份验证并使用 HTTPS 之前建立 HTTP 会话(大多数网站都是这样)。在这种情况下,会话 cookie (JSESSIONID) 是明文的,攻击者很容易捕获它。之后,当用户执行身份验证时,应该切换到 SSL 并确保只通过 SSL 传输 LTPA cookie。问题在于攻击者已经得到了 JSESSIONID cookie。根据编写应用程序的方式不同,攻击者可能能够使用这个 cookie 和不同的 LTPA cookie(可能是代表他自己的身份的 cookie),作为第一个用户访问应用程序。这是一种非常严重的攻击,而且如果使用开放的无线网络,这种攻击很容易实现。启用会话安全性可以阻止这种攻击,因为应用服务器会阻止访问不归当前用户所有的会话。
8. 注意定制的 JMX 网络访问
JMX 和定制的 MBean 让应用程序能够支持强大的远程定制管理。但要注意的是,JMX MBean 可以通过网络进行访问。如果选择部署它们,则需要十分谨慎,确保它们的操作具有合适的授权。WebSphere Application Server 会根据 MBean JAR 文件中的描述符中的信息,自动为 MBean 提供默认的授权限制。这样是否合适取决于您的应用程序。有关的更多信息,请参阅图书 IBM WebSphere: Deployment and Advanced Configuration。
设计和实施措施
接下来,我们将注意力转移到应用程序开发人员和设计人员为了构建安全的应用程序必须采取的措施上。这些步骤非常关键,但遗憾的是,它们经常被忽视。
- 使用 WebSphere Application Server 安全性来保护应用程序
- 不要依赖于 HTTP 会话跟踪用户身份
- 保护应用程序的每一层
- 检验所有用户输入
- 编写安全的应用程序
- 安全地存储信息
- 不要审计和跟踪敏感信息
- 避免对浏览器客户机使用基本身份验证
- 避免小部件盗_窃:如果使用来自第三方的 GET 构建浏览器 UI,则使用 SSL
9. 使用 WebSphere Application Server 安全性来保护应用程序
应用程序开发团队通常能够认识到他们的应用程序需要某种程度的安全性。这通常是业务上的需求。遗憾的是,许多团队决定自己开发安全基础架构。这虽然也有可能做好,但非常困难,而且到头来,大部分团队都不能成功。相反,他们的系统看似非常安全,实际上系统安全性非常脆弱。安全性是个很难解决的问题。密码术的微妙问题、重播攻击和各种形式的攻击很容易被忽视。这里要说的是您应该使用 WebSphere Application Server 安全性。前面曾经建议您启用应用程序安全性;这里建议在应用程序中实际使用它。
关于 Java EE 定义的声明性安全模型,最常见的抱怨可能是它没有提供足够的粒度。例如,只能在 EJB 或 servlet 的方法级上执行授权(在这个上下文中,servlet 上的方法是 HTTP 方法之一,比如 GET、POST、PUT 等等),不能在实例级上执行授权。再如,所有的银行帐号都有相同的安全限制,但是您希望某些用户对他们自己的帐号拥有特殊权限。
这个问题可以通过 Java EE 安全性 API isCallerInRole 和 getCallerPrincipal 来解决。通过使用这些 API,应用程序可以开发自己强大而又灵活的授权规则,但是仍然需要通过已知的准确信息来驱动这些规则:来自 WebSphere Application Server 运行时的安全性属性。如果这仍然不够,可以构建(或购买)更精细的授权框架,框架应该使用应用程序服务器已经维护的现有安全信息(比如组)。即使这仍然不够,安全基础架构是高度可定制的。利用(并在需要时扩展)现有的可以正常工作的东西是正确的做法,不应该抛弃现有的东西,尝试从头构建安全基础架构。
一个弱安全性的例子
这里是一个弱安全性系统的例子。不使用 WebSphere Application Server 安全性的应用程序倾向于创建自己的安全令牌,并在应用程序内部传递它们。这些令牌通常包含用户名和一些安全属性,比如用户的组成员身份。这些安全令牌常常没有可通过密码学技术验证的信息。该方法假设可以根据这些令牌中的信息制定安全决策。这是错误的。这些令牌只能断言用户的特权。这里的问题是,任何 Java 程序都能够伪造这些安全对象,然后可以通过后门侵入系统。最能说明这个问题的例子是,应用程序在 servlet 层创建这些令牌,然后将其传递到 EJB 层。如果 EJB 层不安全(请参见第 第 11 条),那么入侵者就可以使用伪造的凭证直接调用 EJB,使应用程序的安全性完全丧失。因此,除非完成细致的工程工作,可靠的用户信息来源只有 WebSphere Application Server 基础架构。
10. 不要依赖于 HTTP 会话跟踪用户身份
此建议与上一个主题密切相关。许多自己实现安全性的应用程序通过使用 HTTP 会话来跟踪用户的身份验证会话。这非常危险。该会话是通过(URL 或者 cookie 中的)会话 ID 来跟踪的。虽然此 ID 是随机生成的,但它仍然可能遭受重播攻击,因为它没有具体的绝对超时时间。只有在一段时间内没有活动的情况下,才会发生超时,如果截获了会话 cookie,就能在不受限制的时间段内滥用该 cookie。另一方面,当应用程序使用 WebSphere Application Server 安全性时,会创建 LTPA 令牌,它是更强的身份验证令牌。特别是,LTPA 令牌的生存期有限,而且使用了强加密,安全性子系统会对可能是伪造的 LTPA 令牌的收据进行审核。
使用 HTTP 会话还有一个更危险、更微妙的安全问题:会话 cookie (JSESSIONID) 通常是在用户执行身份验证之前创建的,通常在用户第一次访问站点时创建。此时,cookie 常常通过 HTTP 以明文形式发送。在用户执行身份验证之后,大多数应用程序会通过 HTTPS 传输以后的所有通信流,以保护 cookie 和内容;但是,JSESSIONID cookie 可能已经被盗,因为在最初通过 HTTP 发送该 cookie 时,攻击者就可以捕获它。除了尽可能避免使用 HTTP 会话之外,通过启用会话安全性并将 LTPA cookie 限制为 HTTPS,可以降低 HTTP 会话被盗的风险。
此外,您的应用程序创建的 cookie 应该是安全的(仅限使用 HTTPS),所有 cookie 都应标为 HTTPOnly,但有一个可能的例外(这需要在一次设计审核中备案和签字批准),那就是,由于客户机 JavaScript 需要访问该 cookie,所以应用程序会明确要求它运行。
11. 保护应用程序的每一层
我们常常采用一定程度的安全性(自制的或者基于 WebSphere Application Server 的)将 Web 应用程序部署在 servlet 层上,但是对应用程序中的其他层却不加保护。这样做是基于一个错误假设:应用程序中只有 servlet 需要保护,因为它们是应用程序的前门。但是,正如警察常说的那样,您必须将您家的后门和窗户都锁上。在许多场景中都可能出现这种疏忽,但是如果 Java 客户机不是应用程序的一部分,而是在多层架构中使用可远程访问的组件(比如可通过 IIOP 或 Web 服务访问的 EJB),则最容易出现这种情况。在这种情况下,开发人员常常认为可远程调用的组件不需要保护,因为根据应用程序设计,它们不是 “用户可以访问的”,但这种想法是一个危险错误。人们常常开玩笑地将此称为 “隐藏式安全”。如果不对服务层施加保护,入侵者就可以绕过 servlet 接口,直接进入服务层并进行破坏。
通常,人们对此问题的第一反应就是通过一些平常的手段来保护服务,比如将它们标记为可由所有已通过身份验证的用户进行访问。但是,根据注册表,“所有已通过身份验证的用户” 可能是公司中的每个雇员。一些组织更进一步,将访问权限制在某个组的成员,这个组指定 “可以访问此应用程序的所有人”。这样做好一些,但是通常仍不够理想,因为能够访问应用程序的每个人并没有必要执行应用程序中的所有操作。解决这个问题的正确方法是在服务中实现授权检查。对于 EJB,也可以考虑将 EJB 组件实现为只有本地接口,这样就无法远程连接它们。
12. 检验所有用户输入
跨站点脚本是一种相当危险的攻击,它利用了 Web 浏览器的灵活性和强大功能。大多数 Web 浏览器都可以解释多种脚本语言,例如 JavaScript™。浏览器知道它要根据特殊的转义字符序列来寻找可执行的代码。这是 Web 浏览器的强大之处,也是安全性模型中的一个很危险的漏洞。
入侵者可以欺骗 Web 站点,在浏览器中显示入侵者希望该站点执行的脚本,通过这样做来利用此漏洞。在允许任意用户输入的站点上,这很容易做到的。例如,如果站点包含一个用于输入地址的表单,那么用户可以在其中输入 JavaScript。当站点随后显示该地址时,Web 浏览器就会执行该脚本。该脚本由于来自该站点并在 Web 浏览器内部运行,所以可以访问安全信息,比如用户的 cookie。
到此为止,这看似好像没什么危险,但入侵者可以更进一步,他们 欺骗用户进入一个 Web 站点并输入恶意脚本,欺骗手段可以是通过电子邮件向用户发送无害的 URL 或者将 URL 发布在公共博客上。然后,入侵者就可以在用户的浏览器中运行任意代码,通常会访问 cookie,他们可以看到所有键盘活动和显示的所有页面。这样,入侵者就可以对此用户制造无法挽回的损害。
这个问题实际上是与用户输入检验相关的问题的一个特例。只要允许用户输入任意文本,就必须确保该文本不包含会造成破坏的特殊字符。例如,如果用户要输入一个字符串,用它来搜索某个索引,则必须过滤掉可能造成越界搜索的不合适的通配符,这很重要。对于跨站点脚本的预防,需要过滤掉浏览器支持的脚本语言的转义字符。这里要说明的是,对所有外部输入都应该采取怀疑态度,并仔细进行检验。必须解决的问题非常多,对它们的全面讨论已超出了本文的讨论范围。
13. 编写安全的应用程序
鉴于本文的目标和篇幅,无法在这里列举可能影响应用程序安全性的所有应用程序设计和编程问题。如果您对开发安全应用程序很感兴趣,请参阅下面这些关于安全应用程序的设计、开发和测试的参考资料:
- 图书:Writing Secure Code
- 图书:Software Security: Building Security In
- OWASP,它发表了以下指南:
- Web Application Security Consortium
- Penetration testing frameworks
另外,最近发表的一本 IBM 红皮书 讨论了如何使用 IBM Rational AppScan 作为开发周期的关键元素,从而构建安全的 Web 应用程序。
14. 安全地存储信息
要创建安全的系统,必须考虑在何处存储或显示信息。有时,一些相当严重的安全漏洞就是无意间造成的。例如,对于在 HTTP Session 对象中存储高度机密的信息,应该十分谨慎,因为此对象可能被序列化到数据库中,可以从那里读取它。如果入侵者可以访问您的数据库(甚至是对数据库卷进行原始计算机级访问),那么他就可以看到会话中的信息。毫无疑问,这样的攻击需要非常高的技能。
15. 不要审计和跟踪敏感信息
出于业务目的和调试目的,任何比较复杂的应用程序都会生成丰富的日志和跟踪信息。这样做很好,但要记住的是,这些文件中的信息往往保存在许多地方(可能位于您组织之外的地方)。对于非常敏感的信息,不应该进行跟踪,如果可以,应该试图避免对其进行审计。例如,我们见过客户机将用户的密码、驾照号码和社会保险号码输出到跟踪文件中。如果该文件被不当的人(可能是帮助您的顾问)阅读,就会暴露敏感信息。任何以电子形式(包括备份图像)从系统发送给任何人的客户信息都必须是加密的。这一控制级别不仅适用于用户密码,也适用于任何客户信息。
为了确保安全,应该限制并检查日志记录的内容。
16. 避免对浏览器客户机使用基本身份验证
基本身份验证的问题是 Web 浏览器会缓存用户 ID 和密码,在需要的时候,会在以后的请求中自动重新发送它。这意味着使用基本身份验证的用户是不可能注销的。例如,如果用户的 LTPA 令牌过期了,那么应用程序服务器会再次询问用户 ID 和密码。浏览器会重新发送基本的身份验证标头,所以超时已经没有意义了。
这种自动在需要时自动重新发送用户凭据的现象,实际上存在于大多数单一登录解决方案的核心,包括基于 SPNEGO 的 SSO,以及用户使用客户端 SSL 证书进行身份验证时。换句话说,基本身份验证只是让用户可轻松地重新登录,无需手动提供凭据的另一个例子。但是,它不同于其他身份验证,因为用户的密码在每次请求时都会重新发送。
更糟糕的是,如果以后通过 HTTP 发送请求(不加密),浏览器会通过网络以明文形式发送密码。
当客户机不是浏览器时,基本身份验证是合理的方法。例如,Web 服务或 REST 客户机通常会安全地使用基本身份验证。当然,这种请求应该通过 SSL 发送。
17. 避免小部件盗_窃:如果使用来自第三方的 GET 构建浏览器 UI,则应使用 SSL
社交媒体站点已渗透到 “传统的” Web 应用程序中,在此过程中,已经向身份窃贼暴露了您的用户,向他们提供了用户的社交媒体凭据。这称为小部件盗_窃。基本场景是这样的:
- 一位最终用户登录到一个社交站点,比如 Facebook 或 Twitter,但没有注销。他或她可能直接关闭浏览器并重新启动了计算机。
- 这位用户转移到一个公共 wifi 位置并(使用传统的 Web 应用程序)访问他的银行帐户。该银行最近添加了在 Facebook、Twitter 和其他社交媒体站点上 “赞” 该银行的能力。为了帮助用户,该银行的应用程序用户界面包含无所不在的 Facebook 和 Twitter 图标。
- 该银行的页面可通过 SSL 呈现,但返回的页面包含清单 7 中的代码。
清单 7
<iframe src="http://www.facebook.com/plugins/like.php?href=YOUR_URL" scrolling="no" frameborder="0" style="border:none; width:450px; height:80px"> </iframe>
- 请注意,iframe 会导致向未加密的 URL 发出 GET。
- www.facebook.com 的所有 cookie 都通过该 HTTP GET 请求发送,包括包含用户向 facebook.com 站点验证的身份的 cookie。
- 窃_听者 Eve 监听着可通过 wifi 看到的未加密的流量(参见 Firesheep,了解有关的详细信息),并获取了未加密的 cookie。
结合利用社交媒体登录来进行身份验证的第三方应用程序,这会变成一个比您最初想象的更大的风险。已经有一些浏览器插件设计用来保护用户,但需要一位知识渊博的用户来安装和启用这些插件。
我不是提倡您不要将您的应用程序与这些社交媒体站点相集成,或者提倡您的用户不应向这些站点发送一个 “赞”。但是请负起责任:将获取该内容的代码更改为清单 8 中所示的代码。
清单 8
<iframe src="https://www.facebook.com/plugins/like.php?href=YOUR_URL" scrolling="no" frameborder="0" style="border:none; width:450px; height:80px"></iframe>
高级注意事项
现在,我们继续讨论一些与安全性加强相关的高级问题,包括跨计算单元信任、应用程序隔离、身份传播和 WebSphere Application Server 安全性中的限制。在大多数情况下,这里并没有给出具体的建议,而是提供一些重要的信息,帮助您维护和管理安全的基础架构。
计算单元信任和隔离
WebSphere Application Server 计算单元不应跨越信任边界。如果不能完全信任某些人,则不要让他们管理您的计算单元或管理计算单元内的计算机。WebSphere Application Server 管理基础架构不考虑细粒度的管理安全性,而是在整个计算单元范围内每个 WebSphere 进程之间采取粗粒度的共享信任模型。每个应用程序服务器都包含管理基础架构,包括内部 API。从积极的方面讲,这消除了共同的控制和故障点,使得应用服务器具有很高的独立性和稳定性,但是这也对隔离有负面影响。这种方法的影响包括:
- WebSphere Application Server 计算单元中的每个应用程序服务器都对整个计算单元具有完全的管理权限。如果任何应用程序服务器被攻破,那么所有应用程序服务器都会被攻破。
- 物理计算机边界(独立的计算机、LPAR、节点等等)几乎对计算单元安全性没有任何影响。计算单元中的信任单元是所有节点上的所有应用程序服务器。
- 进程边界对计算单元安全性几乎没有影响。从安全的角度来看,将应用程序放在单独的应用程序服务器 JVM 中对增强计算单元内的隔离性并没有太大用处。
- 单独的操作系统标识对计算单元的安全性几乎没有影响。由于应用程序服务器使用各种协议与计算单元的其他部分进行通信,而这些协议不由操作系统管理,因此一般的操作系统保护措施没有效果。
这就引出了两个关键主题:管理隔离和应用程序隔离。
管理隔离
对于 WebSphere Application Server,在默认情况下所有管理员都对整个计算单元具有管理权限(根据分配给他们的角色)。在 WebSphere Application Server V6.1 中增加了一个称为授权组的新特性,因此可以采用更细的粒度(服务器、应用程序、节点、集群等等)授予管理权限。在 V6.1 中,注册授权组的定义只由 wsadmin 支持。在 WebSphere Application Server V7 中,管理控制台也支持它们。如果您的计算单元很大,有许多管理员,那么应该考虑使用这些功能。
应用程序隔离
在此上下文中,应用程序隔离基本上是关于如何防止一个应用程序的恶意操作对另一个应用程序造成损害。此类攻击很难避免。现实情况是,基础架构软件产品(比如 Java EE 应用程序服务器)尚未达到多用户操作系统的成熟水平。它们无法提供操作系统通常在多个用户之间提供的可靠隔离。
这会影响您吗?
首先也是最重要的,此处讨论的缺陷都只是内部缺陷。只有安装到计算单元中的应用程序才能利用这些缺陷。根据 IBM 的经验,绝大部分 WebSphere Application Server 用户(甚至包括使用共享基础架构的用户)都不受这些问题影响。这是因为他们认识到了其共享基础架构位于企业内部,运行的是得到企业认可的代码。因此,他们通常不要求应用程序实现完全的安全隔离。
受到此处的问题影响的 WebSphere Application Server 用户通常具有非常严格的安全策略,例如:
- 具有正式的策略,要求应用程序不能访问其他应用程序的数据,即使它们在共享基础架构中运行。
- 对每个应用程序在共享服务器上以单独的用户 ID 运行(企业中早于 WebSphere Application Server 的系统的常见要求)的要求,详细地规定关于文件系统权限的规则,每个应用程序都具有专用进程,并严格执行针对这些要求的审计过程。
- 具有严格的企业安全方针,严格执行这些方针,确保它们得到了遵从,这些方针中很可能包括应用程序架构和代码检查。如果没有代码检查,开发人员可能会插入违反企业策略的代码。
- 设计策略,确保可能损害一个应用程序的远程攻击无法用来损害其他应用程序。
如果您的环境没有这些特点,那么本节中的问题对您不适用,可以 跳到下一节。
可以采取何种缓解措施?
可以采取以下缓解措施:
- 对所有应用程序代码执行严格的代码检查。最好配合使用源代码管理系统。这样,对于每次代码更改,都会跟踪进行更改的人员,并针对每次代码更改安排和跟踪代码检查。在这种情况下,单个程序员不可能将恶意代码插入到您的系统中。相反,进行任何破坏都需要多人合作。我们认为这相当重要,因为即使实现了应用程序隔离,恶意的应用程序代码也很容易在单一应用程序中造成严重的损害。
- 如果您选择购买在 WebSphere Application Server 上运行的商业应用程序,请确保仅从可靠的、值得信任的供应商处购买,对应用程序进行细致的测试和监视,然后再进行生产部署,并在生产环境中对其进行监视。
- 如果确实有必要,则应将应用程序部署到私有的 WebSphere Application Server 计算单元中。可以考虑根据风险和信任程度,对不同的业务单位或其他组织单位使用不同的计算单元。为了限制硬件成本,可以在单一计算机或 LPAR 上安全地运行来自多个计算单元的节点。只需使用不同的操作系统用户 ID、不同的加***和不同的管理密码运行每个计算单元即可。从安全角度而言,这将提供完全的隔离。
如果这些方法都无法接受,那么可以对基础架构采用应用程序隔离加强技术。请注意,这些技术都要求做大量的工作。更重要的是,它们不能保证提供隔离。在同一个计算单元中的应用程序(即使启用了管理安全性、应用程序安全性和 Java 2 安全性)可能会访问其他应用程序的资源或改变计算单元配置,从而损害同一计算单元中的其他应用程序。无法保证不会出现此类破坏。
清单 9 中的代码演示了需要执行代码检查来实现有效的安全性的另一个例子。
清单 9
Map<String, String> map = new HashMap<String, String>(); map.put(Constants.MAPPING_ALIAS, "cesar-pcNode03/apstsegu"); CallbackHandler callbackHandler = WSMappingCallbackHandlerFactory.getInstance().getCallbackHandler(map,null); LoginContext loginContext = new LoginContext("DefaultPrincipalMapping", callbackHandler); loginContext.login(); Subject subject = loginContext.getSubject(); Set<Object> credentials = subject.getPrivateCredentials(); PasswordCredential passwordCredential = (PasswordCredential) credentials.iterator().next(); String user = passwordCredential.getUserName(); String password = new String(passwordCredential.getPassword());
这段代码利用一个 WebSphere Application Server 安全性 SPI 来获取计算单元中任何 J2C 别名的用户 ID 和密码。因为此 SPI 的用途是用于应用程序安全性上下文扩展和自定义,所以该 SPI 必须能够访问这部分敏感的安全信息。结果是,阻止恶意代码利用此插入点(和其他类似插入点)的惟一方式是执行代码检查,确保应用程序不使用插入点,除非业务需求要求使用它们。
发出了这些警告之后,下面几节将继续讨论可以显著提高计算单元内的应用程序安全隔离的措施。您可能很快发现这些措施很难实施,而且成本非常高。更好、成本更低的方法是,通过严格的雇员管理、代码检查和其他管理控制机制,确保您的应用程序开发人员交付可信的代码。与前面一样,这些措施的优先次序如下。
- 不要在 Java EE 资源上指定组件管理的别名
- 不要在资源上定义默认的用户 ID 和密码
- Java 2 安全性
- 利用安全连锁委托
- 保护 TAM WebSEAL TAI 密码
- 注意定制 JMX 代码中提升的权限
- 谨慎地使用 DynaCache
- 小心地使用所有资源
1. 不要在 Java EE 资源上指定组件管理的别名
在计算单元内运行的任何 Java EE 应用程序都可能访问任何 Java EE 资源。这是因为资源具有 JDNI 名称,而任何应用程序都可以查询此名称,而且对于资源访问没有授权机制。因此,如果应用程序 A 要使用企业数据库,只需将该数据库定义为数据源,同一计算单元中的应用程序 B 就可以访问此数据库。
当应用程序试图通过调用资源工厂(例如数据源或 JMS 连接工厂)上的 getConnection() 来访问资源时,如果相应的底层资源可用,那么 WebSphere Application Server 会隐式地向底层资源提供身份验证信息。提供何种身份验证信息取决于身份验证模式和可用的 J2C 身份验证别名。具体的细节非常复杂,但简单来说,任何应用程序都可以查找 JNDI 名称空间中的任何资源。完成此工作后,将隐式地使用 “应用程序” 的身份验证模式。这又意味着 WebSphere Application Server 将使用组件管理的身份验证别名(如果有的话)。因此,计算单元中的任何应用程序都可以访问任何使用组件管理的别名定义的资源。请注意,在不同的范围内定义资源对安全性并没有影响。资源范围仅仅是方便管理的机制。
另一方面,如果只在资源上定义容器管理的别名(或者不指定别名),那么恶意应用程序就无法访问此资源,因为当在全局 JNDI 名称空间中查找资源时,将使用应用程序身份验证,因此会使用组件管理的别名。因为没有组件管理的别名,所以无法完成隐式身份验证。
如果您选择采用此方法,显然仍然希望恰当的应用程序能够访问这些资源。为此,这些应用程序必须在其部署描述符中指定用于访问资源的本地资源引用。然后可以在部署时将这些引用绑定到正确的资源。如果应用程序在引用上指定了容器管理的身份验证,则将隐式地使用在资源本身上定义的容器管理的别名。这种情况可以使用一张图加以说明。图 3 显示 IBM Rational Application Developer 引用编辑器,其中在一个 JMS 连接引用上指定容器管理的身份验证。
图 6. 使用容器管理的身份验证的数据库资源引用
这种方法有两个问题。首先,在 WebSphere Application Server V6.1 中,容器管理的别名已经被错误地否决了(但是在 V7 中不再被否决)。其次,也是更重要的,您可能已经注意到,并非所有的资源都允许指定容器管理的别名(例如 V6.1 中的 JMS 工厂)。在这种情况下,一定不要在资源上提供默认身份验证信息。必须在应用程序中为每个资源引用指定身份验证信息(在这里身份验证方法并不重要),比如在部署期间。此工作非常单调乏味,但是可以使用 wsadmin 自动执行。至少对于 MDB,可以在**规范中指定身份验证信息。
XA 恢复别名不是问题
不要将组件管理的别名与资源上指定的 XA 恢复别名相混淆。XA 恢复别名仅在涉及到事务故障的某些恢复场景中使用。如果启用 Java 2 安全性并使用默认权限,那么应用程序通常无法访问该别名。
2. 不要在资源上定义默认的用户 ID 和密码
前一条的一个推论是,不应在资源上定义默认的用户 ID 和密码。如果这样做,那么计算单元内的任何应用程序都可以查找该资源,然后隐式地使用所提供的用户 ID 和密码。
3. Java 2 安全性
正如在前面讨论的,所有应用程序服务器均包含 WebSphere Application Server 管理基础架构,因此也包含用于执行大部分管理操作的 API。学习了这些 API 的应用程序员可以编写调用其中任何 API 的应用程序,因而实质上可以执行任何管理操作。此外,文件系统配置存储库包含大量敏感信息(如密码)。任何应用程序都可以使用普通 Java I/O 来读取这些文件。
正如在前面讨论的,所有应用程序服务器均包含 WebSphere Application Server 管理基础架构,因而也包含用于执行大部分管理操作的 API。学习了这些 API 的应用程序员可以编写调用其中任何 API 的应用程序,因而实质上可以执行任何管理操作。此外,文件系统配置存储库包含大量敏感信息(如密码)。任何应用程序都可以使用普通 Java I/O 来读取这些文件。但是,如果不通过正式的过程检查授予的权限,那么使用 Java 2 安全性就没有什么价值,实际上,如果启用它而不检验策略文件,情况会变得更糟糕,因为安全性并未提高,而应用程序开发更困难了,性能也会略微降低。
一旦启用了 Java 2 安全性,在默认情况下,应用程序就被限制为仅使用很小的 “安全的” 权限集。如果应用程序需要更多的权限,它通常必须在 EAR 内包含的 was.policy 文件中定义这些权限。应用程序在运行时会读取 was.policy 文件,并将这些权限添加到标准集中。显然,这是一个潜在的安全漏洞。为了让这种机制正常发挥作用,需要通过严格的、定义良好的过程决定、检查和实施每个应用程序的 Java 2 权限。如果任何应用程序试图请求不可接受的权限(即使在处理紧急情况期间),则应拒绝它。应该有一个正式的过程,其中包含判断允许应用程序具有哪些权限的安全检查。即使有正式的过程,启用 Java 2 安全性也应该小心谨慎;除了需要多做一些工作之外,强制启用 Java 2 安全性常常导致使用不适当的策略文件,为了实现部署,这些策略文件往往很少提供有意义的保护。这不但要多做一些工作,还会产生虚假的安全性。
安装时的检查和检验过程可能十分单调乏味。不过,可以采用另一种方法。首先,对于很多环境而言,大部分应用程序都需要一个公共附加权限集。如果可以这样做,那么基础架构团队可以在节点上的 app.policy 文件中为所有应用程序设置默认权限。只有需要不常用的权限的应用程序才需要将所需权限放置在 was.policy 中,并进行额外的检验。您甚至可以进一步设置一些限制,禁止使用 was.policy,并要求由管理团队将所有权限添加到 app.policy 中。这在某种程度上会使得部署变得复杂(需要编辑一个公共文件),但可以减少应用程序获得不恰当权限的风险。
对此方法的一项完善是在不同的生命周期环境中以不同的方式指定 Java 2 安全性。
- 在开发环境中,启用 Java 2 安全性,定义自定义属性 com.ibm.websphere.java2secman.norethrow=true。这会导致日志出现关于需要哪些 Java 2 安全性权限的警告,但不会抛出任何 Java 2 安全性异常。
- 在测试环境中,启用 Java 2 安全性,但没有定义该自定义属性。如果应用程序没有包含必要的策略文件,那么这些应用程序就不会运行。在测试环境中,对可接受策略文件所做的更改必须按上述方法严格审查。
- 在生产环境中,不要启用 Java 2 安全性,这会避免性能开销。仅将在测试中通过了审查的应用程序部署到生产环境中。
请注意,这项完善不会减少审核对验证策略更改的需求。
(启用 Java 2 安全性会带来不小的运行时性能成本。两个非常重要的安全性自定义属性可减少这一成本(但不会完全消除它)。它们是 com.ibm.websphere.security.auth.j2c.cacheReadOnlyAuthDataSubjects=true 和一个适当调优的 com.ibm.websphere.security.auth.j2c.readOnlyAuthDataSubjectCacheSize。请在 信息中心 中查阅这些属性。)
4. 利用安全连锁委托
应用程序服务器的好处之一就是,会自动在系统层之间和跨应用程序发送用户身份信息。这就实现了透明的单点登录 (SSO)。不过,这有一个可能非常危险的副作用:不恰当模拟。
此处的问题是,当用户为了使用应用程序 A 而进行身份验证时,应用程序 A 可能对应用程序 B 进行远程 EJB 调用。这样,应用程序 B 就会看到原用户的凭证。通常,这没有什么问题。但是,如果应用程序 A 不可信呢?在这种情况下,访问应用程序 A 的用户就有理由担心别人会通过模仿以此用户的名义访问应用程序 B。设想一下,假如应用程序 A “不重要”,因此是采用临时拼凑的方式开发的;而应用程序 B 是管理机密信息的高度敏感的应用程序。此处的问题是,因为应用程序 B 与应用程序 A 共享一个公共安全域,所以必须信任应用程序 A。这种做法非常不好。
有一个办法可以解决此问题。可以使用 WebSphere Application Server 中的一个特性,此特性被大致描述为 “安全连锁委托”。通过使用 WSSecurityHelper.getCallerList() 或 getServerList(),应用程序 B 可以确定请求经过了哪些应用程序和服务器。如果应用程序 B 是高度敏感的应用程序,可能要求它将该列表设置为空,表示它由用户直接使用。关于 WSSecurityHelper 的更多信息,请参阅 WebSphere Application Server 信息中心。
5. 保护 TAM WebSEAL TAI 密码
当在 Tivoli Access Manager WebSEAL 和 WebSphere Application Server 之间配置了 SSO 时,WebSEAL 会在每个请求的 HTTP 标头中发送其保密密码,以确保 TAI 在调用时能正常工作。虽然由于连接应该使用 SSL 进行加密,所以通常这不是什么问题,但这样的确会向 WebSphere Application Server 中运行的 Web 应用程序公开 WebSEAL 密码。(尽管本节专门讨论 WebSEAL TAI,但是通过发送密码来建立信任的任何 TAI 都会发生这种情况。)
首选的方法(如 第 1 部分 中所述)是使用 Enhanced Tivoli Access Manager TAI 代替 WebSEAL TAI,不使用秘密密码来建立信任。
如果运行在 WebSphere Application Server 中的某个 Web 应用程序不可信,那么它可能会获取此密码,然后打开到某个应用程序服务器的 HTTP 连接,并断言任何用户的身份。这样,精心编写的恶意应用程序就可以冒充任何人进行操作了。
如果担心出现这种类型的攻击(可以通过代码检查轻松地防止),那么可以阻止任何不可信的客户机连接到 Web 容器。为此,只需在 Web 容器上配置一个相互身份验证的 HTTPS 监听器即可,请参阅 第 1 部分 中的说明。这样,恶意应用程序就无法打开到 Web 容器的 HTTPS 连接,因为它们没有正确的私钥(只有 WebSEAL 或 Web 服务器拥有此私钥)。
6. 注意定制 JMX 代码中提升的权限
对于 Mbean,需要注意一点是:要想使用它们,就必须提升 Java 2 安全性权限。如果应用程序以编程方式向应用服务器运行时注册 Mbean,则必须向发出调用的代码,授予提升的管理权限。进行此操作时需格外小心。这种权限可用来执行非法管理操作。一种不错的方法是创建一个专门用于注册 Mbean 的单独模块,并仅授予此模块所需的权限。
第二种装载 MBean 的方法是以管理方式将其指定为 Extension Mbean(这是推荐的方法)。这样就消除了必须显式地授予应用程序代码管理权限的问题,但又出现了一个新问题:MBean 现在由相当低层的 WebSphere Application Server 类装载器进行装载,此装载器的信任度更高。因此,与采用普通用户代码相比,MBean 具有更多访问 WebSphere Application Server API 的权限。
如果选择开发定制的 Mbean,必须仔细地对代码及其使用情况进行检查,确保不会给系统带来安全漏洞。
7. 谨慎地使用 DynaCache
DynaCache 为 WebSphere Application Server 应用程序提供了分布式共享缓存。不过,DynaCache 上并没有访问控制机制;在应用程序服务器中运行的任何应用程序都可以访问此应用服务器具有访问权限的任何缓存。更准确地说,任何应用程序都可以访问服务器能够访问的任何缓存,可以看到(或修改)缓存的所有内容。
有两种缓存需要考虑。应用程序服务器本身维护一些隐藏的内部缓存(servlet 缓存、Web 服务缓存和安全缓存),其中可能包含敏感信息。还有用户创建的缓存,比如可以通过 JNDI 访问的 Object Cache。
内部缓存并不在 JNDI 中注册,所以更难找到它们,但是可以通过使用内部 API 访问它们。从积极的方面来看,在默认情况下这些缓存只复制到同一集群中的服务器,所以可以确信不同集群中的应用程序无法看到其他集群的缓存内容。
用户定义的缓存可以在 JNDI 中看到,可以在同一计算单元中的任何服务器上查找它们。查找到缓存之后,应用程序就可以修改或读取缓存。无法防止这种行为。因此,如果关心应用程序隔离,就不要使用用户创建的缓存。
8. 小心地使用所有资源
其他很多 WebSphere Application Server 资源(如工作区)并没有提供应用程序级的授权。与 DynaCache 一样,您必须小心地使用这些资源。如果关心应用程序隔离,则应该仔细地对每个使用场景进行评估,查找潜在的漏洞并采取相应措施。
跨计算单元的信任
通常,WebSphere Application Server 计算单元并不是彼此信任的,因此不可能实现跨计算单元的 SSO。不过,可以对计算单元进行配置,使其支持跨计算单元 SSO。这么做有充足的理由,但是在这么做时,实际上是在扩展计算单元的信任域,需要注意对安全的潜在影响。您需要考虑三个问题:
-
共享 LTPA **
要想让两个计算单元透明地参与 SSO 域,则必须让它们共享兼容的域(在 WebSphere Application Server V6.1 中这意味着同一个域,在 V7 中意味着可信域)、共享相同的 LTPA 加***,并使用兼容的 SSL keyring(供服务器用于服务器通信)。兼容的 SSL keyring 意味着,与任何 SSL 通信一样,发出调用的服务器必须能够访问与接收调用的服务器的证书对应的签名证书。
一旦确保两个计算单元共享了相同的 LTPA 加***,就创建了一个特殊的环境,此环境中的每个计算单元都可以为其他计算单元创建凭证,包括管理凭证。因此,如果一个计算单元被攻破,那么两个计算单元都会被攻破。如果使用多个计算单元,并且由于安全原因需要实现应用程序隔离,那么需要启用 Java 2 安全性来限制对 WebSphere Application Server 内部 API 的访问。
如果共享相同的 LTPA 加***只是为了创建 Web SSO,而不需要共享定制的主题,那么可以通过使用身份验证代理服务器(比如 Tivoli Access Manager WebSEAL)和一个相应的 TAI(比如 Extended TAM TAI)实现相同的结果,而不必共享 LTPA 加***。通过一个代理和一个 TAI 实现 Web SSO 是共享 LTPA **的首选方法。
-
CSIv2 身份断言
如果要进行跨计算单元的 IIOP 调用,但希望避免共享 LTPA 加***,这是完全可以实现的。为此,必须使用 CSIv2 身份断言(当与非 WebSphere Application Server EJB 服务器进行联系时,这也很有用)。
让我们考虑一个简单的场景:假定有两个计算单元(A 和 B),它们均包含多个服务器。假定计算单元 A 中的服务器需要对计算单元 B 中的服务器进行 RMI/IIOP 调用,但不会进行相反的调用。为了实现此功能,需要配置 CSIv2 身份断言。计算单元 A 中的服务器将向计算单元 B 中的服务器断言身份。这里不描述如何配置 CSIv2 身份断言,只讨论这样做的影响。
为了让计算单元 B 中的服务器接受身份断言,计算单元 A 中的上游服务器必须首先对自身进行身份验证。可以采用两种方式来对 CSIv2 进行此操作:基本身份验证(上游服务器发送其用户 ID 和密码)和客户机证书身份验证(上游服务器使用自己的证书进行身份验证)。
身份验证完成之后,接受调用的服务器将检验上游服务器是否可信,是否可以执行身份断言。这是在 CSIv2 配置面板中配置的。此后,上游服务器向下游服务器发送目标用户的身份信息。
让我们考虑一下此方法的信任影响。计算单元 B 中的服务器接受来自计算单元 A 的身份断言,因此信任计算单元 A。如果计算单元 A 被攻破,则计算单元 B 也会被攻破,但对于计算单元 A 有何影响呢?
- 如果计算单元 A 发送服务器用户 ID 和密码来建立信任(这是默认行为),这会让计算单元 B 中的服务器知道其服务器用户 ID 和密码,而此用户 ID 具有完全的管理权限。因此,计算单元 A 现在完全信任计算单元 B。与只共享 LTPA **相比,这并未提高安全性。
- 如果计算单元 A 发送一个指定的用户 ID 和密码(此 ID 只用于此用途),就不会向计算单元 B 公开重要的信息。计算单元 A 不信任计算单元 B。
- 如果计算单元 A 使用自己的证书进行身份验证,则不会向计算单元 B 公开任何内容。计算单元 A 不信任计算单元 B。
总之,为了让计算单元 A 对计算单元 B 断言身份,计算单元 B 必须信任计算单元 A。这非常明显。如果不希望计算单元 A 信任计算单元 B,则应该在服务器到服务器身份验证步骤中使用指定的用户 ID 和密码或证书身份验证,而不是使用默认的方式(发送服务器 ID 和密码)。
-
主题传播回调
如果要使用跨计算单元 主题传播,则必须注意计算单元还可能相互进行带外调用来获取主题。为了说明这一点,我们来考虑一个例子:假定有两个服务器共享一个 SSO 域。某个用户使用 Web 浏览器访问服务器 A 并获得了一个身份验证会话(在浏览器中由 LTPA cookie 表示)。该用户随后访问服务器 B。服务器 B 将试图从服务器 A 获取该用户的主题。这称为主题传播。如果这些服务器位于相同的 DynaCache 复制域中,则很容易完成此操作。如果它们不位于相同的 Dynacache 复制域中,则使用 JMX 回调来获取主题。当然,不同计算单元中的服务器不处于相同的 DynaCache 复制域中。因此,在这个示例中,服务器 B 将对服务器 A 进行安全 JMX 调用,以获取用户的主题。
与任何管理调用一样,JMX 调用需要进行身份验证和授权。在这种情况下(从 WebSphere Application Server V6.1 开始),通过使用共享的 LTPA **隐式地建立信任。因为服务器 B 与服务器 A 共享 LTPA 加***,所以服务器 A 信任它,会提供主题信息。
总而言之,除了由于共享 LTPA **已经引入的风险之外,回调并不会显著影响安全性,但是它们引入了另一个网络路径,一些人可能认为这是风险。
这并不适用于出现使用 IIOP 的下游传播时。在这种情况下,上游服务器会直接将主题发送到下游服务器。不需要进行 JMX 回调。
身份传播
虽然此主题与安全性加强并不直接相关,但它是一个在系统设计中经常出现且没有尽早考虑安全性的问题。必须始终非常谨慎地对在何处建立身份以及如何传播身份(如果可以传播)进行跟踪。我们见过很多设计直接假设身份是已知的,而实际技术却使这不可行。要确保对应用程序中的身份流进行仔细的分析,以防止在后面的开发周期造成大麻烦。下面给出涉及到外部资源和解决方案(对于 WebSphere Application Server V6 和更高版本)的两种常见情况。
-
数据库与 WebSphere Application Server 身份验证
企业系统的主要挑战之一就是恰当地实现强大的系统安全控制。简单来说,需要用恰当的授权保护关键数据。对于多层 Java EE 系统(其中的 Java EE 应用程序代码会访问数据库,比如使用 JDBC、SQLJ、JPA 或 CMP bean)来说,这个挑战尤其难以解决,因为用户的身份信息通常会丢失。这里的 “用户” 指的是应用程序代表其进行操作的用户;也就是说,如果 Bob 使用标准 Java EE 安全性向应用程序进行身份验证,那么 Bob 就是用户。
在典型的基于 Java EE 的系统中,容器维护一个经过身份验证的连接池。每个用户都会经过应用程序服务器的身份验证(使用几种 Java EE 身份验证机制之一),但用户的身份信息对数据库是不可用的。这是因为所有数据库访问都是使用连接池中的公共共享连接之一执行的。在过去,这导致应用程序不得不在应用层内重新实现现有的数据库级授权和审计功能。即使处理得当,也会浪费资源,而在处理不得当时,很可能非常不安全。
在 WebSphere Application Server V6 中,对此问题有一个 精致的解决方案。V6.1 中提供了把身份传播到 IBM DB2® 的机制。®
-
MDB 不以排队者的身份运行
当消息在消息传递系统中排队时,原始调用者的身份通常不会与其相关。也就是说,消息传递引擎根据前面讨论的连接身份授权对队列的访问。队列通常甚至不会记录最终用户的身份。
当消息离开队列时(在 Java EE 中通常由 MDB 将消息从队列取出),原始调用者的身份不可用 —— 即使此信息可用,也会被忽略。MDB 以匿名 Java EE 身份或静态 run-as 身份运行。它们不会以消息排队者的身份执行。
在 WebSphere Application Server 中没有用于处理此问题的直接支持。不过,如果您愿意编写一些定制的代码,那么可以解决此问题。从 WebSphere Application Server V6.0.2 开始,WebSphere Application Server 支持服务器端身份断言。也就是说,服务器端代码可以使用 JAAS 更改其 Java EE run-as 身份,不需要提供密码。它直接断言用户身份信息。下面是一个使用 Java 代码实现此功能的简单示例:
清单 10
CallbackHandler wscbh = WSCallbackHandlerFactory.getInstance().getCallbackHandler(username, null); LoginContext lc = new LoginContext("system.RMI_INBOUND", wscbh); lc.login();
请注意,这里没有提供密码。
这可以与 关于高级身份验证的这篇文章 中的思想相结合,以断言定制的主题。按照 这篇信息中心文章 中的描述,还可以让 WebSphere Application Server 创建 LTPA cookie,让 Web 应用程序能够实现这个功能。
很明显,这存在严重的安全隐患,因此在使用 Java 2 安全性时在默认情况下这个调用被阻止。但是,通过向应用程序代码授予所需的 Java 2 安全权限,就可以使用它。
WebSphere Application Server 限制
一般来说,通过仔细地进行配置,可以使用 WebSphere Application Server 创建高度安全的健壮的环境。不过应该注意,一些通常很小的、不太明显的限制可能会对您有所影响。
目标身份未经检验
安全系统的一个非常微妙的方面是检验请求目标的概念。通常,当提到身份验证时,我们会想到服务器验证客户机的身份,但客户机如何验证服务器的身份呢?客户机如何知道服务器确实是它所期望连接的服务器呢?大部分人没有认识到这一点,但 Web 浏览器每天都会隐式地执行此项检查。当使用 HTTPS 时,Web 浏览器将会检验 Web 服务器的主机名是否与其证书中的 Web 服务器的主题相同。这可以确保服务器确实是用户所希望连接的服务器(当然,假定用户知道他们使用的主机名;很多人并不这样细心)。
不太明显的是,Web 浏览器执行的此项检查并不是 SSL 的固有部分,而是在 SSL 之外执行的特定于浏览器的检查。发起方(发出调用的服务器)必须明确地对证书执行此检查。WebSphere Application Server 并不执行此检查。因此,当应用程序服务器(或客户机)打开到服务器的 SSL 连接时,它并不能确信该服务器就是希望使用的服务器。尽管可能性很小,但是如果黑客可以攻破您的内部 DNS 或网络,就可以在 WebSphere Application Server 计算单元中插入一个欺诈服务器并窃取信息。这是一种异常困难的攻击(很多其他攻击要简单得多),但应当注意这种可能性。
可以从应用程序服务器信任存储文件中删除任何不需要的签名**,以防止出现这种情况。只有使用可信的签署者颁发的证书才能发起这种攻击。在 SSL 握手期间,客户机会拒绝无法检验的服务器端证书。如果可信列表很小(或许仅包含自签名证书),则发起此类攻击会非常困难。因为信任存储在默认情况下不包含任何 CA 签署者,默认签署者惟一要关注的是,如果在共享的计算单元信任存储中添加 CA 签名证书,则会遇到这种风险(尽管风险不大)。
保护桌面开发环境
在考虑安全性加强时,大多数人习惯于将注意力集中在生产系统上,生产系统自然是最重要的。不过,您也应该花一些时间来确保其他计算机(包括桌面系统)也具有合理的安全性。
对于这些运行 Rational Application Developer(或早期的 WebSphere Studio)的计算机,这是一个非常重要的问题。桌面 IDE 包含一个功能齐全的嵌入式 WebSphere Application Server 运行时环境。此应用程序服务器具有开放的端口,易于受到本文前面描述的各种方式的远程访问攻击。需要对此进行保护。
加强嵌入式应用程序服务器
至少应该在嵌入式应用程序服务器测试环境中启用管理安全性。Rational Application Developer 7.5 在默认情况下已经启用了管理安全性。这可以防止最严重的攻击类型,其中入侵者可能使用内置的管理基础架构将恶意应用程序部署到您的桌面上。如果使用一个具有管理权限的操作系统用户 ID 来运行 Rational Application Developer,这相当重要。您可能还希望采取本文中提到的其他加强步骤,不过确保管理基础架构的安全是最为重要的步骤。
加强嵌入式应用程序服务器的另一种方法是安装桌面防火墙产品,阻止对您计算机的访问。如果配置得当,此方法会非常有效。但是,信任整个内部企业网络的防火墙在此场景中几乎没有价值。
从以前的版本迁移
在 WebSphere Application Server 的不同版本之间迁移可能导致向后兼容性问题。从安全性的角度来看,推荐的应用程序迁移方法是从头构建一个完整的新计算单元,然后在新的计算单元中安装应用程序(当然先要进行严格的测试)。通过采用这种方法,可以受益于新版本的所有默认配置更改。
如果使用 WebSphere Application Server 计算单元迁移工具,它们会把现有的配置和应用程序复制到新的计算单元,因此不会受益于每个主要版本对安全性的许多改进。这是因为我们会尽可能少修改现有的配置,以确保应用程序能够继续正常运行。这么做的副作用是加强默认安全性的配置更改不会生效。因此,如果使用工具从 WebSphere Application Server V6 迁移到 V7,那么应该阅读本文的 V6 和 V7 版本,而不只是最新版本。
结束语
这篇由两部分组成的文章讨论了大量内容。虽然我们讨论了有关安全性的很多方面,但是重点放在加强 WebSphere Application Server 环境这一核心主题上。希望您已经获得了保证 Java EE 系统安全所需的基本信息。本文的内容并不全面,您应当从其他信息源查找关于加强基础架构的其他部分的信息。WebSphere Application Server 只是冰山的一角。
您可以使用一个称为 IBM Security Scanner for WebSphere Application Server 的自动化工具对本文中所描述的一部分事项进行检查,可以 下载这个工具。
最后,如果希望进一步了解 WebSphere Application Server 安全性,请联系 IBM Software Services for WebSphere,参加关于 WebSphere Application Server 安全性的定制的现场课程。这个课程深入讲解安全性加强、定制身份验证、集成、单点登录和各种其他相关主题。