1. 程式人生 > >關於防止微信投票刷票行為的一些思考

關於防止微信投票刷票行為的一些思考

背景介紹

微信投票在這幾年一直很熱門,只要是個活動往往都做一個投票的功能。刷票已形成了一個龐大的產業鏈但如何防止刷票行為就很讓人頭疼了。首先要清楚微信的刷票行為,微信投票是根據openid來判斷一個使用者是否已投過票。

openid是加密後的微訊號,每個使用者對每個公眾號的openid是唯一的。

這個判斷依據有較大的漏洞,就是隻能判斷openid是否重複,但無法校驗openid是不是真實的。而且就算openid是真實的,刷票軟體也有批量的正確openid。刷票軟體就是通過使用HttpClient等類似客戶端發包,把openid和投票資訊post至伺服器。由於刷票軟體動態偽裝ip,擁有大量openid,很容易就在沒有完善防刷的應用投大量的票。

幾種防止刷票的方法

1、只有關注了公眾號才能投票

在服務呼叫獲取使用者基礎資訊的API

https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN

access_token是全域性呼叫憑證

介面會返回以下資料

{
    "subscribe": 1,        // 1為關注  0為未關注
    "openid": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M", 
    "nickname": "Band"
, "sex": 1, "language": "zh_CN", "city": "廣州", "province": "廣東", "country": "中國", "headimgurl": , "subscribe_time": 1382694957, "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL" "remark": "", "groupid": 0 }

當用戶投票且未關注時,我們可根據subscribe是0還是1判斷有沒有關注,若沒有關注則轉至公眾號的二維碼頁面並提示使用者先長按二維碼關注公眾號。此方法的缺點就是每次投票都要與微信伺服器進行互動。

2、判斷refer和User-Agent

以下為一個request header的部分引數示例:

Host:
localhost:8080
Origin:
http://localhost:8080
Pragma:
no-cache
Referer:
http://www.example.com/vote.jsp
User-Agent:
Mozilla/5.0 (iPhone; CPU iPhone OS 9_0_2 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) 
Mobile/13A452 MicroMessenger/6.2.3 NetType/WIFI Language/zh_CN
X-Requested-With:
XMLHttpRequest

Referer為上一個訪問的頁面,所以refer必須要為投票的頁面地址。
User-Agent裡面必須有關鍵詞MicroMessenger

3、限制客戶端投票次數

    用ip當成同一個ip投票次數受限制,由於很多時候使用nginx或apache之類的代理伺服器,因此直接使用HttpServletRequest的getRemoteAddr()很多時候取得的是代理伺服器的ip,而我們要取得的是真實的ip址。

下面是一個獲取真實ip的示例程式碼

 public String getIpAddr() {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("CLIENTIP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

很多刷票軟體都使用ip代理池,所以ip限制只能一部分刷票行為。

4、當投票量大於閱讀量時就是刷票行為

儲存頁面閱讀量,投票完成後,計算票數若投票量大於閱讀量時就是刷票行為。

5、在提交表單資料的地方使用校驗碼

服務端返回一個校驗碼,在dom初始化的時候頁面使用js加密這個校驗碼,commit的時候提交這個加密的校驗碼,服務端再判斷這個加密碼的校驗碼是否正確。校驗碼設定使用N次後就作廢。
以下為虛擬碼:

  • jsp頁面
<%=request.setAttribute("_check_code_", UUID.randomUUID())%>;
var _check_code_salt_  = "gx=**&^%%$$###@#$---eeax221";
$(function(){
    var _check_code = '<%=request.getAttribute("_check_code_")%>';
});
$.ajax({
    url: "http://www.example.com/vote.do",
    params: { checkCode:  md5(_check_code + _check_code_salt_ ), openid: OPENID },
    type:"post",
    success:function() {

    }
});
  • 服務端處理
String checkCodeSalt = "gx=**&^%%$$###@#$---eeax221";
String serverCode = MD5.get((String) request.getAttribute("_check_code_") + checkCodeSalt);
String pageCode = (String) request.getAttribute("checkCode");
if (serverCode.equals(pageCode)) {
    response.setCharacterEncoding("UTF-8");
    response.setContentType("application/json; charset=utf-8");
    PrintWriter out = null;
    out = response.getWriter();
    out.append("{errorCode:'1'}");
    return;
} else {
    doSomething();
}

注:加一個加密的過程是為了讓使用HttpClient的模擬訪問行為變的更困難

以上的方法可結合專案的實際情況組合使用,五項組合起來使用基本上能杜絕大部分的刷票行為。