微信号:importnew

介绍:伯乐在线旗下账号,专注Java技术分享,包括Java基础技术、进阶技能、架构设计和Java技术领域动态等.

Java 应用程序漏洞:概念及如何修复

2019-01-25 11:30 ImportNew

(给ImportNew加星标,提高Java技能)


来自:唐尤华

译自:https://dzone.com/refcardz/java-application-vulnerabilities


与其他任何应用程序一样,Java 应用程序也存在安全漏洞。 本文重点介绍了可能影响 Java 应用程序前几名的漏洞以及如何消除这些漏洞。


1. 引言


过去15年中开发得企业应用程序有一半是用 Java 编写的,这使得它们在企业中几乎无处不在。不幸的是,这意味着 Java 应用程序也是黑客最常攻击的目标之一。


对于一般漏洞和一些 Java 平台特有漏洞,Java 的防范都很薄弱。 例如,一般类型的漏洞包括:


  • 标准库中的漏洞

  • 编程错误造成的漏洞(比如查询结构不当)


Java 平台特有漏洞包括:


  • Java 库中的漏洞

  • Java 沙盒机制中的漏洞,攻击者会利用它们绕过安全管理器设置的限制


开发安全的 Java 应用程序,免受上述漏洞的影响,是确保应用程序健壮并免受安全威胁的最佳方法。 将安全性纳入开发工作流程有助于开发人员避免产生漏洞,在开发过程中纠正潜在的漏洞,在时间和资源上都比在生产环境中发现漏洞成本低得多。

本文旨在帮助开发人员理解常见的 Java 漏洞,以及如何在开发过程早期修复它们。


2. 最大的 Java 漏洞


下面是最常见、最普遍、最重要的 Java 漏洞。 这份列表根据2017年白帽安全应用程序安全统计报告编译而成,记录了 Java 应用程序中发现的漏洞。 介绍了每种漏洞类型、发生的方式和位置、如何修复漏洞实例,以及关于该漏洞的其他一般信息。


获胜者是……


在软件开发过程中,静态应用程序安全测试(SAST)中最常见的代码漏洞是 未打补丁的开发库(Unpatched Libraries)。为什么?因为现代软件大部分是由单独的组件组合而成的,而且现在每个人都使用开源库。 这些库提供了现成的选项,但不是很安全。


同样的情况适用于第二种常见错误:错误的应用程序配置。许多软件组件(比如嵌入式调试和 QA 功能)对安全性几乎没有考虑,开发人员会默认启用这些配置。这样为供攻击者提供了可利用的配置漏洞。这些启用的功能会被用来绕过身份验证,进而获得访问敏感信息的权限,甚至有可能让攻击者用来提升更高的特权。


解决办法?软件组成分析(SCA)对于保护第三方或开放源码代码至关重要。 SCA 深入研究了专有、开放源码和商业程序的源代码,进而识别和列出所有存在的漏洞。

关键的软件错误(比如 SQL 注入)必须在开发过程中修复,减少在生产环境中发生漏洞。



3. 未打补丁的开发库


关键风险:OWASP A9:统计报告排名1


描述

未打补丁的开发库会给应用程序带来严重的风险。使用这样的开发库可能会引入漏洞,绕过现有的安全管控。攻击者可以利用一些众所周知的信息来源,比如国家漏洞数据库(National Vulnerability Database)、 US-CERT、 CVE 数据库等,来识别这些潜在的漏洞,并利用它们带来威胁。


解决方案

确保组件及时更新并打好补丁。监控上报的漏洞,以便及时采取行动。使用依赖项管理器(例如 Maven <dependencyManagement> 部分),为多次声明或有依赖传递的库定义最小依赖版本。防止系统和服务泄漏不必要的信息,如版本信息。如果由于某种原因,不能被修补或替换某个易受攻击的开发库,请确保应对措施已经到位,例如正确配置网络防火墙、IDS/IPS 或应用层防火墙。


在选择组件之前,对已知的漏洞进行研究,并将调查结果存储到中央仓库,包括已知的易受攻击的库列表。确保所有开发团队都能访问这些信息,以便可以立即处理易受攻击的开发库,从而减少不小心用到这些组件的可能性。一定要考虑任何已识别的漏洞的实际影响。在某些情况下,风险可能远远高于正常情况;而在其他时候,脆弱性可能与组件的使用方式无关。


