java安全编码指南之:拒绝Denial of Service
文章目錄
- 簡介
- 為什么會有DOS
- 不合理的資源使用
- 請求用于矢量圖的SVG文件和字體文件
- 字符串或二進(jìn)制表示的圖片轉(zhuǎn)換
- zip炸彈
- billion laughs attack
- hashMap中插入太多相同hashcode的元素
- 正則表達(dá)式悲觀回溯
- 序列化和序列化
- 大量的輸出日志
- 無限循環(huán)
- 使用第三方j(luò)ar包
- Xpath攻擊
- 釋放所有資源
簡介
DOS不是那個windows的前身,而是Denial of Service,有做過系統(tǒng)安全方面的小伙伴可能對這個再熟悉不過了,簡單點(diǎn)講,DOS就是服務(wù)型響應(yīng)不過來,從而拒絕了正常的服務(wù)請求。
今天本文不是要講怎么發(fā)起一個DOS攻擊,而是講一下怎么在java的代碼層面盡量減少DOS的可能性。
為什么會有DOS
為什么會有DOS呢?排除惡意攻擊的情況下,DOS的原因就是資源的使用不當(dāng)。一般意義上我們所說的資源有CPU周期,內(nèi)存,磁盤空間,和文件描述符等。
如果這些資源受到了惡意使用,那么很有可能會影響正常的系統(tǒng)服務(wù)響應(yīng),從而產(chǎn)生DOS。
怎么在編碼層面上,解決DOS問題呢?
不合理的資源使用
如果系統(tǒng)有不合理的資源使用的話,就會造成資源緊缺,從而會產(chǎn)生問題。我們這里舉一些不合理使用資源的例子。
請求用于矢量圖的SVG文件和字體文件
SVG (全稱是 Scalable Vector Graphics) 是一個跟分辨率無關(guān)的圖形格式。因?yàn)镾VG是基于XML的,并且保存著大量的復(fù)雜路徑信息,所以它的體積一般比較大。我們在使用的時候要考慮。
同時如果使用大量的字體文件也會加重系統(tǒng)的資源負(fù)擔(dān)。
字符串或二進(jìn)制表示的圖片轉(zhuǎn)換
圖片是一個文件,文件就可以使用二進(jìn)制來表示,同樣的如果我們把二進(jìn)制進(jìn)行base64編碼就得到了圖片的字符串表示。
如果使用過webpack進(jìn)行前端項目構(gòu)建的同學(xué)應(yīng)該知道,對于項目中的小圖像,一般是將其編碼成為字符串直接嵌套在html中的。但是對于大圖片,還是保存的原來的格式。
如果我們在后臺對字符串或者二進(jìn)制表示的圖片進(jìn)行轉(zhuǎn)換的時候,可能會需要幾倍于原image大小的內(nèi)存。
看一個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炸彈
為了提升數(shù)據(jù)傳輸?shù)男?#xff0c;很多時候我們都會使用壓縮算法,比如在HTTP中。但是一個壓縮過的很小的zip文件,解壓之后可能會變得非常非常大。
這里給大家介紹一個非常有名的zip炸彈。
42.zip 是很有名的zip炸彈。它的大小只有42KB,但是解壓之后居然有4.5PB之多。
怎么做的呢?
一個zip文件中又包含了16個zip文件,每一個zip文件又包含了16個zip文件,這樣循環(huán)5次,產(chǎn)生了16的5次方個文件,每個文件的大小是4.3GB,最后導(dǎo)致你的硬盤爆炸了。
感興趣的朋友可以從http://www.unforgettable.dk/42.zip 下載,自己嘗試一下。
怎么避免zip炸彈呢?
第一種做法在解壓過程中檢測解壓過后的文件大小,如果超出一定的限制就結(jié)束解壓。
另一種做法,就是判斷壓縮文件中是否還有壓縮文件,盡量減少這種壓縮套壓縮的做法。
billion laughs attack
billion laughs attack是解析XML文件產(chǎn)生的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,從而實(shí)現(xiàn)了指數(shù)級的字符串增長。最后生成了包含10億個字符串的xml文件。
一般情況下,我們會將xml放在內(nèi)存中保存,這么多的字符串最后會耗盡我們的內(nèi)存,最終導(dǎo)致DOS。
我們可以通過設(shè)置 XMLConstants.FEATURE_SECURE_PROCESSING 來防止這種攻擊。
hashMap中插入太多相同hashcode的元素
我們知道java中hashMap是用分離鏈表來處理hash沖突的,如果插入了太多相同hashcode的元素,就會導(dǎo)致這個hashcode對應(yīng)的鏈表變得很長,從而查詢效率降低,影響程序性能。
正則表達(dá)式悲觀回溯
什么是悲觀回溯呢?
我們舉個例子,假如大家對正則表達(dá)式已經(jīng)很熟悉了。
假如我們使用/^(x*)y$/ 來和字符串xxxxxxy來進(jìn)行匹配。
匹配之后第一個分組(也就是括號里面的匹配值)是xxxxxx。
如果我們把正則表達(dá)式改寫為 /^(x*)xy$/ 再來和字符串xxxxxxy來進(jìn)行匹配。 匹配的結(jié)果就是xxxxx。
這個過程是怎么樣的呢?
首先(x*)會盡可能的匹配更多的x,知道遇到字符y。 這時候(x*)已經(jīng)匹配了6個x。
接著正則表達(dá)式繼續(xù)執(zhí)行(x*)之后的xy,發(fā)現(xiàn)不能匹配,這時候(x*)需要從已經(jīng)匹配的6個x中,吐出一個x,然后重新執(zhí)行正則表達(dá)式中的xy,發(fā)現(xiàn)能夠匹配,正則表達(dá)式結(jié)束。
這個過程就是一個回溯的過程。
如果正則表達(dá)式寫的不好,那么就有可能會出現(xiàn)悲觀回溯。
還是上面的例子,但是這次我們用/^(x*)y$/ 來和字符串xxxxxx來進(jìn)行匹配。
按照上面的流程,我們知道正則表達(dá)式需要進(jìn)行6次回溯,最后匹配失敗。
考慮一些極端的情況,可能會導(dǎo)致回溯一個非常大的次數(shù),從而導(dǎo)致CPU占用率飆升。
序列化和序列化
我們將java對象存進(jìn)文件或者進(jìn)行網(wǎng)絡(luò)傳輸?shù)臅r候,都需要使用到序列化和反序列化。
如果我們在對一個java對象進(jìn)行反序列化的時候,很可能就會加載惡意代碼。
因此我們需要在反序列化的時候進(jìn)行住夠的安全控制。
大量的輸出日志
通常我們?yōu)榱苏{(diào)試程序或者尋找問題都會輸出大量的日志,如果日志文件太大會影響到磁盤空間的使用。
同時,日志寫入操作也會對同一個硬盤上的其他寫入操作產(chǎn)生影響。所以日志輸出要抓住重點(diǎn)。
無限循環(huán)
在使用循環(huán)的時候一定要注意,不要產(chǎn)生無限循環(huán)的情況。
使用第三方j(luò)ar包
現(xiàn)代的java程序都會使用第三方j(luò)ar包,但是第三方j(luò)ar包的安全性還是需要我們注意的。如果某些第三方j(luò)ar包中包含有惡意代碼,那么會對我們的系統(tǒng)造成非常嚴(yán)重的影響。
Xpath攻擊
XPath 解析器是用來解析XML結(jié)構(gòu)的工具,但是在使用XPath 解析器的時候,我們需要注意防止注入攻擊。
舉個例子:
<users><user><name>張三</name><username>zhangsan</username><password>123</password></user><user><name>李四</name><username>lisi</username><password>456</password></user>如果使用xpath,我們需要這樣來驗(yàn)證一個用戶是否存在:
//users/user[username/text()='lisi'and password/text()='456']如果用戶傳入username = ‘lisi’ 和 password = ‘456’, 那么可以匹配成功,證明用戶存在。
但是如果用戶輸入類似 ’ or 1=1 or ‘’=’ 的值,我們看下xpath的解析結(jié)果:
//users/user[username/text()=''or 1=1 or ''='' and password/text()='' or 1=1 or ''='']結(jié)果產(chǎn)生和SQL注入一樣的結(jié)果。
釋放所有資源
通常來說,我們在進(jìn)行文件操作,鎖獲取操作的的時候會申請相應(yīng)的資源,在使用完這些資源過后,千萬要記得釋放他們。
在JDK7 之后,引入了try with表達(dá)式,我們可以將要釋放的資源放入try語句內(nèi),在程序執(zhí)行完畢,資源會自動釋放。
舉個例子:
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/
最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發(fā)現(xiàn)!
歡迎關(guān)注我的公眾號:「程序那些事」,懂技術(shù),更懂你!
總結(jié)
以上是生活随笔為你收集整理的java安全编码指南之:拒绝Denial of Service的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 看动画学算法之:平衡二叉搜索树AVL T
- 下一篇: 巧用HashMap一行代码统计单词出现次