简介
DOS 不是那个 windows 的前身,而是 Denial of Service,有做过系统安全方面的小伙伴可能对这个再相熟不过了,简略点讲,DOS 就是服务型响应不过去,从而回绝了失常的服务申请。
明天本文不是要讲怎么发动一个 DOS 攻打,而是讲一下怎么在 java 的代码层面尽量减少 DOS 的可能性。
为什么会有 DOS
为什么会有 DOS 呢?排除歹意攻打的状况下,DOS 的起因就是资源的使用不当。个别意义上咱们所说的资源有 CPU 周期,内存,磁盘空间,和文件描述符等。
如果这些资源受到了歹意应用,那么很有可能会影响失常的零碎服务响应,从而产生 DOS。
怎么在编码层面上,解决 DOS 问题呢?
不合理的资源应用
如果零碎有不合理的资源应用的话,就会造成资源紧缺,从而会产生问题。咱们这里举一些不合理应用资源的例子。
申请用于矢量图的 SVG 文件和字体文件
SVG (全称是 Scalable Vector Graphics) 是一个跟分辨率无关的图形格局。因为 SVG 是基于 XML 的,并且保留着大量的简单门路信息,所以它的体积个别比拟大。咱们在应用的时候要思考。
同时如果应用大量的字体文件也会减轻零碎的资源累赘。
字符串或二进制示意的图片转换
图片是一个文件,文件就能够应用二进制来示意,同样的如果咱们把二进制进行 base64 编码就失去了图片的字符串示意。
如果应用过 webpack 进行前端我的项目构建的同学应该晓得,对于我的项目中的小图像,个别是将其编码成为字符串间接嵌套在 html 中的。然而对于大图片,还是保留的原来的格局。
如果咱们在后盾对字符串或者二进制示意的图片进行转换的时候,可能会须要几倍于原 image 大小的内存。
看一个 imageToBase64 的例子:
public String imageToBase64() {File f = new File("/tmp/abc.jpg");
try {BufferedImage bi = ImageIO.read(f);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(bi, "jpg", baos);
byte[] bytes = baos.toByteArray();
return encoder.encodeBuffer(bytes).trim();} catch (IOException e) {e.printStackTrace();
}
return null;
}
zip 炸弹
为了晋升数据传输的效率,很多时候咱们都会应用压缩算法,比方在 HTTP 中。然而一个压缩过的很小的 zip 文件,解压之后可能会变得十分十分大。
这里给大家介绍一个十分有名的 zip 炸弹。
42.zip 是很有名的 zip 炸弹。它的大小只有 42KB,然而解压之后竟然有 4.5PB 之多。
怎么做的呢?
一个 zip 文件中又蕴含了 16 个 zip 文件,每一个 zip 文件又蕴含了 16 个 zip 文件,这样循环 5 次,产生了 16 的 5 次方个文件,每个文件的大小是 4.3GB,最初导致你的硬盘爆炸了。
感兴趣的敌人能够从 http://www.unforgettable.dk/4… 下载,本人尝试一下。
怎么防止 zip 炸弹呢?
第一种做法在解压过程中检测解压过后的文件大小,如果超出肯定的限度就完结解压。
另一种做法,就是判断压缩文件中是否还有压缩文件,尽量减少这种压缩套压缩的做法。
billion laughs attack
billion laughs attack 是解析 XML 文件产生的 DOS 攻打。
先上代码:
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ELEMENT lolz (#PCDATA)>
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>
下面的代码定义了 10 个 entities,每个 entity 又蕴含了 10 个后面定义的 entity,从而实现了指数级的字符串增长。最初生成了蕴含 10 亿个字符串的 xml 文件。
个别状况下,咱们会将 xml 放在内存中保留,这么多的字符串最初会耗尽咱们的内存,最终导致 DOS。
咱们能够通过设置 XMLConstants.FEATURE_SECURE_PROCESSING 来避免这种攻打。
hashMap 中插入太多雷同 hashcode 的元素
咱们晓得 java 中 hashMap 是用拆散链表来解决 hash 抵触的,如果插入了太多雷同 hashcode 的元素,就会导致这个 hashcode 对应的链表变得很长,从而查问效率升高,影响程序性能。
正则表达式乐观回溯
什么是乐观回溯呢?
咱们举个例子,如果大家对正则表达式曾经很相熟了。
如果咱们应用 /^(x*)y$/ 来和字符串 xxxxxxy 来进行匹配。
匹配之后第一个分组(也就是括号外面的匹配值)是 xxxxxx。
如果咱们把正则表达式改写为 /^(x*)xy$/ 再来和字符串 xxxxxxy 来进行匹配。匹配的后果就是 xxxxx。
这个过程是怎么样的呢?
首先 (x) 会尽可能的匹配更多的 x,晓得遇到字符 y。这时候 (x) 曾经匹配了 6 个 x。
接着正则表达式继续执行 (x) 之后的 xy,发现不能匹配,这时候 (x) 须要从曾经匹配的 6 个 x 中,吐出一个 x,而后从新执行正则表达式中的 xy,发现可能匹配,正则表达式完结。
这个过程就是一个回溯的过程。
如果正则表达式写的不好,那么就有可能会呈现乐观回溯。
还是下面的例子,然而这次咱们用 /^(x*)y$/ 来和字符串 xxxxxx 来进行匹配。
依照下面的流程,咱们晓得正则表达式须要进行 6 次回溯,最初匹配失败。
思考一些极其的状况,可能会导致回溯一个十分大的次数,从而导致 CPU 占用率飙升。
序列化和序列化
咱们将 java 对象存进文件或者进行网络传输的时候,都须要应用到序列化和反序列化。
如果咱们在对一个 java 对象进行反序列化的时候,很可能就会加载恶意代码。
因而咱们须要在反序列化的时候进行住够的安全控制。
大量的输入日志
通常咱们为了调试程序或者寻找问题都会输入大量的日志,如果日志文件太大会影响到磁盘空间的应用。
同时,日志写入操作也会对同一个硬盘上的其余写入操作产生影响。所以日志输入要抓住重点。
有限循环
在应用循环的时候肯定要留神,不要产生有限循环的状况。
应用第三方 jar 包
古代的 java 程序都会应用第三方 jar 包,然而第三方 jar 包的安全性还是须要咱们留神的。如果某些第三方 jar 包中蕴含有恶意代码,那么会对咱们的零碎造成十分重大的影响。
Xpath 攻打
XPath 解析器是用来解析 XML 构造的工具,然而在应用 XPath 解析器的时候,咱们须要留神避免注入攻打。
举个例子:
<users>
<user>
<name> 张三 </name>
<username>zhangsan</username>
<password>123</password>
</user>
<user>
<name> 李四 </name>
<username>lisi</username>
<password>456</password>
</user>
如果应用 xpath,咱们须要这样来验证一个用户是否存在:
//users/user[username/text()='lisi'and password/text()='456']
如果用户传入 username = ‘lisi’ 和 password = ‘456’, 那么能够匹配胜利,证实用户存在。
然而如果用户输出相似 ‘ or 1=1 or ”=’ 的值,咱们看下 xpath 的解析后果:
//users/user[username/text()=''or 1=1 or''=''and password/text()='' or 1=1 or ''='']
后果产生和 SQL 注入一样的后果。
开释所有资源
通常来说,咱们在进行文件操作,锁获取操作的的时候会申请相应的资源,在应用完这些资源过后,千万要记得开释他们。
在 JDK7 之后,引入了 try with 表达式,咱们能够将要开释的资源放入 try 语句内,在程序执行结束,资源会主动开释。
举个例子:
public R readFileBuffered(InputStreamHandler handler) throws IOException {try (final InputStream in = Files.newInputStream(path)) {handler.handle(new BufferedInputStream(in));
}
}
下面的 InputStream 会主动开释。
本文已收录于 http://www.flydean.com/java-security-code-line-dos/
最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!
欢送关注我的公众号:「程序那些事」, 懂技术,更懂你!