Java作为企业主流开发语言已流行多年,各种java安全编码规范也层出不穷,本文将从实践角度出发,整合工作中遇到过的多种常见安全漏洞,给出不同场景下的安全编码方式。
本文漏洞复现的基础环境信息:jdk版本:1.8 ,框架:springboot1.5,数据库:mysql5.6和mongodb3.6,个别漏洞使用到不同的开发框架会特别标注。
安全编码实践
Sql注入防范
常见安全编码方法:预编译+输入验证
预编译适用于大多数对数据库进行操作的场景,但预编译并不是万能的,涉及到查询参数里需要使用表名、字段名的场景时(如order by、limit、group by等),不能使用预编译,因为会产生语法错误。常见的预编译写法如下
jdbc:
![e3bb345aab3a150962959baa20f0e0db.png](https://i-blog.csdnimg.cn/blog_migrate/c2b646154b542b353abbf3ff6a176a5d.png)
Hibernate
![9ab7df89a1e119bc912202a3b45d78b6.png](https://i-blog.csdnimg.cn/blog_migrate/7712b57bb3846c18f21bd24749eafe7f.jpeg)
Ibatis
![811bd067a539980e9abef1163cf479ba.png](https://i-blog.csdnimg.cn/blog_migrate/174d78a708abd4f04881f355b44adc90.png)
Mybatis
![134d7dbe6bc67b673523bb7448e6df8d.png](https://i-blog.csdnimg.cn/blog_migrate/f7aba322eff8de3563bf5a904d71eff7.png)
在无法使用预编译的场景,可以使用数据校验的方式来拦截非法参数,数据校验推荐使用白名单方式。
错误写法:不能使用预编译的场景(直接拼接用户的查询条件)
![677522bdb581107aafe8bef6d7676bd7.png](https://i-blog.csdnimg.cn/blog_migrate/c1a20ad64c24ca02e0573f00d5c1e0a9.png)
漏洞利用验证:
![567eac44cab1ee3393e036fa8af88a0b.png](https://i-blog.csdnimg.cn/blog_migrate/9e3541adbeca592932233c29b7740e75.jpeg)
不能使用预编译的正确写法(通过白名单验证用户输入):
![2288fc3380290751fc3c15f7025f04a3.png](https://i-blog.csdnimg.cn/blog_migrate/4a05480254823dd28ccf4d75bb7bc628.jpeg)
漏洞修复验证:
![9404e5949e8331c1d00319076be4b1f1.png](https://i-blog.csdnimg.cn/blog_migrate/9c4d9f9f9ba85eebe2fad586de24a17b.jpeg)
Nosql注入防范
涉及到非关系型数据库mongdb在查询时不能使用拼接sql的方式,需要绑定参数进行查询,跟关系型数据库的预编译类似
错误写法(拼接用户的查询条件):
![48f8009bbed959415e9de7ca862092ee.png](https://i-blog.csdnimg.cn/blog_migrate/78a57f858d91befe7897aa902beec516.jpeg)
漏洞利用验证:
![24b1e87af81ad475446b51b7c2520bae.png](https://i-blog.csdnimg.cn/blog_migrate/017d4898be235fdad311ad42a724d883.jpeg)
正确写法(参数绑定):
![d4ee1518384b08efcf6f9ff037da4223.png](https://i-blog.csdnimg.cn/blog_migrate/10f2ac5d64f22bc6121f8b0d63903b4b.jpeg)
漏洞修复验证:
![4c38ae8912a10fe6597cf9d55e5cec3e.png](https://i-blog.csdnimg.cn/blog_migrate/22e726da766b3ef90648d4cde17790c2.jpeg)
Xss防范
白名单校验
适用于纯数字、纯文本等地方,如用户名
Esapi
适用于常规的输入输出,如用户评论
![64a3519e666db6185261738a7d948a26.png](https://i-blog.csdnimg.cn/blog_migrate/57dea8f9fca96c9f0def0efeedb6460a.jpeg)
错误写法(对用户输入内容不做处理):
![e1dda6de9e26ef6a547f7243366639d1.png](https://i-blog.csdnimg.cn/blog_migrate/26b551a1098cf4358f8e9bb39fc7c445.png)
正确写法(通过esapi的黑白名单配置来实现富文本xss的过滤):
![a65277ca9777b363af38f09a167e8044.png](https://i-blog.csdnimg.cn/blog_migrate/bf1fb33e30fd2accb7e069672a470b2f.png)
漏洞利用及修复验证:
![107b241deb89bc033fc29735a993aa6c.png](https://i-blog.csdnimg.cn/blog_migrate/f603c95504597948fedc21fa5a0e92dc.png)
XXE注入防范
为了避免 XXE injections,应为 XML 代理、解析器或读取器设置下面的属性:
factory.setFeature(“ http:// xml.org/sax/features/ex ternal-general-entities “,false);
factory.setFeature(“ http:// xml.org/sax/features/ex ternal-parameter-entities “,false);
如果不需要 inline DOCTYPE 声明,可使用以下属性将其完全禁用:
factory.setFeature(“ http:// apache.org/xml/features /disallow-doctype-decl “,true);
错误写法(以xmlReader为例,允许解析外部实体):
XMLReaderxmlReader = XMLReaderFactory.createXMLReader();
xmlReader.parse(newInputSource(newStringReader(body)));
漏洞利用验证:
![99c096528e5bd96a5de2f77e54048f88.png](https://i-blog.csdnimg.cn/blog_migrate/af013005814ce25f062dbe97be36371c.jpeg)
![20d00c10793d073a5f2e2064993af857.png](https://i-blog.csdnimg.cn/blog_migrate/b8cc6cf3c2b1883f5a0d16700835b077.jpeg)
正确写法(禁止解析部实体):
XMLReaderxmlReader
文件上传漏洞
文件名随机,防止被猜解上传路径
限制上传文件大小,防止磁盘空间被恶意占用
限制上传文件类型,防止上传可执行文件
正确写法(限制文件类型大小,通过uuid生成随机文件名保存):
![93aea9272f5a6b1d2640e14804da420f.png](https://i-blog.csdnimg.cn/blog_migrate/b6296b0e5dad730fd165289cd70029e2.jpeg)
漏洞利用验证
![879a5a5841318a7a595804c17103e1ff.png](https://i-blog.csdnimg.cn/blog_migrate/934d13fd1a283609fd59438fb16e7277.png)
漏洞修复验证:
![e3d88c6103c87f0ea847fc8578dc175a.png](https://i-blog.csdnimg.cn/blog_migrate/7e0fd8afbdf6ccddc018e0691fe8e5d1.png)
文件包含
限制文件在指定目录,逻辑名称绑定文件路径,跟文件上传的处理类似,通过文件id读取对应资源文件
错误写法(直接请求用户设置的资源):
String
漏洞利用验证:
![776c94f5159e8373e0c2d4b5490fc903.png](https://i-blog.csdnimg.cn/blog_migrate/dd1f053c0f17e7bd135c2afa6c2c537f.jpeg)
正确写法(通过文件id和真实路径的映射设置白名单):
if
文件上传后对应的路径会存储在数据库里,表结构如下:
![7dfb155fc5bf25cdb523ff237cbac3ef.png](https://i-blog.csdnimg.cn/blog_migrate/af936b34a160f09efed174dda1a60cbc.jpeg)
漏洞修复验证
![854e6913d520ca2f25df618f6b2f3af6.png](https://i-blog.csdnimg.cn/blog_migrate/e4e9bb2b4dc62e5ee4db714fa1f86784.jpeg)
Csrf
常见的框架已经自带了防范csrf的功能,只需要正确的配置启用即可
struts2
JSP使用标签,在struts配置文件中增加token拦截器
页面代码:
![b5c1cf7b130aed08eec5323628a5e344.png](https://i-blog.csdnimg.cn/blog_migrate/153de984d2658bbf81af942643cc4e36.jpeg)
漏洞修复验证:
![43d2f258893d220c5178e262cc773a47.png](https://i-blog.csdnimg.cn/blog_migrate/80a16f1c44419927ae2184d13f3a1be7.jpeg)
Spring
正确写法:使用spring-security
![d6689f9b6a7a2f72541faadd0e9de8c1.png](https://i-blog.csdnimg.cn/blog_migrate/01f29f858d01e57a8745dacac2c83e02.jpeg)
漏洞修复验证
![05c09a6f340825f8f6eab984b5c08a7f.png](https://i-blog.csdnimg.cn/blog_migrate/9498799bc7fc82d0def9e33f431c81dc.jpeg)
越权
Java通用权限框架(shiro)
进行增删改查操作时采用无法遍历的序号
对于敏感信息,应该进行掩码设置屏蔽关键信息。
垂直越权
角色权限矩阵
![8401647297d2de50a128ce28e5c78e3a.png](https://i-blog.csdnimg.cn/blog_migrate/07b6a30d61eba5fb612e5743d52f22f1.png)
限制匿名用户和低权限用户,执行操作前检查用户登录状态和权限清单
正确写法(判断用户权限清单是否包含请求的权限):
![1ac5a674de516aa0d7d2fd120a1991a1.png](https://i-blog.csdnimg.cn/blog_migrate/d48d898fd30eb212255a5149000f6a17.jpeg)
漏洞修复验证
![075543719b8df17c774a0a3dce9b5a43.png](https://i-blog.csdnimg.cn/blog_migrate/a052dfcda073cd6a42df23207f7f1c83.jpeg)
水平越权:
操作前判断下当前用户是否有对应数据权限,修复后修复前两次验证,通过返回长度不同可看到水平越权问题已解决。
![898b325380fe92d3e3f04c8daadba18a.png](https://i-blog.csdnimg.cn/blog_migrate/e37b076337337509b146d350ca6c9fe3.jpeg)
url重定向&ssrf
url重定向
对于白名单内的地址,用户可无感知跳转,不在白名单内的地址给用户风险提示,用户选择是否跳转
正确写法:
![cc9900674768715b208223db91d3a178.png](https://i-blog.csdnimg.cn/blog_migrate/3ee17adba9a1e336319bbceaccd39a88.png)
漏洞修复验证
![fc419f4cb1c1255c9a34fdf203eddb3f.png](https://i-blog.csdnimg.cn/blog_migrate/40c782468e1a04c220f472336476831b.png)
Ssrf
漏洞利用验证:
![3dd4de8abeb61b1f001e4cf386900fe4.png](https://i-blog.csdnimg.cn/blog_migrate/b376d237baf3cce97d73f7935e9d8e4c.jpeg)
正确写法(限制请求协议,设置白名单域名,避免内网地址探测):
![74326c5c0df8ac7a41460cbd08bc7a74.png](https://i-blog.csdnimg.cn/blog_migrate/2b2b47be0b7dfe7896c840355864904c.jpeg)
漏洞修复验证
![13be43986348c54ad8cd35d7d49e25e6.png](https://i-blog.csdnimg.cn/blog_migrate/3cff2085c427b0bf6406720095cb8b7d.png)
拒绝服务
正则表达式拒绝服务,这种漏洞需要通过白盒审计发现,黑盒测试比较难发现。
错误写法(正则匹配时未考虑极端情况的资源消耗)
![189e389d124f41843f2f154713246413.png](https://i-blog.csdnimg.cn/blog_migrate/18b83fc123802deb3ca2bd26795b0816.png)
漏洞利用验证,随着字符长度增加,响应时间会越来越长,cpu满负荷运转
![f3ba9d31340b9da10f3f8cb4239e711b.png](https://i-blog.csdnimg.cn/blog_migrate/aa484b53cd24f5a46a9f023a4bc1b348.png)
正确写法(运行超过2秒就中止匹配):
![ce84299b20326fa54f7c6eaa6af807f2.png](https://i-blog.csdnimg.cn/blog_migrate/58facccfe53cf89546cc7ed8cc0544d4.jpeg)
漏洞修复验证:
![bfb8ddd06f06ad7cda1b3ceb30706564.png](https://i-blog.csdnimg.cn/blog_migrate/5f388056041dcd81e9fc4556724f6d7d.png)
不安全的加密模式
需要通过白盒审计发现漏洞,直接黑盒测试比较难。
错误写法:使用ECB模式,相同明文生成相同密文
![db7588b59368e8ec9d2fff7708cd4fb3.png](https://i-blog.csdnimg.cn/blog_migrate/4cc689af3cb0a154037a55348a261013.jpeg)
漏洞利用验证(使用选定明文攻击从后向前按位猜解):
![a6ee2aa53dec24d8c1615adf44d5137c.png](https://i-blog.csdnimg.cn/blog_migrate/1a0ed7f1d391fc26af74435f314aff73.jpeg)
正确写法:使用CBC模式,相同明文生成不同密文
Cipher
需要通过白盒审计发现漏洞,直接黑盒测试比较难。
错误写法:使用伪随机,相同种子生成相同随机数序列
漏洞利用验证:
需要通过java生成前后2000毫秒内的随机数,然后使用python调用这些随机数尝试暴破
![84f66e34c5215b85cd07ae76d2ec9c7d.png](https://i-blog.csdnimg.cn/blog_migrate/be35489b94f028e08507ad5364cf92f2.jpeg)
正确写法:使用Securerandom
漏洞修复验证(Securerandom不能指定seed,避免伪随机):
![cfc241f7600fd68aff203f2f1955aae1.png](https://i-blog.csdnimg.cn/blog_migrate/6f9d48c61b75069b61af1691de180deb.jpeg)
条件竞争
Servlet的单例模式容易导致条件竞争,也是推荐白盒方式审计漏洞。
错误写法:初始积分100个,每天限制签到1次领取1积分
![739e03646a354fedb6f841f18e07db08.png](https://i-blog.csdnimg.cn/blog_migrate/4f2a1b8d8ad85cca19f908eb6538285f.png)
漏洞利用验证(10个并发可实现多次签到,这里多并发跟业务功能复杂度和服务器性能有关,如果想必现漏洞,可以在读取签到次数和增加签到次数之间增加2秒延时,可以保证漏洞复现。)
![fe919e5027f85627147f8ff6eb90d59e.png](https://i-blog.csdnimg.cn/blog_migrate/ed075463a09ea3d408a4257a74512cf8.jpeg)
![d3414a35f12df0a1302287f7ea87677c.png](https://i-blog.csdnimg.cn/blog_migrate/20cdf497b4cdbc23e5c1f0142ef73090.png)
正确写法:使用线程同步
![042f78f22c69250fa74f380eddad996b.png](https://i-blog.csdnimg.cn/blog_migrate/60dce67bea8386e793d14d7a14da04bd.png)
漏洞修复验证:
![3317ef95686b6835ed03ae846923dde8.png](https://i-blog.csdnimg.cn/blog_migrate/131ca9f992963b62b5266c1d1550d4fb.jpeg)
修复后返回数据包速度明显变慢,不能再重复签到领积分
![46ec5d20f1bfdf173388708eccf23011.png](https://i-blog.csdnimg.cn/blog_migrate/6056b869a0350130395ffc1ac81e81d9.png)
日志伪造防范/http响应拆分防范
日志伪造黑盒测试无法发现,需要通过白盒审计发现漏洞。
错误写法(直接将用户的输入打印到日志文件):
public
漏洞利用验证(通过%0d%0a插入换行控制符,伪造日志记录)
![863823dfde1ea7d59999933711b39019.png](https://i-blog.csdnimg.cn/blog_migrate/1496aa8424f743c642b13e15c2e161a2.png)
正确写法(过滤换行空格):
public
漏洞修复验证
![499eb3858c4e1e9838f5d5268b5831c4.png](https://i-blog.csdnimg.cn/blog_migrate/2e5ec61fcba716f1d3209eb21c111c13.png)
http响应拆分,只在低版本web服务器上出现,使用tomcat9未复现这个问题
错误写法
@RequestMapping
漏洞修复验证(新版本的web服务器可以自动处理http响应拆分):
![dcd9fb975c066d1e0f03d721c454195d.png](https://i-blog.csdnimg.cn/blog_migrate/525ffde44d254f06e828edf8edb8de09.jpeg)
动态代码执行
Runtime.exec
错误写法(直接执行用户输入的命令):
Process p = run.exec(cmd);
![8a56ded2b8d2cf6dc07088f463b9472b.png](https://i-blog.csdnimg.cn/blog_migrate/3ea2481b4277fe199f03fbadd972def2.jpeg)
正确写法:
1.输入净化
2.Switch-case命令映射
if
3.使用语言标准api获取系统信息
“当前用户:”+System.getProperty(“user.name”)
漏洞修复验证
![48ac3407aa16f799ff227f832d6e35ed.png](https://i-blog.csdnimg.cn/blog_migrate/8297af572c8b102cc685c6b5b9936bb9.png)
反序列化
错误写法(对序列化的类未做限制):
Stringdata=request.getParameter(“data”);
byte[] decoded = java.util.Base64.getDecoder().decode(data);
ByteArrayInputStream bytes =newByteArrayInputStream(decoded);
ObjectInputStream in =newObjectInputStream(bytes);
in.readObject();
in.close();
漏洞利用验证
![c0bb89bc22d86269ed6573e2374fe2cf.png](https://i-blog.csdnimg.cn/blog_migrate/0867124f50257802eb5dcc6c39690db3.png)
正确写法(使用serialkiller,主要也是通过黑名单去过滤,可以防御大部分的攻击)
String data =request.getParameter(“data”);
byte[] decoded = java.util.Base64.getDecoder().decode(data);
ByteArrayInputStream bytes =newByteArrayInputStream(decoded);
try{
ObjectInputStream in =newSerialKiller(bytes,”d:serialkiller.conf”);
in.readObject();
in.close();
}catch(InvalidClassException e){
response.getWriter().write(“class not allowed!”);
}
漏洞修复验证
![587e59414aba644ea82da021be1df023.png](https://i-blog.csdnimg.cn/blog_migrate/92f3b9407a6dae14dfff97711e827057.png)
表达式注入
Spel表达式注入
错误写法(直接解析表达式):
public voidspel_injection(HttpServletRequest request,HttpServletResponse response)
throwsException {
A a=newA(“test”);
ExpressionParser parser =newSpelExpressionParser();
StandardEvaluationContext context =newStandardEvaluationContext();
parser.parseExpression(“Name = “+request.getParameter(“name”)).getValue(context, a);
response.getWriter().write(“usrname:”+a.getName());
}
漏洞利用验证:
![c59c604860fc3a31798b6f12a0ae2b80.png](https://i-blog.csdnimg.cn/blog_migrate/20d7ecbda992fff44748316f8da3c01b.jpeg)
正确写法(使用SimpleEvluationContext,可解析白名单内的表达式):
public voidspel_injection_sec(HttpServletRequestrequest, HttpServletResponse response)
throwsException {
A a=newA(“test”);
ExpressionParser parser =newSpelExpressionParser();
EvaluationContext context =SimpleEvaluationContext.forReadWriteDataBinding().build();
parser.parseExpression(“Name = “+request.getParameter(“name”)).getValue(context, a);
response.getWriter().write(“usrname:”+a.getName());
}
漏洞修复验证
![324e08e7ceffe5423617b3b3a9284740.png](https://i-blog.csdnimg.cn/blog_migrate/f4f87d9c7d0762ebd13ca78da0453aad.png)
OGNL表达式注入涉及到struts框架的安全配置,主要是禁用动态方法调用,不再继续展开验证。
Spring-boot安全配置
1.Spring Boot 应用程序配置为显式禁用Shutdown Actuator:endpoints.shutdown.enabled=false避免应用被非法停止
2.删除生产部署上的 spring-boot-devtoos依赖关系。
3.不要远程暴露mbean spring.application.admin.enabled=false
4.启用html自动转义
![c07475dbd45787cd7c1483304013e06c.png](https://i-blog.csdnimg.cn/blog_migrate/01ec02df5627a249b29a27bb9ea38993.png)
5.使用默认http防火墙StrictHttpFirewall
6.Spring Security身份认证配置,该配置默认为拒绝对之前不匹配请求的访问:
![2291b10a2f882deb39fd8736ebc73e21.png](https://i-blog.csdnimg.cn/blog_migrate/fa39ada62cb892f5931c173d45e369b7.png)
7. 禁用 SpringBoot ActuatorJMX
endpoints
8. Spring Boot Actuator 开启security功能
错误配置:
management
漏洞利用验证:
![a3f0750d33879093c56ed00fcc822544.png](https://i-blog.csdnimg.cn/blog_migrate/ff8b5002d0166ab1ac6e90119083565e.png)
正确配置:
management
漏洞修复验证
![fadf014c7a551bf01fb72a855893f8fd.png](https://i-blog.csdnimg.cn/blog_migrate/7ed739c88d4195d8756a42010fef6dff.png)
总结
作为安全人员经常会被开发问如何修复漏洞,开发需要具体到某行代码如何改动,通过对常见漏洞的复现利用以及安全编码实践,可以加深安全人员对相关漏洞原理的理解,根据业务需要更具体地帮助开发人员写出健壮的代码,预防或修复安全漏洞。
参考资料
https:// github.com/JoyChou93/ja va-sec-code/ 前面的常见注入类漏洞参考了这里的代码。 https:// vulncat.fortify.com/en 后面的不安全加密模式,不安全随机数等配置漏洞参考fortify官方的漏洞知识库。