欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

安全编码规范-小羊的记录本

程序员文章站 2022-05-12 12:00:52
...

安全编码规范

安全目标

  • 机密性:数据不被非法访问和窃取
  • 完整性:数据完整,未被篡改
  • 可用性:保护资源在需要时可以访问

数据校验

信任域:一个域包含了一个或者多个组件,不同的域有不同的权限,不同域的组件互不信任
信任边界:域之间的边界
不可信数据:来自信任边界外的数据,比如外部输入数据,第三方输入数据。
信任域A和信任域B中间有个信任边界,互相不信任!于对方而言都是不可信的数据。

  • 不可信数据的列表:文件(包括程序的配置文件),注册表,网络,环境变量,命令行,用户输入,用户态数据,进程间通信(管道,消息,共享内存,socket,RPC),函数的参数,全局变量(其他线程可能会改变全局变量)

  • 校验的含义
    校验对象时来自边界之外的不可信数据,防止不可以或对系统造成伤害。比如SQL注入攻击,OS命令注入攻击,目录遍历攻击

  • 数据校验的步骤
    不可信输入->标准化和归一化(把路径的… 符号链接处理&&最简单格式)->输入清理(删除,替换不期望的字符,满足约束)->验证(确保输入格式是符合的)->输出清理(对敏感信息过滤,防止信息泄露)->命令解释器(下面注入攻击会用到)

  • 注入攻击
    SQL注入----命令解释器(RDBMS)
    OS命令注入----命令解释器(OS)
    XML注入----命令解释器(XML解析器)
    上面的----分别用于处理以上的注入攻击,防止注入

SQL注入

  • 定义:未经校验的外部输入要构造SQL语句,导致SQL注入漏洞。攻击者构造恶意输入来改变原本的SQL逻辑或者执行额外的SQL语句。
    比如 用户登录Ycx password123 ,输入Ycx’ or ‘1’='1 也是会登陆成功
  • 危害:绕开检查,业务异常;获取和修改数据库数据,敏感信息泄露或者篡改;修改服务器配置
  • 案例:SQL注入盗取网站的所有用户信息
  • 防御策略:
    1.参数化查询(预编译):使用PreparedStatement,sql语句中的参数使用占位符?来替换(不能解决所有问题)
    2.验证输入:白名单验证参数是否符合类型,大小,长度,格式。黑名单查找是否存在已知的不良字符。
    3.转码:特殊字符转码
    具体实施会将3个策略串联起来防御

案例分析:
MySQL
典型错误:直接在SQL中拼接外部输入
SELECT * FROM db_user WHERE username = ‘Ycx’ AND password= ‘password123’ or ‘1’='1’永远是真
安全编码规范-小羊的记录本
正确:使用预编译语句,参数化查询将查询逻辑和数据分离,互不干扰。
安全编码规范-小羊的记录本
Hibernate和上述类似

MyBatis
错误:
用$符号传参,类似拼接方式构造SQL语句
安全编码规范-小羊的记录本
正确:
用#符号,达到语义逻辑和数据分离的效果
安全编码规范-小羊的记录本
存储过程
错误:还是拼接
安全编码规范-小羊的记录本
正确:参数化查询
安全编码规范-小羊的记录本
正确:对外部输入做转码
安全编码规范-小羊的记录本

OS注入

主要是用shell方式(Windows的cmd.exe和Linux的/bin/sh)
Java中Runtime.exec()/ProcessBuilder类 OS命令注入原理:

dir="dummy & del c:\\dbms\\*.*"
cmd.exe /c dir dummy & del c:\dbms\*.*

主要就是Dos攻击:crash,exit,restart;读取或修改未授权数据;隐藏恶意活动
案例:Eclipse命令注入漏洞
解决方案:API代替执行命令,输入校验,转码

错误:直接使用外部输入拼接命令行
安全编码规范-小羊的记录本
正确:系统中应该尽量避免使用Runtime.exec(),若涉及到一些JDK未提供API的平台强相关的特性(如windows注册表访问),可优先考虑使用本地方法调用实现。
安全编码规范-小羊的记录本
输入校验:正则表达式对输入范围做限制,防止其中包含一些命令分隔符或者重定向符(“&&”、“&”、“||”、“|”)
安全编码规范-小羊的记录本
正确:转码后再拼接
安全编码规范-小羊的记录本

目录遍历攻击

通过…/来访问上一层目录,进行篡改和删除等恶意操作
策略:校验和过滤,对文件路径标准化处理,函数必须是getCanonicalPath(),禁止用getAbsolutePath()

  • getAbsolutePath()方法用来获取文件的绝对路径,但不会解析符号链接、别名、快捷方式(NO)
  • getCanonicalPath() 方法解析文件别名、快捷方式、符号链接、以及“…”符号,从而返回文件的标准全路径(YES)
    真实案例:绝对路径遍历漏洞

XML注入

Java开发中,XML使用很多:XML格式的配置文件,上传/下载XML格式的文件,web请求中body体中的数据为XML格式等。
XML数据的使用,存在的风险:使用不可信数据构造XML数据导致XML注入;解析不可信XML文件时,导致XXE漏洞内部实体扩展攻击;XML 文件格式转换时,使用不安全的XSLT导致任意代码执行;
防御策略:
1.白名单校验禁止输入特殊字符:
安全编码规范-小羊的记录本
2.dom4j构建XML字符串
安全编码规范-小羊的记录本
3.转码API调用开源API
安全编码规范-小羊的记录本

正则注入

Jdk中java.util.regex包中Pattern和Matcher类实现了正则匹配功能。另外,String类的多个接口函数默认使用正则,如replaceAll()、split()等。

修改政策逻辑,导致正则错误匹配或匹配过多信息导致敏感信息泄露,或导致ReDos攻击。

