java安全編碼指南之:拒絕Denial of Service

簡介

DOS不是那個windows的前身,而是Denial of Service,有作過系統安全方面的小夥伴可能對這個再熟悉不過了,簡單點講,DOS就是服務型響應不過來,從而拒絕了正常的服務請求。html

今天本文不是要講怎麼發起一個DOS攻擊,而是講一下怎麼在java的代碼層面儘可能減小DOS的可能性。前端

爲何會有DOS

爲何會有DOS呢?排除惡意攻擊的狀況下,DOS的緣由就是資源的使用不當。通常意義上咱們所說的資源有CPU週期,內存,磁盤空間,和文件描述符等。java

若是這些資源受到了惡意使用,那麼頗有可能會影響正常的系統服務響應,從而產生DOS。webpack

怎麼在編碼層面上,解決DOS問題呢?web

不合理的資源使用

若是系統有不合理的資源使用的話,就會形成資源緊缺,從而會產生問題。咱們這裏舉一些不合理使用資源的例子。正則表達式

請求用於矢量圖的SVG文件和字體文件

SVG (全稱是 Scalable Vector Graphics) 是一個跟分辨率無關的圖形格式。由於SVG是基於XML的,而且保存着大量的複雜路徑信息,因此它的體積通常比較大。咱們在使用的時候要考慮。算法

同時若是使用大量的字體文件也會加劇系統的資源負擔。windows

字符串或二進制表示的圖片轉換

圖片是一個文件,文件就可使用二進制來表示,一樣的若是咱們把二進制進行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/

最通俗的解讀,最深入的乾貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!

歡迎關注個人公衆號:「程序那些事」,懂技術,更懂你!

相關文章
相關標籤/搜索