7.5代码攻击和安全防护
目录介绍
- 01.代码安全的指标
- 1.1 软件安全是代码缺陷
- 1.2 安全风险有哪些
- 1.3 软件安全问题思考
- 1.4 安全指标建设
- 02.Java应用注入攻击
- 2.2 SQL注入攻击
- 2.3 操作系统命令注入
- 2.4 XML注入攻击
- 2.5 移动端注入攻击
- 03.安全领域的重点
- 3.1 应用安全机制
- 3.2 安全漏洞定义
- 3.3 常见的安全漏洞
- 04.代码安全如何保障
- 4.1 安全基础构成
- 4.2 运行时安全机制
- 4.3 安全框架API
- 4.4 JDK集成安全工具
- 4.5 安全漏洞表现形式
- 05.如何写出安全代码
- 5.1 数值类型防范溢出安全
- 5.2 异常减少暴露敏感信息
- 5.3 序列化安全问题
- 5.4 哈希碰撞安全防范
- 5.6 final修饰类安全
- 06.功能安全的设计
- 6.1 提高网络请求安全
- 6.2 输入验证和过滤
- 6.3 安全的会话管理
- 6.4 so库安全加固设计
- 07.开发阶段安全策略
- 7.1 设计安全策略
- 7.2 本地安全策略
- 7.4 测试安全策略
- 7.5 部署安全策略
- 07.保障安全的案例
- 7.1 OpenJDK如何保障安全
01.代码安全的指标
1.1 软件安全是代码缺陷
- 安全问题实际就是软件的缺陷
- 软件安全并不存在一劳永逸的秘籍,既离不开设计、架构中的风险分析,也离不开编码、测试等阶段的安全实践手段。
- 对于面试官来说,考察安全问题,除了对特定安全领域知识的考察,更多是要看面试者的 Java 编程基本功和知识的积累。
1.2 安全风险有哪些
- 软件安全主要包括下面几种:
- 密钥破解,导致本地加密数据被盗取
- 通信密钥破解,导致接口数据被盗取
- 伪造接口数据上报
- 接口签名被破解,导致接口可以被重放攻击
- 那么归结起来,实际上就是这样几种模式:
- 代码反编译
- so破解,这个是终端
- 中间人攻击,这个是攻击网络请求
1.3 软件安全问题思考
- 有哪些不安全案例
- 代码线程安全,尤其多线程,如果不加锁,容易造成数据错误,引发其他问题
- 业务安全,比如短信登录增加滑块校验,避免网络攻击
- 代码不规范安全,比如异常日志不要打印重要信息
- 传输数据安全,尤其是数据在客户端和服务端传输时,有的不序列化,还要保证有的数据不能被捕获。
- 网络安全问题
- 密钥破解,导致本地加密数据被盗取;通信密钥破解,导致接口数据被盗取
- 伪造接口数据上报;接口签名被破解,导致接口可以被重放攻击
02.Java应用注入攻击
2.2 SQL注入攻击
- 最常见的 SQL 注入攻击。
- 一个典型的场景就是 Web 系统的用户登录功能,根据用户输入的用户名和密码,我们需要去后端数据库核实信息。
- 假设应用逻辑是,后端程序利用界面输入动态生成类似下面的 SQL,然后让 JDBC 执行。
Select * from use_info where username = “input_usr_name” and password = “input_pwd”
- 但是,如果我输入的 input_pwd 是类似下面的文本,那么,拼接出的 SQL 字符串就变成了下面的条件,OR 的存在导致输入什么名字都是复合条件的。
“ or “”=” Select * from use_info where username = “input_usr_name” and password = “” or “” = “”
- 只是举个简单的例子,它是利用了期望输入和可能输入之间的偏差。
- 上面例子中,期望用户输入一个数值,但实际输入的则是 SQL 语句片段。类似场景可以利用注入的不同 SQL 语句,进行各种不同目的的攻击,甚至还可以加上“;delete xxx”之类语句,如果数据库权限控制不合理,攻击效果就可能是灾难性的。
- 提到的 SQL 注入等典型攻击,我们在开发中怎么避免?
- 待完善
2.3 操作系统命令注入
- 操作系统命令注入。
- Java 语言提供了类似 Runtime.exec(…) 的 API,可以用来执行特定命令,假设我们构建了一个应用,以输入文本作为参数,执行下面的命令:
ls –la input_file_name
- 但是如果用户输入是 “input_file_name;rm –rf /*”,这就有可能出现问题了。
- 当然,这只是个举例,Java 标准类库本身进行了非常多的改进,所以类似这种编程错误,未必可以真的完成攻击,但其反映的一类场景是真实存在的。
2.4 XML注入攻击
- XML 注入攻击。
- Java 核心类库提供了全面的 XML 处理、转换等各种 API,而 XML 自身是可以包含动态内容的,例如 XPATH,如果使用不当,可能导致访问恶意内容。
2.5 移动端注入攻击
- 中间人攻击,在客户端主要是防抓包
- 通过一些黑科技手段,抓包app的数据。哪怕是https这种安全协议!
- 客户端抓包的突破口集中以下几点
- 第一点:必须链接代理,且跟Charles要具有相同ip。思路:客户端是否可以判断网络是否被代理了。
- 第二点:CA证书,这一块避免使用黑科技hook证书校验代码,或者拥有修改CA证书权限。思路:集中在可以判断是否挂载。
- 第三点:冒充中间人CA证书,在客户端client和服务端server之间篡改拦截数据。思路:可以做CA证书校验。
- 第四点:为了可以在7.0上抓包,App往往配置清单文件networkSecurityConfig。思路:线上环境去掉该配置。
03.安全领域的重点
3.1 应用安全机制
3.2 安全漏洞定义
- 在Java中,安全漏洞(Security Vulnerability)定义是什么
- 是指存在于Java应用程序或Java开发框架中的潜在弱点或缺陷,可能被攻击者利用来破坏系统的机密性、完整性或可用性。
- 安全漏洞可能导致恶意用户执行未经授权的操作、获取敏感信息、篡改数据或拒绝服务等。
3.3 常见的安全漏洞
- 跨站脚本攻击(Cross-Site Scripting,XSS):
- XSS攻击是指攻击者通过注入恶意脚本代码到Web应用程序中,使其在用户浏览器中执行。
- SQL注入攻击(SQL Injection):
- SQL注入攻击是指攻击者通过在用户输入中注入恶意SQL代码,从而绕过应用程序的输入验证,执行未经授权的数据库操作。
- 这可能导致数据库信息泄露、数据篡改或拒绝服务。
- 不安全的序列化(Insecure Deserialization):
- 不安全的序列化是指应用程序在反序列化过程中未正确验证和过滤输入数据,导致攻击者可以注入恶意对象或执行任意代码。
04.代码安全如何保障
4.1 安全基础构成
4.2 运行时安全机制
- 运行时安全机制。可以简单认为,就是限制 Java 运行时的行为,不要做越权或者不靠谱的事情,具体来看:
- 在类加载过程中,进行字节码验证,以防止不合规的代码影响 JVM 运行或者载入其他恶意代码。
- 类加载器本身也可以对代码之间进行隔离,例如,应用无法获取启动类加载器(Bootstrap Class-Loader)对象实例,不同的类加载器也可以起到容器的作用,隔离模块之间不必要的可见性等。
- 利用 SecurityManger 机制和相关的组件,限制代码的运行时行为能力,其中,你可以定制 policy 文件和各种粒度的权限定义,限制代码的作用域和权限,例如对文件系统的操作权限,或者监听某个网络端口的权限等。
- 我画了一个简单的示意图,对运行时安全的不同层次进行了整理。
image - Java 的安全模型是以代码为中心的,贯穿了从类加载,如 URLClassLoader 加载网络上的 Java 类等,到应用程序运行时权限检查等全过程。
4.3 安全框架API
- Java 提供的安全框架 API,这是构建安全通信等应用的基础。例如:
- 加密、解密 API。
- 授权、鉴权 API。
- 安全通信相关的类库,比如基本 HTTPS 通信协议相关标准实现,如TLS 1.3;
- 注意,这一部分 API 内部实现是和厂商相关的,不同 JDK 厂商往往会定制自己的加密算法实现。
4.4 JDK集成安全工具
- JDK 集成的各种安全工具,例如:
- keytool,这是个强大的工具,可以管理安全场景中不可或缺的秘钥、证书等,并且可以管理 Java 程序使用的 keystore 文件。
- jarsigner,用于对 jar 文件进行签名或者验证。
4.5 安全漏洞表现形式
- 按照传统的定义,任何可以用来绕过系统安全策略限制的程序瑕疵,都可以算作安全漏洞。
- 具体原因可能非常多,设计或实现中的疏漏、配置错误等,任何不慎都有可能导致安全漏洞出现,例如恶意代码绕过了 Java 沙箱的限制,获取了特权等
05.如何写出安全代码
5.1 数值类型防范溢出安全
- 首先,我们一起来看一段不起眼的条件判断代码,这里可能有什么问题吗?你可能会纳闷,这是再常见不过的一个条件判断了,能有什么安全隐患?
// a, b, c 都是 int 类型的数值 if (a + b < c) { // … }
- 这里的隐患是数值类型需要防范溢出,否则这不仅仅可能会带来逻辑错误,在特定情况下可能导致严重的安全漏洞。
- 从语言特性来说,Java 和 JVM 提供了很多基础性的改进,相比于传统的 C、C++等语言,对于数组越界等处理要完善的多,原生的避免了缓冲区溢出等攻击方式,提高了软件的安全性。
- 但这并不代表完全杜绝了问题,Java 程序可能调用本地代码,也就是JNI 技术,错误的数值可能导致 C/C++ 层面的数据越界等问题,这是很危险的。
- 所以,上面的条件判断,需要判断其数值范围,例如,写成类似下面结构。
if (a < c – b)
5.2 异常减少暴露敏感信息
- 再来看一个例子,请看下面的一段异常处理代码:
try { // 业务代码 } catch (Exception e) { throw new RuntimeException(hostname + port + “ doesn’t response”); }
- 这段代码将敏感信息包含在异常消息中,试想,如果是一个应用,异常也没有良好的包装起来,很有可能就把内部信息暴露给终端客户。
- 对于安全标准特别高的系统,甚至可能要求敏感信息被使用后,要立即明确在内存中销毁,以免被探测;或者避免在发生 core dump 时,意外暴露。
5.3 序列化安全问题
- Java 提供了序列化等创新的特性,广泛使用在远程调用等方面,但也带来了复杂的安全问题。直到今天,序列化仍然是个安全问题频发的场景,通常建议:
- 敏感信息不要被序列化!在编码中,建议使用 transient 关键字将其保护起来。
- 反序列化中,建议在 readObject 中实现与对象构件过程相同的安全检查和数据检查。
- 另外,在 JDK 9 中,Java引入了过滤器机制,以保证反序列化过程中数据都要经过基本验证才可以使用。
- 其原理是通过黑名单和白名单,限定安全或者不安全的类型,并且你可以进行定制,然后通过环境变量灵活进行配置,会更加具体的使用你可以参考 ObjectInputFilter。
5.4 哈希碰撞安全防范
5.6 final修饰类安全
- 看到很多第三方库,比如gson,rxJava等库,几乎大多数类都是用finial修饰的。这个是为什么呢?
- 当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。
- final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。
- 很多类,不想被开发者继承,为了内部安全。
- 同时也避免反射攻击,所以就用finial修饰。这也是一种保证类安全的有效做法。
06.功能安全的设计
6.1 提高网络请求安全
- 客户端抓包的突破口集中以下几点
- 第一点:必须链接代理,且跟Charles要具有相同ip。思路:客户端是否可以判断网络是否被代理了。
- 第二点:CA证书,这一块避免使用黑科技hook证书校验代码,或者拥有修改CA证书权限。思路:集中在可以判断是否挂载。
- 第三点:冒充中间人CA证书,在客户端client和服务端server之间篡改拦截数据。思路:可以做CA证书校验。
- 第四点:为了可以在7.0上抓包,App往往配置清单文件networkSecurityConfig。思路:线上环境去掉该配置。
- 刷掌SDK网络安全实践操作
- 第一种,sdk网络请求默认设置关闭代理【厂商这边也可以通过配置打开支持代理】,避免代理抓包
- 第二种,设置配置文件,配置networkSecurityConfig清单文件的system和user权限去掉,只有这样才不会信任用户证书。不信任证书,自然增加抓包难度!
- 第三种,数据加解密,可以保证接口请求安全性!
- 第四种,sign签名,即使别人拿到token,通过对请求参数md5 + 时间戳,即可最大程度保证安全!该方案,需要跟服务端一起做,待定!
- 第五种,CA证书校验,可以有效防止抓包,保证数据安全
- 接口上的安全,最基本的保证就是Https,同时对SSL协议的域名进行校验(关键词:X509TrustManager、hostnameVerifier)
- 在此之上,请求的接口上,我们一般会带上一个签名,或者叫token,这个加密的密钥串,就是我们身份的象征。
- 一般来说,这个签名也就是通过前面我们千辛万苦要藏好的本地密钥来进行生成的,通常也就是那几个参数,例如时间戳、UserID、IMEI、Mac地址等等进行拼装,然后通过DES、3DES、AES、hmacSHA1等方式进行加密后,再经过Base64进行编码生成的,这些加密过程就不赘述了。
6.2 输入验证和过滤
6.3 安全的会话管理
6.4 so库安全加固设计
- 先说一下so库的风险点
- So文件为APK中包含的动态链接库文件,Android利用NDK技术将C/C++语言实现的核心代码编译为So库文件供Java层调用。
- So文件被破解可能导致应用的核心功能代码和算法泄露。攻击者利用核心功能与算法可轻易抓取到客户端的敏感数据,并对其解密,导致用户的隐私泄露或直接财产损失。
- 解决方案
- 由于release签名的唯一性,可以考虑在native层进行签名的校验。如果签名不正确,直接让App crash。https://juejin.cn/post/6844903609197395976?searchId=20231030192753CFCCAB7732D5AC0A58BB#heading-3
- 对so文件进行安全加固、可以使用第三方的安全平台so加固方案。
07.开发阶段安全策略
7.1 设计安全策略
- 针对功能设计安全要求
- 数据加密:开发者应该采取适当的加密措施,确保敏感数据在储存过程中得到保护。
- 安全传输:开发者需要规划安全的数据传输方式,如使用 HTTPS 协议来传输敏感数据,以及做好防止抓包或者攻击保护。
- 数据处理:开发者应该严格控制数据的访问权限,只允许有必要权限的组件对数据进行处理。
7.2 本地安全策略
- 密钥的安全问题
- 第一种:最低级的,密钥被直接放在Java代码中。
- 第二种:放在Java代码中,会把密钥拆成几个部分,然后通过一定算法计算合成完整的密钥。
- 第三种:把密钥和加解密放so中
- 第四种:so再做下签名校验
- 秘钥及敏感信息
- 此类配置应当妥善存放,不要在类中硬编码敏感信息,可以使用JNI将敏感信息写到Native层。
08.保障安全的案例
8.1 OpenJDK如何保障安全
落实到实际开发流程中,以 OpenJDK 团队为例,我们应用了几个不同角度的实践:
- 在早期设计阶段,就由安全专家组对新特性进行风险评估。
- 开发过程中,尤其是 code review 阶段,应用 OpenJDK 自身定制的代码规范。
- 利用多种静态分析工具如FindBugs、Parfait等,帮助早期发现潜在安全风险,并对相应问题采取零容忍态度,强制要求解决。
- 甚至 OpenJDK 会默认将任何(编译等)警告,都当作错误对待,并体现在 CI 流程中。
- 在代码 check-in 等关键环节,利用 hook 机制去调用规则检查工具,以保证不合规代码不能进入 OpenJDK 代码库。
你了解Java应用开发中的注入攻击吗?
- https://time.geekbang.org/column/article/11211
如何写出安全的Java代码?
- https://time.geekbang.org/column/article/11355