建立一个公司层面的治理策略,用来选择、测试和批准开发团队使用的组件。当组件批准使用后,将它们存到中央仓库,并与组织中的其他开发团队共享。对同一个问题,最好只有一个解决方案,而不是多个解决方案。为了进一步提高一致性和安全性保证,应该在整个组织内保持使用相同的版本和补丁级别。


  • 依赖管理工具(例如 Maven)

  • WhiteHat Sentinel Source 提供了相关开发库和框架的信息


其他资源

Maven 依赖管理


OWASP 排名前10的漏洞



4. 应用程序配置不当:Servlet 泄漏


关键风险:OWASP A5:统计报告排名2


描述

Axis 应用程序部署时配置了管理接口,此接口可以在不使用正常的身份验证/访问限制的情况下查看。恶意用户通过此接口能访问意料之外的服务器功能。如果应用程序“仅限内部”使用,那么针对内部网络访问需求的限制可能会减少。 然而,将未经身份验证的管理功能暴露给内部网络仍是不安全的,应被视为具有一定的泄漏风险。


解决方案

从生产环境 web.xml 中删除突出有明显含义的代码片段。 因为 AdminServlet 和 SOAPMonitorService 都不支持身份验证,所以禁用这些 servlet 是唯一的安全选项。


其他资源

Apache Axis:Web 服务安全指南


5. 应用程序配置不当:权限过多


低风险:OWASP A5:统计报告排名2


描述

应用程序可以使用自定义权限,允许单独的应用程序通过 API 访问硬件层功能。这些独立的应用程序可以通过 API 绕过正常的提示过程访问敏感功能。


解决方案

应用程序应该只请求功能所需的最小权限,不要请求任何不必要的权限,不要请求任何未使用的权限。应用程序将来应该提示用户撤消不需要的权限。


6. 应用程序配置不当:禁用全局错误处理


中等风险:OWASP A5:统计报告排名2


描述

禁用全局错误处理机制会增加风险,攻击者通过堆栈跟踪信息会了解更加详细实现细节。


解决方案

为了让错误消息泄露实现细节的风险降到最低,应确保应用程序部署时声明一个错误页面,用以捕获应用程序抛出的所有未捕获的异常。


示例

web.xml 应当定义错误处理:


<error-page>
   <error-code>500</error-code>
   <location>/path/to/default_500.jsp</location>
</error-page>
<error-page>
   <exception-type>java.io.IOException</exception-type>
   <location>/path/to/default_exception_handler.jsp</location>
</error-page>


7. 跨站脚本(XSS)


高风险:OWASP A3:统计报告排名3


描述

跨网站脚本漏洞指,攻击者在表单或查询变量中嵌入恶意客户端脚本或 HTML 并通过 web 接口提交给网站,恶意内容被发送给终端用户。持久化/跨网站脚本攻击指,恶意内容是由一个用户(攻击者)提交并存储在数据库中,然后发送给另一个用户(受害者)。反射跨网站脚本攻击指,攻击者诱使受害者通过电子邮件或攻击者控制的网站上的链接提交污染后的数据。


击者可以使用 XSS 向不知情的用户发送恶意脚本或其他内容。 最终用户和他们的浏览器都不知道这些内容实际上不是由受信任的网站生成的。恶意脚本可以访问浏览器中的任何 Cookie、会话令牌或其他敏感信息。这些脚本甚至可以重写 HTML 页面的内容,造成网络钓鱼攻击、身份盗窃、网站破坏、拒绝服务和其他攻击。


解决方案

阻止大多数 XSS 攻击的最可靠的方法是,对所有来源的数据使用 HTML 或 URL 编码后输出数据。这样可以确保受污染的数据不会影响任何来源的输出,包括用户输入、其他应用程序共享的信息、来自第三方的信息。对所有输出数据统一编码让应用程序更容易审计,无需执行耗时(且昂贵)的数据流分析。需要注意的是,编码功能必须能够处理不同的上下文输出,包括 HTML、HTML 属性、URL、CSS 和 JavaScript。单一编码方法未必能在每种上下文中减轻 XSS 攻击带来的影响。


如果对输出编码不可行,另一个最有效的方法是根据允许的字符白名单仔细过滤所有输入数据。这种方法的优点是可以在外部执行,无需修改应用程序源代码。对第三方检索数据、其他应用程序共享的数据应与用户输入数据一起过滤。白名单应该只包括可能是用户输入的合法字符。下列字符对于进行 XSS 攻击特别有用,在任何编码或过滤方案中都应该考虑:< > “ ‘ ; & ?。应用程序可能需要这些字符中的一个或多个,例如单引号。如果作为输入过滤方案的一部分无法删除一个或多个危险字符,则必须小心地处理这些字符。例如,可以对输入进行编码,并将这些字符存储在数据库中。理想情况下,应用程序应该谨慎地执行输入验证,并使用输出编码来防范 XSS 和其他注入攻击。


