基於Redis的防刷票、防刷短信、及所有防刷系統的設計

一、背景介紹

在設計大型Web網站時,特別是涉及到金錢交易的,如電商系統,免費抽獎,1分錢秒殺等網站,一些不法黑客會想辦法攻破來獲取“利益”。他們常用的手段,大概分爲以下幾種:
1、初級版:通過抓包工具,抓取網站請求URL,分析請求的參數,然後通過編寫腳本程序,模擬正常的請求,自動批量發送請求
2、中級版:有些網站對於初級版的攻擊,它們做了單ip限制和單用戶ID請求頻次限制。對於這種情況,黑客們,就會抓取一批肉機,或者通過動態代理IP工具,僞造不同的ip請求,繞過網站的限制。
3、高級版:通過購買虛擬的手機驗證碼,如淘寶上有一些專門售賣短信驗證碼的,然後在各大網站上註冊用戶,這樣他就有一大批殭屍賬戶。通過一批用戶,然後動態ip,跑腳本程序,來攻擊網站。
4、終極版:通過3上面購買的一大批賬戶,來編寫程序,模擬用戶的正常操作,如登錄,簽到,瀏覽網頁,評論,訂單等,讓網站覺得你的用戶不是殭屍用戶,而是真實用戶。所謂的就是把賬戶“養起來”,然後還是通過腳本程序,來批量發送請求攻擊網站。

這種情況特別普遍,特別是網站在做大型促銷活動,如1分錢秒殺,免費抽獎,交易返現,以及在某寶,某東上面刷單的,基本上就是上面4種套路。

二、基於Redis防刷系統簡單設計

通過上面的分析,我們已經知道“對手”的思路,針對他們的套路,我們來逐一應對即可。對於一般的黑客,常用的方法是第1、2種。
現在就基於第1來設計一個簡單的防刷系統。
1、配置參數
在redis設置參數變量如下
risk:iplimit:1000 //單ip限制參數配置,限制1000次
risk:idlimit:100 //單用戶id限制參數配置,限制50次

2、Redis計數器Key設計
由於redis可以設置自動過期時間,正好利用這個特性,來設置計數key值,24小時後過期自動刪除,計數重新開始。
key值使用 id:ip:調用次數 組合來存儲。這樣查詢id調用頻次和ip調用頻次都可以查到。

3、程序實現

3.1 獲取http請求真實ip

public static String getIpAddr(HttpServletRequest request){
        String ipAddress = request.getHeader("x-forwarded-for");
            if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")){
                    //根據網卡取本機配置的IP
                    InetAddress inet=null;
                    try {
                        inet = InetAddress.getLocalHost();
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }
                    ipAddress= inet.getHostAddress();
                }
            }
            //對於通過多個代理的情況,第一個IP爲客戶端真實IP,多個IP按照','分割
            if(ipAddress!=null && ipAddress.length()>15){ //"***.***.***.***".length() = 15
                if(ipAddress.indexOf(",")>0){
                    ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));
                }
            }
            return ipAddress; 
    }

3.2 計數

//獲得真實ip
String ip = getIpAddr(request);
//獲取當前用戶
String userId = ((User)session.getAttribute("SessionUser")).getUserId();
//計數key
String key = "risk:"+userId+":"+ip;
//redis記錄每次請求,自增加1
jedis.incr(key);
//設置24小時過期(ps:時間可以作爲配置參數,寫靈活點)
jedis.expire(key, 24*60*60);

3.3 限制頻次

//獲取限制次數
String key = "risk:"+userId+":"+ip;
String limit = jedis.get(key);
int limitCount = 0;
if(StringUtils.isNotBlank(limit)){
    limitCount = Integer.valueOf(limit);
}

key = "risk:"+userId+":"+ip;
//判斷key是否存在
if(limitCount > 0 && jedis.exists(key)){
    String value = jedis.get(key);
    value = StringUtils.isNotBlank(value) && !"nil".equalsIgnoreCase(value) ? value : null;
    if(StringUtils.isNotBlank(value)){
        //判斷是否達到限制次數
        if(Integer.valueOf(value) > limitCount){
            return false; //返回false
        }
    }
}

return true;

三、總結

針對第1種攻擊,總結解決思路爲3步:
1、配置參數上限
2、計數
3、判斷是否達到限制

對於類似系統,如防刷票,防刷短信驗證碼,等等都可以是一種參考的方案。
由於風控系統的建設,不是一朝一夕的事,對於第2,3,4種,則需要大數據,實時並行計算,以及更復雜的算法才能完成。所以不做說明了。此篇文章的目的,只是爲了提供一種設計思路。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章