关于java:java安全编码指南之拒绝Denial-of-Service

1次阅读

共计 4292 个字符,预计需要花费 11 分钟才能阅读完成。

简介

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/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

欢送关注我的公众号:「程序那些事」, 懂技术,更懂你!

正文完
 0