其他资源

  • 跨网站脚本缺陷

  • 预防跨网站脚本指南


8. 密码学:使用不恰当的伪随机数发生器


中等风险:OWASP NA:统计报告排名4


描述

需要随机结果,而软件生成可预测的值,这时就会产生随机性不足。当安全机制依赖于随机且不可预测的值来限制对敏感资源访问时,比如初始向量(IV)、生成密钥或会话 ID 的种子,那么使用随机性不足的数字可能会让攻击者有机可乘,会通过猜测值来访问该资源。


使用随机性不够充分的随机数,潜在后果是数据被盗窃或修改,帐户或系统受到损害,最终名誉扫地。


解决方案

在安全上下文中使用随机数时,请使用加密的安全伪随机数生成器(CSPRNG)。


示例

byte[] randomBytes = new byte[8];
SecureRandom random = new SecureRandom();
random.nextBytes(randomBytes);


9. 应用程序配置不当:调试


中等风险:OWASP A5:统计报告排名5


描述

应用程序错误通常发生在正常操作中,特别是当应用程序被误用时,甚至是无意中发生了错误。如果开启调试,那么在调试时,应用程序可能会向最终用户提供不该被访问内部信息,而且可能会被用来攻击应用程序。最终用户看到的错误信息可能包括服务器信息、详细的异常消息、堆栈跟踪,甚至发生错误的页面代码。这些信息可以被用来制定攻击计划。


如果应用程序提供了生产环境中的调试模式启用开关,攻击者可能会猜到或了解这个参数,并利用应用程序可能提供的各种附加信息,甚至自定义调试模式实现绕过身份验证获得为测试分配管理员级权限。


解决方案

生产环境中应禁用调试模式。除了编程语言本身提供的任何调试模式外,开发人员还可以自定义调试模式。自定义调试选项应该存储在应用程序配置文件中而不是源代码中,但是可能需要搜索代码库以验证没有隐藏的调试选项。


生产代码通常不能进入调试模式或生成调试消息。但如果需要此功能,则应通过编辑服务器上的文件或配置选项来启用调试模式。特别注意,调试模式不应该由应用程序本身中的选项启用。例如,不应该传入 URL 参数来触发调试模式,例如:https://www.website.com?debug=true。无论参数多么模糊,它从来都不是一个安全的选项。


应用程序使用的框架和组件也可以有自己的调试选项。在部署应用程序之前,必须在整个应用程序中禁用调试选项。


示例

Java 应用程序中可能存在许多调试模式的实例,根据容器的类型各有不同。下面是一个禁用 JSP Servlet 调试模式的示例:


<servlet>
   <servlet-name>jsp</servlet-name>
   <servlet-class>oracle.jsp.runtimev2.JspServlet</servlet-class>
   <init-param>
       <param-name>debug_mode</param-name>
       <param-value>false</param-value>
   </init-param>


由于开发人员可能已经实现了自定义调试模式,所以一定要检查配置文件,并在代码库中搜索以下内容:


DEBUG MODE
debug = true
debug = 1
debug


10. 泄漏:明文密码


中等风险:OWASP A2:统计报告排名6


描述

如果应用程序在源代码中包含一个或多个硬编码的密码,那么攻击者可以从源代码或编译的二进制文件中提取凭据,并试图访问相应的服务。使用像 Base-64 这样的加密算法是不够的。


采用明文存储密码(例如在应用程序的属性或配置文件中)可能会导致帐户或系统受到损坏。这会把密码暴露给任何可以访问应用程序配置文件的人,包括开发人员、架构师、测试人员、审计人员和开发经理。由于其他人有可能接触用户密码,所以不能假定帐户的所有者是唯一能够登录该帐户的人。


解决方案

只要有可能,系统密码或者含有密码的配置文件应该加密。


应该用密钥对凭证加密,并且存储到磁盘上的一个网络无法访问的目录。运行的 web 应用程序(webserver)的用户对该目录只具备只读权限。密钥与凭据采取相同的方式处理。随后,应用程序可以(从已知的固定目录)读取密钥、读取加密凭据、解密、使用。每隔30至90天加密密钥应该轮换一次。


