java 拼接路径优雅方式_Java安全编码实践总结
发布日期:2021-06-24 11:10:48 浏览次数:3 分类:技术文章

本文共 6691 字,大约阅读时间需要 22 分钟。

Java作为企业主流开发语言已流行多年,各种java安全编码规范也层出不穷,本文将从实践角度出发,整合工作中遇到过的多种常见安全漏洞,给出不同场景下的安全编码方式。

本文漏洞复现的基础环境信息:jdk版本:1.8 ,框架:springboot1.5,数据库:mysql5.6和mongodb3.6,个别漏洞使用到不同的开发框架会特别标注。

安全编码实践

Sql注入防范

常见安全编码方法:预编译+输入验证

预编译适用于大多数对数据库进行操作的场景,但预编译并不是万能的,涉及到查询参数里需要使用表名、字段名的场景时(如order by、limit、group by等),不能使用预编译,因为会产生语法错误。常见的预编译写法如下

jdbc:

4e97120d66b9724ce2b3f3828d6592a4.png

Hibernate

0a49631203ffe629e42954d9f2205677.png

Ibatis

74eae2a84b4873872263a8ca2cddd593.png

Mybatis

7543c6f99a69ac0e17a123e6b45cb5aa.png

在无法使用预编译的场景,可以使用数据校验的方式来拦截非法参数,数据校验推荐使用白名单方式。

错误写法:不能使用预编译的场景(直接拼接用户的查询条件)

8dbc999eed1b3a6767a310c5fff5cadd.png

漏洞利用验证:

0a6ec7379f44c99dd881bf4da79b0493.png

不能使用预编译的正确写法(通过白名单验证用户输入):

90ae9bb3f13497a53f448d54c1064fda.png

漏洞修复验证:

fce686d9286d91a5a07985f0487c0db8.png

Nosql注入防范

涉及到非关系型数据库mongdb在查询时不能使用拼接sql的方式,需要绑定参数进行查询,跟关系型数据库的预编译类似

错误写法(拼接用户的查询条件):

b11a61e2f81ae7cc8eb6f13a3b38bd46.png

漏洞利用验证:

6d657c19dc686e7ac189cef66047c9d6.png

正确写法(参数绑定):

ec66ed74966011d54e1d09958acede67.png

漏洞修复验证:

5085bbbaee987da309da1c6308465802.png

Xss防范

白名单校验

适用于纯数字、纯文本等地方,如用户名

Esapi

适用于常规的输入输出,如用户评论

222ee61cc76123817c23ec804e618b9a.png

错误写法(对用户输入内容不做处理):

d280a8852eb32335972c8a900c6287fb.png

正确写法(通过esapi的黑白名单配置来实现富文本xss的过滤):

09764ef9c0bfd788c3084fd9977270bc.png

漏洞利用及修复验证:

21d79e4f7be7d2d33d6fd56024bf7e61.png

XXE注入防范

为了避免 XXE injections,应为 XML 代理、解析器或读取器设置下面的属性:

