2020 年大多數以位置為基礎的加密貨幣構想之所以悄悄消失,是因為它們信任 GPS。GPS 是任何接收器都能收聽的廣播訊號,而接收器可以被設定成對它聽到的內容說謊。做到這件事的工具在 Amazon 上大約三十美元,而且從 2015 年起就是這個價格。任何一個會因為錢包站在某個座標就付錢、又只用 GPS 驗證這件事的協議,都會把錢賠給坐在沙發上的假錢包。這既不是困難的攻擊,也不是聰明的攻擊。
Proof of location 就是修正這個問題的那一層。這個詞被使用的次數遠多於被定義的次數,所以我們從定義開始。Proof of location 是一項主張,可以由不在場的一方驗證:某個特定裝置在特定時間範圍內,位於某個特定座標。它是關於「到場」的主張,而不是關於座標的主張。大部分的工程工作,就落在這個區別上。
威脅模型。我們防禦的不是國家級對手;那種極端情況留到最後再談。我們防禦的是一般的攻擊者:一支手機、一台筆電、一個下午。這樣的攻擊者可以用現成工具偽造 GPS、在模擬器中執行應用程式,並用一行程式碼建立一個錢包。任何單一的防護措施,都能被這樣的攻擊者突破。工程上的目標,是讓同時突破所有防護的成本,高於投放物的獎勵。
本文以淺白的方式說明 Seek 目前做了什麼,以及接下來會推出什麼。我們刻意少談行銷,多談機制。
第一層,在捕捉當下:裝置認證。每一個捕捉請求都附帶一份由作業系統簽署的聲明。在 iOS 上是 Apple App Attest,在 Android 上是 Google Play Integrity。Seek 在伺服器端驗證兩者。認證用一個簽章說明三件事:這是一台真實的裝置、應用程式的二進位檔沒有被竄改,以及呼叫者持有一把在這台裝置上為我們的應用程式產生的金鑰。已 root 的手機無法通過。模擬器無法通過。跳過位置檢查、重新打包的應用程式也無法通過。認證並不能證明手機在哪裡;它證明的是手機沒有謊稱自己是手機。正因如此,其他訊號才值得參考。
第二層,在捕捉當下:附帶寬限範圍的距離檢查。只有當你位於生成點的收集半徑內,用戶端才會讓你按下捕捉按鈕。伺服器會獨立比對你送出的座標和生成點的座標,並接受最多七十五公尺的距離差。按鈕半徑和伺服器半徑之間的落差是刻意設計的。靠在牆邊的手機,GPS 可能因為不是你造成的原因而飄移幾公尺,在這個誤差範圍內拒絕捕捉會讓人覺得很武斷。超過七十五公尺,請求就會被直接拒絕,這一層不會評分,也沒有申訴的餘地。
第二層(續):頻率限制與每個生成點的上限。兩次收集請求間隔不到五秒,就會被一個以 Postgres 為後端、能跨 serverless 執行個體持續運作的關卡回傳 429。單一生成點最多只接受同一個帳號嘗試兩次,而第二次的價值是第一次的 65%。這些上限不是為了偵測詐欺;它們的用意是讓一個已經突破其他防護層的機器人,挖取這個端點的速度無法超過真人玩遊戲的速度。
第三層,在捕捉當下:環境訊號,只記錄不強制。每次捕捉也會附帶一些旗標:請求是否來自模擬器、Android 是否回報定位來自模擬位置提供者,以及回報的 GPS 精確度(公尺)。這些資料會被記錄,但從不用來拒絕捕捉本身。會在這些資料上說謊的用戶端,正是你最不希望讓它做決定的用戶端,所以決定是在別處做出的。它們在第四層才派上用場。
第四層,在提領時:詐欺關卡。提領會經過一個伺服器函式,讀取該帳號過去九十天的捕捉紀錄。只要其中任何一次捕捉來自模擬器,或 Android 回報了模擬位置提供者,或帳號有兩次成功捕捉間隔不到十秒,或帳號在兩個捕捉座標之間移動的速度超過人類所能及,又或是另一個帳號曾從同一個裝置指紋提領過,這筆提領就會轉交人工審核,而不是自動核准。使用者不會被告知是哪一條警報線被觸發,帳號也會照常遊玩。偵測是一項關於付款的商業決策,而不是懲罰。
把捕捉和提領分開的原因,是它們防禦的東西不同。捕捉層守護的是遊戲。就算某次捕捉是詐欺,它也只會變成永遠不會離開應用程式的遊戲貨幣,不花任何成本。提領層守護的是真金白銀。每一塊離開系統的錢,都要經過對形成這筆餘額的過程所做的九十天審查,而這段期間內只要出現一個可疑訊號,就足以把決定交給真人。兩層結合起來,代表正當玩家在捕捉的那一刻不會遇到任何阻礙,而偷偷混進一次惡意捕捉的攻擊者,永遠看不到那筆錢。
第五層,在捕捉當下:移動軌跡。每支手機都有加速度計和陀螺儀,和 GPS 位於同一顆晶片上。Seek 現在會記錄捕捉前幾秒的短暫移動資料,計算一小組統計數值(加速度幅度變異、旋轉活動、靜止幀數),並把這份摘要隨收集請求一起送出。一支走到生成點的手機,其移動特徵和一支放在桌上、同時執行 GPS 模擬器的手機截然不同。目前這個特徵在捕捉層並不強制執行,原因和環境旗標一樣:經過修改的用戶端可以說謊。它會和模擬器與模擬位置提供者的訊號一起進入九十天的詐欺關卡,而紀錄上一條平坦的移動軌跡,就是另一條會把提領交給人工審核的警報線。
一個實際範例。一名測試者執行 GPS 偽造工具,把座標設定到城市另一頭的 Seek 投放物,然後按下捕捉。距離檢查通過,因為偽造的座標吻合。認證通過,因為應用程式沒有被修改。捕捉成立。兩小時後,同一名測試者又完成三次捕捉,其中一次來自十公里外的另一個座標。四次捕捉的移動軌跡都是平的(手機從來沒動過),而不可能移動的檢查也對那兩個兩小時內不可能步行抵達的座標發出警報。帳號照常遊玩、照常收集,完全不知道有什麼不對勁。當測試者申請提領時,這筆申請會連同不可能移動的旗標和平坦的移動軌跡,一起送到人工審核台上。審核人員拒絕了它。測試者收集到的那四十美元餘額,在應用程式之外永遠一文不值。
經濟層面的框架。重點不在於捕捉本身不可能被偽造,而在於提領關卡會回頭檢視帳號為了累積這筆餘額所做的一切。一個價值兩歐元、受認證與距離檢查保護的投放物,攻擊一次的成本很低,但反覆攻擊毫無意義,因為提領檢查會在模式變成錢之前就把它揪出來。一個價值一萬歐元的投放物,在捕捉層就需要更強的框架,而這正是下面發展藍圖要處理的。
發展藍圖上的項目。室內投放物的信標備援,是下一個要上線的防護措施。對於放在建築物內、GPS 毫無意義的生成點,登錄到該投放物的小型藍牙信標,能讓應用程式完全不依賴衛星定位就證明到場。強制執行認證是第二條路線:目前未經認證的請求會被記錄,但仍然會被接受,這樣我們才能在切換開關、直接拒絕它們之前,先測量正當裝置被誤拒的比率。兩者都會以小而無聊、可驗證的方式,加進目前已在使用的同一套捕捉與提領模式中。
目前仍做得不夠好的地方。在購物中心裡,室內 GPS 會偏差十到十五公尺,伺服器端再怎麼聰明也無法修正;真正的解答是信標,所以它才會在發展藍圖上。次秒級的延遲目前做不到,大概也不會做到,因為詐欺關卡讀取的是歷史紀錄,而不是單一請求。穿戴式裝置的感測器比手機少,所以為手機調校的 proof of location 設計,無法順利移植到眼鏡上。而一個開著一卡車射頻設備的國家級對手,現在可以突破任何手機驗證機制;任何宣稱不然的協議,都沒有對你說實話。我們不會那樣做。
值得明白說出來的設計原則:驗證機制應該誠實面對自己能證明和不能證明的事。把機率分數包裝成絕對的是或否,比用嚴格規則給出絕對的是或否更糟,因為分數會產生一個滑桿,而當參與度目標落後時,任何滑桿都會被推向接受更多主張。我們的捕捉層沒有滑桿。它要嘛通過檢查,要嘛不通過。詐欺層有滑桿,而那個滑桿握在真人手上,而不是寫在程式碼裡。
這就是 Seek 底下讓錨定資產和經位置驗證的空投值得去做的那一層。發布者之所以願意在某個座標放置具有真實價值的代幣,是因為最後得到它的人,確實帶著經過認證的裝置到過那個座標,也因為提領關卡在錢移動之前,會回頭檢視帳號的歷史。白皮書中有完整的規格。本文則是應用程式內部運作的白話版本。
簡單來說:到場不等於座標。單一訊號無法證明到場。Seek 分兩層驗證到場:在捕捉的那一刻,靠認證、距離和移動軌跡;在提領的那一刻,靠九十天的詐欺關卡。唯一仍待完成的室內投放物信標備援,將補上單靠 GPS 無法處理的最後一種座標情境。這就是 proof of location 如今在 Seek 內部真正的樣貌,也是為什麼這一層比它所促成的任何亮眼功能都更重要。