用户密码应该使用强大的单向散列算法(如 SHA-256)来存储。在对每个密码进行哈希前应该加盐。盐的长度至少应该是64位,并且对每个用户是随机且唯一。


译注:盐(Salt),在密码学中,是指通过在密码任意固定位置插入特定的字符串,让散列后的结果和使用原始密码的散列结果不相符,这种过程称之为“加盐”。


示例

在 Java 中,有许多可用于加密/解密数据的方法和库。许多常见的方法都不够安全。Java 开发人员通常用 Base-64 或 DES 对密码加密——这两种方法都不够安全,尤其是编码。


下面的代码使用安全算法 AES 来加密解密:


public static String encrypt(String value, File keyFile)
       throws GeneralSecurityException, IOException
{
   if (!keyFile.exists()) {
       KeyGenerator keyGen = KeyGenerator.getInstance(CryptoUtils.AES);
       keyGen.init(128);
       SecretKey sk = keyGen.generateKey();
       FileWriter fw = new FileWriter(keyFile);
       fw.write(byteArrayToHexString(sk.getEncoded()));
       fw.flush();
       fw.close();
   }
   SecretKeySpec sks = getSecretKeySpec(keyFile);
   Cipher cipher = Cipher.getInstance(CryptoUtils.AES);
   cipher.init(Cipher.ENCRYPT_MODE, sks, cipher.getParameters());
   byte[] encrypted = cipher.doFinal(value.getBytes());
   return byteArrayToHexString(encrypted);
}


11. 注入:未知的解释器


中等风险:OWASP A5:统计报告排名7


描述

应用程序在创建和使用解释器的同时也使用了非可信的数据。攻击者获取非可信数据,并将其作为参数传递给解释器进行危险调用。由于无法正确验证或对解释器使用的数据进行编码,从而增加了注入攻击风险。这种注入通常让攻击者得以在程序处理解释器结果时能够执行任意代码。


解决方案

定义一组严格的标准,应用程序应接受哪些作为有效输入,并在执行前对传递给解释器的所有非可信数据根据上下文进行编码。


12. 拒绝服务攻击(Dos):Readline


中等风险:OWASP NA:统计报告排名7


描述

java.io.BufferedReader readLine() 方法用于从套接字或文件读取数据;但是,readLine() 会一直读取数据,直到遇到换行符或回车。如果找不到这两个字符,readLine() 将无限地读取数据。如果攻击者对正在读取的源代码有任何控制,他或她可以注入不带有这些字符的数据,从而在系统中制造拒绝服务攻击。即使读取的行数有限,攻击者也可以传入没有换行字符的大文件,并导致 OutOfMemoryError 异常。


解决方案

OWASP 的企业安全 API 为 readLine() 提供了一个更安全的替代方案,称为 SafeReadLine()。 此方法从输入流读取数据,直到达到行尾或字符数的最大值,从而有效降低了这种风险。


另一个解决方案是重写 BufferedReader 和 readLine() 方法,并对可读取的最大字符数进行限制。在缺乏更安全方法的情况下,尽可能避免接收客户端的输入,并确保读取的数据是可信的。


示例

可以使用 OWASP ESAPI 的 safeReadLine() 安全地读取非可信数据,像下面这样:


ByteArrayInputStream s = new ByteArrayInputStream("testinput".getBytes());
IValidator instance = ESAPI.validator();
   try {
       String u = instance.safeReadLine(s, 20);
   } catch (ValidationException e) {
       // Handle exception
   }


13. 滥用 URL 重定向


中等风险:OWASP A10:统计报告排名9


描述

应用程序经常使用存储的 URL 把用户重定向到其他页面。有时候,目标页面由非可信参数指定,这让攻击者得以选择目标页面或地址。这样的重定向可能不恰当地利用了用户对受攻击网站的信任。


解决方案

如果非可信数据成为重定向 URL 的一部分,请确保提供的数据经过了适当的验证,且来自合法的、经过授权的用户请求。建议重定向目的地由服务器端“目的地 id”驱动,而不是由用户请求中的 URL 决定。查找映射表或访问控制表最适合达成这个目标。


14. 会话超时设置过短


中等风险:OWASP A2:统计报告排名10


描述

PCI 数据安全标准 V3,8.1.8节规定了,应用程序关键组件的会话超时最长15分钟:“如果会话空闲时间超过15分钟,需要用户重新验证以重新激活终端或会话”。