factory.setFeature(“http://xml.org/sax/features/external-general-entities“,false);

factory.setFeature(“http://xml.org/sax/features/external-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)));

漏洞利用验证:

5e974dfdc9be2f3a04baf51307a665a2.png

a8fc028bc1ca24d0ea436d6c24a2b1ee.png

正确写法(禁止解析部实体):

XMLReaderxmlReader = XMLReaderFactory.createXMLReader();xmlReader.setFeature("[http://xml.org/sax/features/external-general-entities](http://xml.org/sax/features/external-general-entities)",false);xmlReader.setFeature("[http://xml.org/sax/features/external-parameter-entities](http://xml.org/sax/features/external-parameter-entities)",false);xmlReader.parse(newInputSource(newStringReader(body)));

文件上传漏洞

文件名随机,防止被猜解上传路径

限制上传文件大小,防止磁盘空间被恶意占用

限制上传文件类型,防止上传可执行文件

正确写法(限制文件类型大小,通过uuid生成随机文件名保存):

8fdfdd3604b2e276550365b8e554c08e.png

漏洞利用验证

466f886192b96c09b4039f12a2794276.png

漏洞修复验证:

ba44ff4c1631b9d64f44fe375deaa63f.png

文件包含

限制文件在指定目录,逻辑名称绑定文件路径,跟文件上传的处理类似,通过文件id读取对应资源文件

错误写法(直接请求用户设置的资源):

String returnURL = request.getParameter("returnURL");returnnew ModelAndView(returnURL);

漏洞利用验证:

8fbafd2f66d6d7e60725542f20aaf902.png

正确写法(通过文件id和真实路径的映射设置白名单):

if(SecurityUtil.checkid(file_id) ==null) {
return"资源文件不存在!";}returnget_file(SecurityUtil.find_path(file_id));}

文件上传后对应的路径会存储在数据库里,表结构如下:

39ef01c93a55a0d18c272045b67dc712.png

漏洞修复验证

fe7f5940d1ba95b33edded8cced0c678.png

Csrf

常见的框架已经自带了防范csrf的功能,只需要正确的配置启用即可

struts2

JSP使用标签,在struts配置文件中增加token拦截器

页面代码:

530c82d2b198832760c5f2ca90f657a5.png

漏洞修复验证:

968d469fdba9756ed93f0e9db868e5db.png

Spring

正确写法:使用spring-security

4408375bd6bb744d869575a255b81b18.png

漏洞修复验证

287b6bcbb065fef3484f699a8853f852.png

越权

Java通用权限框架(shiro)

进行增删改查操作时采用无法遍历的序号

对于敏感信息,应该进行掩码设置屏蔽关键信息。

垂直越权

角色权限矩阵

6876e39f8697140ad0f3de462bc7553f.png

限制匿名用户和低权限用户,执行操作前检查用户登录状态和权限清单

正确写法(判断用户权限清单是否包含请求的权限):

a6504dbcd9729178caeeb9b3cc3e5ea3.png

漏洞修复验证

a80f615bc97afa0e4ea17855dfcf7ae7.png

水平越权:

操作前判断下当前用户是否有对应数据权限,修复后修复前两次验证,通过返回长度不同可看到水平越权问题已解决。

748d63c1a4dfde2847fd53910b6ae50b.png

url重定向&ssrf

url重定向

对于白名单内的地址,用户可无感知跳转,不在白名单内的地址给用户风险提示,用户选择是否跳转

正确写法:

2a97bfbe08afb197fc76e2cc86936226.png

漏洞修复验证

e38f471341630bdcf6d6f19da7f981ab.png

Ssrf

漏洞利用验证:

34781cd127fc474ee8238ad803e9e931.png

正确写法(限制请求协议,设置白名单域名,避免内网地址探测):

793c444fb19e7ef1c8b1b274f1fd5482.png

漏洞修复验证

e7dbc40b1dec44091abb0c48e0b26a57.png

拒绝服务

正则表达式拒绝服务,这种漏洞需要通过白盒审计发现,黑盒测试比较难发现。

错误写法(正则匹配时未考虑极端情况的资源消耗)

cc35427a55ef673cf1e52acd59a68339.png

漏洞利用验证,随着字符长度增加,响应时间会越来越长,cpu满负荷运转

1d0cc510539d22a80e5e35a19b961f30.png

正确写法(运行超过2秒就中止匹配):

81a84cfefd100a53a7b1c109b0ab230a.png

漏洞修复验证:

7a6e4f61fcefc1f00e9cd29a93711d6f.png

不安全的加密模式

需要通过白盒审计发现漏洞,直接黑盒测试比较难。

错误写法:使用ECB模式,相同明文生成相同密文

70ea7e35cbe444fb5954df1a55bc0f0a.png

漏洞利用验证(使用选定明文攻击从后向前按位猜解):

5e9022817bcd310fd95dd1491e163664.png

正确写法:使用CBC模式,相同明文生成不同密文

Cipher cipher =Cipher.getInstance("AES/CBC/PKCS5Padding");
不安全的随机数

需要通过白盒审计发现漏洞,直接黑盒测试比较难。

错误写法:使用伪随机,相同种子生成相同随机数序列

漏洞利用验证:

需要通过java生成前后2000毫秒内的随机数,然后使用python调用这些随机数尝试暴破

8f3313b1d7345f2d4e9f05a396a3b50a.png

正确写法:使用Securerandom

漏洞修复验证(Securerandom不能指定seed,避免伪随机):

0852e467e5a40c9fb4d026945df7bf33.png

条件竞争

Servlet的单例模式容易导致条件竞争,也是推荐白盒方式审计漏洞。

错误写法:初始积分100个,每天限制签到1次领取1积分

208b5c2fd6dfb6523b16aece0aa59f16.png

漏洞利用验证(10个并发可实现多次签到,这里多并发跟业务功能复杂度和服务器性能有关,如果想必现漏洞,可以在读取签到次数和增加签到次数之间增加2秒延时,可以保证漏洞复现。)

4740e50f9e445fd7b7c3291c6073222a.png

ebb414d23e392a637f42aedb76761b67.png

正确写法:使用线程同步

85f96ee17883db0d72b86a1e1ecb4326.png

漏洞修复验证:

989bd7dc15ffa69a2963f34cf487d994.png

修复后返回数据包速度明显变慢,不能再重复签到领积分

c3165739a0aeaf45152d7bc100f33f62.png

日志伪造防范/http响应拆分防范

日志伪造黑盒测试无法发现,需要通过白盒审计发现漏洞。

错误写法(直接将用户的输入打印到日志文件):

public voidlog_forging(HttpServletRequest request,HttpServletResponse response)throwsException {
logger.info("user: "+request.getParameter("name")+" received 100$;");}

漏洞利用验证(通过%0d%0a插入换行控制符,伪造日志记录)

dda13323a6b9910eace4a0efead16dcc.png

正确写法(过滤换行空格):

public voidlog_forging_sec(HttpServletRequestrequest, HttpServletResponse response)throwsException {
Pattern p = Pattern.compile("\\s*|\t|\r|\n");Matcher m =p.matcher(request.getParameter("name"));String name= m.replaceAll("");logger.info("user: "+name+" received 100$;");}

漏洞修复验证

fe53314542ccc1503d3a0ebf7fb93797.png

http响应拆分,只在低版本web服务器上出现,使用tomcat9未复现这个问题

错误写法

@RequestMapping("/http_splitting")@ResponseBodypublic voidhttp_splitting(HttpServletRequest request,HttpServletResponse response)throwsException {
Stringaddheader=request.getParameter("addheader");response.addHeader("addheader", addheader);}

漏洞修复验证(新版本的web服务器可以自动处理http响应拆分):

dab42e7d616534595358385b57c4c5d4.png

动态代码执行

Runtime.exec

错误写法(直接执行用户输入的命令):

Process p = run.exec(cmd);

e2c4788564b9084e99ea7995224dedf0.png

正确写法:

1.输入净化

2.Switch-case命令映射

if(!cmd.equals("xxx")){
return"command "+cmd+" not allowed!";}

3.使用语言标准api获取系统信息

“当前用户:”+System.getProperty(“user.name”)

漏洞修复验证

931443eb8b34d74106871a49081b528e.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();

漏洞利用验证

48fb3ffd32b40521be8beb6c5a5ecd8b.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!”);

}

