年會抽獎程序的一些總結

起源

2019年年會的到來,固然免不了激動人心的抽獎環節啦,那直接延用上一年的抽獎程序吧,然而Boss但願今年的抽獎程序可以能讓全部人都參與進來,一塊兒來搶有限獎品,先到先得,而不是站在那裏盯着屏幕。javascript

OK,程序內容大概是這樣子,每一個人在手機瀏覽器打開抽獎程序界面,系統會隨機給個數字,誰戳屏幕上的圓形最快最準,就能參與抽獎活動,有多少獎品就有多少場battle(僅限手機瀏覽器打開 & 每人僅限得到一個獎品html

重要的是,年會以前得把程序公佈出來,讓其餘同事想辦法做弊,硬件做弊軟件做弊都容許,咱們主要分析軟件做弊並制定對應防護策略,就像是一場CTF,對方是攻擊,咱們是防護。前端

本文有引用他人文章,如有侵權或其它不當,會當即刪除。java

開發過程

我跟另外一個同事完成這個項目,他負責後端,我負責前端,總體採用React+Nodejs+Express+MySQL的架構,這個項目有實時的需求,所以咱們用到了socket.ioreact

首先,咱們分析了對方做弊的可能:webpack

  1. 經過抓包分析,成功僞造點擊請求
  2. 找到圓形位置,實現高準確率的點擊
  3. 搞垮其他競賽者的帳號,本身穩步前進
  4. 直接搞掉服務器,修改數據庫

對於做弊3,帳號列表是年會抽獎前10分鐘公佈的,因此做弊者沒有充足的準備時間。
對於做弊4,都是本身人,應該不會那麼狠吧...git

那麼,咱們主要針對做弊1做弊2作了如下防護:
後端防護github

  • 校驗請求參數是否有效
  • 分析正確點擊頻率,超過正常點擊閥值則做懲罰處理
  • 點擊錯誤次數過多則做懲罰處理

前端防護web

  • 使用webpack-obfuscator混淆打包js代碼,加大代碼分析難度
  • react-konva畫圓形,防止對手輕易獲取DOM節點並執行點擊事件
  • 圓形的顏色和位置隨機,防按鍵精靈之類的軟件
  • 核心websocket請求採用白盒加密
  • 普通請求採用rsa+aes混合加解密

通過了一個星期的開發和防護策略研究,咱們把程序弄出來了,等待年會的到來。

算法

抽獎過程

年會抽獎那天,在大屏幕上面能夠看到每一個人的點擊數,注意到有個同事的按擊次數值飈得極快,很明顯他做弊成功了,但我想不明白是怎麼作到的。因而年會結束後我向他請教,才得知做弊手法。

結果分析

做弊手法以下,很是簡單,在手機瀏覽器的地址欄輸入下面一段代碼,回車便可執行。跟我同樣不知道瀏覽器地址欄能夠執行js代碼的童鞋,請看這篇文章瀏覽器地址欄運行JavaScript代碼

javascript:Math.random=function(){return 1;}

因爲我在圓形隨機位置和隨機顏色的設置上用到了Math.random這個方法,此時,他的按擊圓形就定死在同一個位置,衆人皆動我獨靜,並且都是正常點擊請求,必須贏呀!

// 修改前
function getRandomNumber(min, max) {
    const range = max - min;
    const random = Math.random();
    const num = min + range * random;
    return num;
}

代碼修補

找到問題所在,很明顯我須要找個隨機函數方法替換Math.random,因而我google了幾篇文章,都提到線性同餘生成器(LCG, Linear Congruential Generator),貌似全部運行庫都是採用這種算法生成僞隨機數,我就把getRandomNumber函數改爲下面的模樣,問題獲得了暫時的解決。

// 修改後
const getRandomNumber = (function() {
    let seed = Date.now();
    function random() {
        //線性同餘生成器(LCG, Linear Congruential Generator)
        seed = (seed * 9301 + 49297) % 233280;
        return seed / (233280.0);
    };
    return function rand(min,max) {
        const range = max - min;
        const random_num = random();
        const num = min + range * random_num;
        return num;
    };
})();

經驗分享

可是,getRandomNumber的代碼功能仍是很是容易改,畢竟混淆過的js依舊是裸奔着的,這一點是避免不了的。
好比說,咱們都知道知乎能夠設置文章的權限,例如禁止轉載之類的,有一次我看到這篇知乎文章應對流量劫持,前端能作哪些工做?,想複製代碼下來運行下卻被告知禁止轉載,打開chrome控制檯查看代碼塊內容,惋惜代碼內容變成富文本內容形式,因而我另尋法子,直接在js上面修改。

首先,定位到複製功能觸發的文件和代碼段。

第二步,在控制檯Sources->Overrides下,打勾Enable Local Overrides

第三步,在控制檯下面Sources->Page下,找到剛剛定位到的代碼文件,右鍵Save for overrides

第四步,直接把handleCopy的處理邏輯註釋掉,在控制檯的Overrides下直接修改,或者在選定保存的本地文件夾中修改。


最後,保存修改後的代碼,刷新頁面,便可複製成功。

var SriPlugin = require('webpack-subresource-integrity');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ScriptExtInlineHtmlWebpackPlugin = require('script-ext-inline-html-webpack-plugin');
var ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');
var path = require('path');
var WebpackAssetsManifest = require('webpack-assets-manifest');
var writeJson = require('write-json');

Reference

socket.io
白盒加密
webpack-obfuscator
JavaScript根據種子生成隨機數實現方法
瀏覽器地址欄運行JavaScript代碼
在 Chrome DevTools 中調試 JavaScript 入門

相關文章
相關標籤/搜索