会话超时设置过长或者无超时设置可能帮助攻击者实施重播攻击或劫持会话,社会工程学攻击得手的成功率也更高。如果用户没有关闭应用程序,攻击者有更大的机会控制用户的计算机。


如果没有在 web 应用程序配置中指定会话超时,那么将使用默认值,通常是24分钟、30分钟或60分钟,这取决于 web 服务器、版本及其配置。


解决方案

一般来说,用户会话超时的空闲时间应在15-20分钟以内,对敏感应用程序,超时时间应小于15-20分钟。如果有配置选项,请考虑禁用“滑动过期”。确有必要启用此选项,请考虑在滑动超时之外实现强制会话超时。会话超时后,应用程序应该设置会话无效、删除会话数据以及所有 Cookie 和身份验证令牌。


示例

可以在服务器默认 web.xml 中配置会话超时,也可以为每个 web 应用程序单独配置。 下面的代码应该包含在应用程序的 web.xml 文件中:


<session-config>
   <session-timeout>15</session-timeout>
</session-config>


如果使用了 WebLogic,还需要在 WebLogic.xml 文件中指定会话超时。下面的代码将超时设置为15分钟:


<?xml version="1.0" encoding="ISO-8859-1"?>
<weblogic-web-app xmlns="http://www.bea.com/ns/weblogic/90">
   <session-descriptor>
       <timeout-secs>900</timeout-secs>
   </session-descriptor>
</weblogic-web-app>


15. 访问策略缺失


高风险:OWASP A5:统计报告排名13


描述

应用程序没有为一个或多个组件使用访问控制策略。访问控制缺失可能导致敏感功能暴露给不相关的用户。恶意用户会寻找这种功能,从而对其他用户或应用程序本身造成伤害。


在 Websphere 中,如果启用通过类名 Servlet,那么和 Android 允许通过类名调用类似。 假设存在下面这样的配置或者没有声明变量,则允许调用 Servlet 无需任何权限:


enable-serving-servlets-by-class-name value="true"


解决方案

对应用程序中可能存在的所有敏感功能组件使用访问控制策略。通过添加下面配置,防止 Servlet 按 classname 提供服务:


enable-serving-servlets-by-class-name value="false"


16. 传输层保护不足


描述

需要保护敏感数据传输时,应用程序通常无法对网络通信加密。因此,必须对所有认证连接进行加密(通常是 TLS),特别是 Internet 访问的网页。后端连接也应当加密。否则应用程序会向与应用程序主机位于同一网络中的恶意攻击者泄漏身份验证或会话令牌。虽然比起外部互联网,后端连接被利用的可能性更低。然而,一旦被攻击造成的用户帐户泄露会更严重。


传输敏感数据(如信用卡或健康信息)时,无论何时都应当加密。攻击者可能会滥用回到纯文本处理或因为其他原因被迫退出加密模式的应用程序。


解决方案

确保应用程序具备安全约束,该约束定义了基于机密性和完整性的安全传输保证,确保所有数据在传输过程中不被偷窥或更改。如果必须在负载均衡器、 web 应用防火墙或其他内网主机上终止 TLS 传输,应该对传输到目标主机的数据重新加密。


17. 总结


所有组织都必须在他们的 SDLC 中更早地实现应用程序安全性程序。事实证明,发现和修复缺陷越早越好,而且成本也更低。开发过程中的常规安全测试让开发出的应用程序功能更强大。将多种测试方案(如 DAST、SAST、mobile 等)直接集成到 SDLC 中的组织可以获得最好的效果。当今的应用程序安全平台通过软件组成分析、API 测试、培训和其他服务进一步扩展了可见性和可控性。


从 WannaCry 到9月份的 Equifax 攻击,我们几乎每天都被提醒,不解决应用程序中的漏洞可能导致可怕的攻击,然而这些攻击是可以避免的。在数字商业时代,采用安全的 DevOps 或 DevSecOps 对于确保真正的安全是非常必要的。


看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

喜欢就点「好看」呗~


 
ImportNew 更多文章 浅析几种线程安全模型 分布式、高并发、多线程,到底有什么区别? 谈谈 Tomcat 请求处理流程 如何优雅的设计 Java 异常 哥们,你们的系统架构中为什么要引入消息中间件?
猜您喜欢 GIF 图片最佳实践 - FLAnimatedImage @所有TGO鲲鹏会会员及粉丝,这里有一份七夕礼物等待您签收…… 比特 ▏Cookie与Session的区别-总结很好的文章 安兔物语 | 周一见的安兔物语 Block进阶篇