漏洞修复验证

594874b55ee4258ba45133c07212bcbe.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());

}

漏洞利用验证:

844f4ac378f18dd6937f662abff5c6de.png

正确写法(使用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());

}

漏洞修复验证

198f42654fd06b47baa4ca14400fce36.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自动转义

75e2221aa5b5d84fc8022a6d16590894.png

5.使用默认http防火墙StrictHttpFirewall

6.Spring Security身份认证配置,该配置默认为拒绝对之前不匹配请求的访问:

2da3d2003f9a5e11fb51b2b2d277acf4.png

7. 禁用 SpringBoot ActuatorJMX

endpoints.jmx.enabled=falsemanagement.endpoints.jmx.exposure.exclude=*

8. Spring Boot Actuator 开启security功能

错误配置:

management.security.enabled=false

漏洞利用验证:

3f1669b922d573d20e7c8f92d61d7dac.png

正确配置:

management.security.enabled=true

漏洞修复验证

f406c297d9bb9e20d193c81b0fcfb010.png

总结

作为安全人员经常会被开发问如何修复漏洞,开发需要具体到某行代码如何改动,通过对常见漏洞的复现利用以及安全编码实践,可以加深安全人员对相关漏洞原理的理解,根据业务需要更具体地帮助开发人员写出健壮的代码,预防或修复安全漏洞。

参考资料

https://github.com/JoyChou93/java-sec-code/ 前面的常见注入类漏洞参考了这里的代码。

https://vulncat.fortify.com/en 后面的不安全加密模式,不安全随机数等配置漏洞参考fortify官方的漏洞知识库。

本文作者:随便看看, 属于FreeBuf原创奖励计划,未经许可禁止转载

76d27ee6ab4ffc55be4ba7111de6ae1d.gif

精彩推荐

fd97aa48c705461664be3e18e672d61f.png
58d8bc2b0ed9d698c0fe5bec6fc9d6fb.png

b7bd38297d0815ebd6ea521e9d2b0951.png58d8bc2b0ed9d698c0fe5bec6fc9d6fb.pnge8faace36ce5bea02f86a28ab3b37f5d.png

ddec73af7a1a34c580c872f11de56798.gif

转载地址:https://blog.csdn.net/weixin_32421193/article/details/112431994 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:realme x2 深度测试打不开_搭载65W超级闪充,realme真我X7手机充电评测
下一篇:洞泾智能机器人产业基地_G60科创走廊洞泾人工智能产业基地(核心区块)暨洞泾镇招商人员培训班顺利开班...

发表评论

最新留言

很好
[***.229.124.182]2024年03月29日 12时21分15秒