ReDos攻击(典型的不良正则表达式)
用白名单过滤解决

不安全的zip文件解压
安全风险注意2方面:1.文件解压到目标路径之外;2.解压消耗过多的系统资源
sanitizeFileName()检查文件名称是否存在路径遍历类的问题,然后边读边统计实际解压文件大小和数量,保证解压不消耗过多的系统资源。

日志注入

不可信数据没有校验或过滤,导致日志被伪造或者记录敏感数据
在登陆时候,恶意加入了敏感信息
防御措施:禁止\n\r等换行符;长度限制;确定不敏感才能写入日志

敏感数据保护

敏感数据:个人社保号,口令,**等
当跨信任边界传递数据时,必须确保不包含信任域以外无权访问的敏感信息,防止敏感数据泄露。
为了防止敏感信息泄露,对于包含敏感信息的数据可直接禁止跨信任域传递或者对敏感信息进行过滤,再传出去。

异常暴露敏感信息

抛出的异常被边界外的看到,可能会促进攻击者的进一步攻击。
Java中典型的敏感异常:

  • 错误:
    1.异常文本与类型泄露敏感信息:用户请求的目录不存在时,抛出FileNotFoundException异常,并且将目录结构信息包含在异常信息中。攻击者可以采用不断伪造文件路径的方法重现文件系统的结构。
    2.抛出封装后的敏感异常:IOException封装原始异常,攻击者还是可以获得文件系统的敏感信息的。
    3.替换原始敏感异常:替换包含敏感信息的异常不能防止敏感信息泄露,攻击者可以通过穷举发现有效的文件路径。
  • 正确:
    定义安全策略:只允许用户访问“c:\homepath”中的文件,当用户试图访问“c:\homepath”以外的文件或无效的文件时,程序只是给出一个简洁的提示信息。
    安全编码规范-小羊的记录本
  • 序列化泄露敏感信息
    序列化,反序列化重构原来的对象,比如在Server和client之间传输数据,黑客会窃取数据修改数据。
    错误:可序列化类中包含敏感信息字段
    包含敏感字段,可能在对象被序列化的时候被无意识的泄露出去。
    安全编码规范-小羊的记录本
    正确:将x,y属性声明为transient,则序列化的时候不会被写入字节流
    安全编码规范-小羊的记录本
    错误:序列化传递未签名与加密的敏感数据
    正确:序列化前对敏感数据先签名再加密
    安全编码规范-小羊的记录本
    当要传输敏感数据时,程序必须使用javax.net.ssl.SSLSocket类,而不能使用java.net.Socket。SSLSocket类提供了诸如SSL/TLS等安全协议保证信道不被监听或随意篡改,具体特性包括:完整性保护(SSL防止消息被主动窃取者篡改),认证(服务器客户端被认证),保密性(客户端和服务器之间数据加密)

线程同步、IO操作、反序列化、平台安全

线程同步

  • 防止将锁暴露给非信任代码
    对共享变量的访问:同步方法和同步块(this)。都是使用对象自身的锁(隐式锁)。
    攻击者可以获得一个可访问类对象的隐式锁并无期限持有来触发死锁,引起拒绝服务DoS。
    当一个类的对象会暴露出去与非信任代码进行交互时,对这个类的代码做同步应该使用私有不变对象锁(private final Object lock),使得攻击者无法获得锁对象。
    错误:
    安全编码规范-小羊的记录本
    此时,如果SomeObject类的对象被非信任代码获取,就会阻塞changeValue()的调用。
SomeObject ob = getTheObject();
synchronized (ob) {
	while (true) {
		Thread.sleep(Integer.MAX_VALUE);
	}
}

正确:使用私有不变锁,让攻击者拿不到锁对象。
安全编码规范-小羊的记录本
使用不安全的锁对象导致不正确的线程同步
1.基于可被重用的对象进行同步:死锁或不正确
2.基于getClass()返回的类对象进行同步:类继承时,基类和子类getClass()返回的类对象不同,错误使用导致不同的锁进行线程同步
3.基于高层的并发对象内置锁进行同步:实现java.util.concurrent.locks包中的Lock或Condition接口的类,被认为是高层并发对象。同步时应该调用lock()/unlock()方法,而不是使用内置锁。
4.实例锁同步静态数据:使用多个锁来对数据进行同步

死锁:
异常时未释放锁
请求锁和释放锁顺序不当
在阻塞操作中持有锁

IO操作

  • 临时文件使用完毕应及时删除,否则攻击者可以利用共享目录中的文件操作。
    正确:
try (BufferedWriter writer = Files.newBufferedWriter(tempFile, StandardOpenOption.DELETE_ON_CLOSE))

还有一种显式删除

try {
	fop.close();
} catch (IOException x) {
}
if (!f.delete()) {
	//delete
}
  • 不要将Buffer对象封装的数据暴露给不可信代码
    比如:IntBuffer,CharBuffer,ByteBuffer,以只读视图或者拷贝的方式返回。
return cb.asReadOnlyBuffer();
  • 防止让外部进程阻塞在输入输出流上
    exe()用来调用外部程序进程。
    错误:waitFor()会一直阻塞调用线程
    正确:正确处理进程的输出流、错误流

反序列化

SecurityManager

平台安全

  • Java组件:字节码验证器,类加载器,安全管理器(用于指明代码是否可以访问指定资源),访问控制器,策略文件。
  • 安全管理器:对不同来源代码的权限配置,对代码能够访问敏感资源和操作进行控制。
  • 典型错误:
    1.安全检查方法被恶意子类覆写,导致安全检查被忽略
    正确:声明方法是private或final
    2.编写自定义类加载器时必须调用超类的getPermission()函数
PermissionCollection pc = super.getPermissions(cs);
相关标签: 信息安全 安全