開箱91App瘋型轉轉樂

Kuro Sean
11 min readAug 28, 2020

--

前幾天我才在 I am Programmer,I have no life. 裡看到下面這張圖

看到的時候就覺得一定是惡搞才這樣寫的拉,實際上哪有人會這樣寫code

結果我今天在 純靠北工程師裡看到這樣的貼文

真是不敢相信我的眼睛啊~~(已瞎

為什麼你要這樣塞資料到 Array 裡啊~~~

而且Array本來就是從0開始啊!你塞了一個 無意義,只是方便記順序 你當Array[0] 是塑膠做的喔!

底下的留言有

  1. 寫得太好了!完全符合KISS(Keep It Simple, Stupid)原則
  2. 有註解,是個好工程師
  3. PM : 你今天績效不錯 !寫了很多行code

剛好下面有人貼了網站連結 http://tw.91app.com/act/20200818001257/

所以我就決定來開箱一下這個網站,看到底會有多少令人吐槽的code在裡面

首先這是網站長的樣子(為了之後網站可能會被拿掉我盡量放圖上來

點了start 之後輪盤會開始轉,然後停下來之後就會跳一個modal

原本我還想說該不會超瞎的連這個畫面都是一個靜態圖片的,看了一下HTML還好沒有這麼瞎,不過好像也沒好到哪裡去就是。

之後點了右上角的X或是在玩一次就會重新load這個網頁

在 source code裡面也看得到這一段,嗯!真是好人工程師有加註解XD

在玩了三次之後就會出現這樣的畫面

根據遊戲規則

耶…..不過到這個網頁也不用登入會員就可以玩了說(好佛心

好了大概介紹完這個網站,接下來就實際來開箱

首先既然有遊玩次數的限制,那就代表一定有一個地方會記錄玩家玩了幾次

所以我們先來看一下有沒有任何的request送出去給 server,打開devtool看了一下 Network裡的XHR,在我點下跟轉盤結束後並沒有任何的request送出去,即使之後網頁重新載入後也沒有收到任何的 GET request 那就代表,這個網頁完全沒有跟伺服器溝通,不管是記錄遊玩幾次或是折價券的號碼甚至到所謂的輪盤隨機的結果,實作完全在前端。

所以這時候就來合理的判斷一下可能是記在 cookie 上,那我們就來翻一下 cookie吧。看了一下cookie果然有一個叫 count_ 開頭的 cookie name,然後他的值是 2。

所以只要改了這個值以後應該就可以再重新玩了,不然開無痕模式或是清cookie都可以

接下來就是要來看 source code了

打開 source code就馬上看到令人崩潰的程式碼

這個長長長長的Hash是怎麼回事啊!!!
讓我想起了這個廣告

大佬~不是code 寫得越多行領的錢就越多啊~~
看起來那個很長的hash是要記錄隨機到什麼數字就要跳哪一個獎項,我們等等再來看那個部分。

現在我們先來看獎項的折價券序號,在code裡面有發現這樣的東西。
嗯~看起來就是儲存折價券的地方啊~

不過title的地方有Unicode編碼看不太出來是什麼,好險這個gift變數是寫全域的,那我們就在 console裡 log出來看看吧

好喔~看起來我們連轉盤都不用轉了直接在 console 裡下 gift 按Enter 就可以知道所有的折價碼了(草

接下來我們來看看有沒有辦法是可以不用改寫 cookie的值就可以玩很多次

在 code裡有這麼一段,看起來是主要負責判斷有沒有超過3次的邏輯
有一個變數叫 total_of_day 看起來就是記上限次數,然後 count_check 的function是來檢查有沒有玩超過三次,function裡面的get_count 應該就是從cookie取出value,所以兩個數字相減之後如果沒有大於0就代表已經不能玩了

要是我們能改寫 total_of_day的數字的話那是不是就可以玩超多次了!

那我們來實際試試看,步驟是這樣的

  1. 把cookie的值改成3,顯示出到達遊玩上限的畫面
  2. 切到console去把 total_of_day 改成99999
  3. 重新整理畫面看能不能在玩一次

嗯?看起來好像沒用耶?為什麼?

其實原因是在一開始程式執行的時候就已經做好檢查了

所以之後去console改值也沒有用,不過難道這樣就放棄了嗎

不!!還沒,所以我們要找另一個方法,既然畫面一重整程式就會執行的話,那我們就在他執行之前下中斷點去改值不就好了

JS執行的順序會是 變數先被定義然後後來才是function會被定義,所以我們在 total_of_day 的之後下中斷點跟count_check function裡的remaining下中斷點,所以我們的程式執行的順序會是

  1. total_of_day 先被定義為 3
  2. 中斷停在179行
  3. 之後到console去把 total_of_day 設為 99999
  4. 之後跳到下一個中斷點就可以看到 total_of_day變99999,所以 99999–3大於0,所以就可以繼續玩了~~(撒花

最後其實如果想要一直玩的話也不用這麼麻煩拉直接把cookie的值改成一個超大的負數就好,這樣就可以一直轉下去

講完怎麼無限轉轉轉之後還是要來code review一下

如果只是要根據gift裡的id來決定要加麼class的話,直接寫這樣不好嗎?

$(".game_outer ul li:nth-child(1) img").addClass(`game${id}`);

看起來那個 GAME 的Array也沒用到其他地方了,然後如果真的要塞Array的話可以直接塞拉

const GAME = ["game_01", "game_02", ....]

要比較炫炮一點的可以這樣塞

Array.from(Array(8), (_, i) => `game0${i+1}`)
# => ["game01", "game02", "game03", "game04", "game05", "game06", "game07", "game08"]

然後可以好好正視一下Array的index是從0開始這件事嗎,一直逃避也不是辦法阿~

然後看起來下面這一段code完全沒用到啊,資料裡面的type也只有code沒有link

if (gift.type == 'code') {
type = 1;
} else if (gift.type == 'link') {
type = 2;
}
--------------$(PRIZE[type]).delay(3500).fadeIn(300);
# PRIZE的array看起來也只有用到1而已

再來就是那個長長長長的Hash了

var lottery = {
"1": 1,
"2": 2,
"3": 2,
"4": 2,
"5": 2,
"6": 2,
"7": 2,
"8": 2,
"9": 2,
"10": 2,
"11": 2,
"12": 2,
"13": 2,
"14": 2,
"15": 2,
"16": 2,
"17": 2,
"18": 2,
"19": 2,
"20": 2,
"21": 2,
"22": 2,
"23": 2,
"24": 2,
"25": 2,
"26": 2,
"27": 2,
"28": 2,
"29": 2,
"30": 2,
"31": 2,
"32": 3,
"33": 3,
"34": 3,
"35": 3,
"36": 3,
"37": 3,
"38": 3,
"39": 3,
"40": 3,
"41": 3,
"42": 3,
"43": 3,
"44": 3,
"45": 3,
"46": 3,
"47": 4,
"48": 4,
"49": 4,
"50": 4,
"51": 5,
"52": 6,
"53": 6,
"54": 6,
"55": 6,
"56": 6,
"57": 6,
"58": 6,
"59": 6,
"60": 6,
"61": 6,
"62": 6,
"63": 6,
"64": 6,
"65": 6,
"66": 6,
"67": 6,
"68": 6,
"69": 6,
"70": 6,
"71": 6,
"72": 6,
"73": 6,
"74": 6,
"75": 6,
"76": 6,
"77": 6,
"78": 6,
"79": 6,
"80": 6,
"81": 6,
"82": 7,
"83": 7,
"84": 7,
"85": 7,
"86": 7,
"87": 7,
"88": 7,
"89": 7,
"90": 7,
"91": 7,
"92": 7,
"93": 7,
"94": 7,
"95": 7,
"96": 7,
"97": 8,
"98": 8,
"99": 8,
"100": 8
};

如果真的要按照原本的順序跟值的話其實可以用 fill 來達成

let arr = Array(100)
arr.fill(1,0,1)
arr.fill(2,1,31)
arr.fill(3,31,46)
arr.fill(4,31,50)
arr.fill(5,50,51)
arr.fill(6,51,81)
arr.fill(7,81,96)
arr.fill(8,96,100)

不過其實這邊根本可以不用用hash,直接用array就好,而且其實機率比較小的數字放在array的哪個index被抽中的機率是一樣的,不會因為你放在第50個被抽中的機率就更低,所以可以這樣寫

根據 gift的設定,1有1個,2有30個, 3有15個, 4有4個, 5有1個, 6有30個, 7有15個, 8有4個let arr = Array(100)
arr.fill(1,0,1)
arr.fill(5,1,2)
arr.fill(4,2,6)
arr.fill(8,6,10)
arr.fill(3,10,25)
arr.fill(7,25,40)
arr.fill(2,40,70)
arr.fill(6,70,100)

這邊只有根據現有的code來優化,至於有沒有其他更好的寫法一定有,不過我這邊就沒有要再幫refactor了

會寫這篇主要還是要說幾點

  1. 要認清楚在前端處理資料並不是這麼的安全,所以資料類的東西最好都是到後端處理後再傳回去,除非今天這個轉盤只是做好玩的,或是coupon其實沒上限,不然直接放一個500塊的coupon code出來還比較快
  2. 不要把 javascript 101的練習作業拿來放在 production上,而且還是這麼大的電商,看這樣的網頁真的會擔心你們的電商真的沒問題嗎?
  3. 有人code review 很重要!很重要!很重要!

後記

文章發了以後得到了一些迴響,看起來當初就是以行銷為目的寫的網頁

連91app都在自己的粉絲團自嘲

平常我也只是偶爾幫忙寫寫前端code的小嫩嫩,不過如果這邊文章可以讓剛接觸前端的新手作為一個負面教材,知道在前端處理資料不是這麼安全的話要更警慎的話也不錯,如果也有幫助到91app做行銷的話,那一舉兩得也不錯!!

--

--