第九章:後端安全性 - 如何在 FastAPI 驗證 LIFF Token 防止偽造?

在前面的前端課程中,我們教了 React 如何拿到 liff.getAccessToken(),並附在打卡 API 請求的 Headers 裡傳給後端。 但是,如果你是負責開發 FastAPI 的後端工程師,你怎麼知道這個 Token 不是駭客隨便用小畫家... 不對,用 Postman 捏造的一串亂碼?

在 Web 開發的世界裡有一句名言:「永遠不要相信前端傳來的任何資料。」 如果你不驗證,這將是整個打卡系統最致命、最容易被攻擊的弱點!今天我們要在 FastAPI 築起一道銅牆鐵壁,把這個漏洞徹底補起來。


🛡️ 1. 為什麼一定要驗證 Token?(慘痛的商業代價)

如果你的打卡 API (POST /api/punch) 沒有驗證 Token,駭客或是一個略懂程式的員工,只要知道你的 API 網址,他就可以自己寫個腳本:送出一個假的 JSON { "action": "check-in", "user_id": "U123456..." } 到你的伺服器。

結果就是:他可以每天躺在峇里島的沙灘上,讓腳本每天早上 9 點準時幫他打卡上班,而且系統完全無法察覺。老闆要是發現了,絕對會扣留你的尾款並把你告上法院。

因此,當後端收到這把名為 Token 的鑰匙時,我們必須做一件最重要的事: 拿著這把鑰匙,送到 Line 的官方驗證中心去核對:「請問這把鑰匙真的是你們官方發出去的嗎?它是發給哪一個真實的 Line ID?它有沒有過期?」


🧱 2. 在 FastAPI 實作強大的 Token 驗證機制

我們會利用 FastAPI 最強大的功能:Dependency Injection (依賴注入),來寫一個類似「門口警衛」的攔截器。任何想要打卡的人,都必須先通過警衛的盤查。

🔥【Vibe Prompt 實戰咒語】 我正在用 FastAPI 開發 Line 打卡後端。前端會透過 Header: 'Authorization: Bearer <LIFF_TOKEN>' 傳送憑證。 請幫我寫一個 Dependency (依賴函數) verify_liff_token: 1. 從 Header 提取 Token,如果沒有就拋出 401。 2. 使用 httpx (非同步) 去呼叫 Line 的驗證 API (https://api.line.me/oauth2/v2.1/verify)。 3. 確認 response 的 client_id 是我專案的 LINE_LOGIN_CHANNEL_ID (防範跨站攻擊)。 4. 若驗證成功,再去呼叫 Line Profile API (https://api.line.me/v2/profile) 取得真實的 userId 並 return。 5. 請將這個 Dependency 注入到一支測試用的 POST /api/punch 路由中。

AI 產出的高品質程式碼會長這樣:

import httpx
from fastapi import FastAPI, Depends, HTTPException, Header
import os

app = FastAPI()
LINE_LOGIN_CHANNEL_ID = os.getenv("LINE_LOGIN_CHANNEL_ID")

# 警衛室:負責驗證所有 Token
async def verify_liff_token(authorization: str = Header(None)):
    if not authorization or not authorization.startswith("Bearer "):
        raise HTTPException(status_code=401, detail="警衛:你沒帶識別證!")
    
    token = authorization.split(" ")[1]
    
    # 步驟一:向 Line 官方伺服器驗證這把鑰匙的真偽
    async with httpx.AsyncClient() as client:
        verify_res = await client.get(f"https://api.line.me/oauth2/v2.1/verify?access_token={token}")
        
        if verify_res.status_code != 200:
            raise HTTPException(status_code=401, detail="警衛:這把鑰匙是假的或已過期!")
            
        data = verify_res.json()
        
        # 步驟二:防範跨站攻擊 (非常重要!)
        # 確保這個 Token 是發給你這個打卡系統的,而不是駭客從別的遊戲 APP 偷來的
        if data.get("client_id") != LINE_LOGIN_CHANNEL_ID:
            raise HTTPException(status_code=403, detail="警衛:這鑰匙是別棟大樓的!")
            
        # 步驟三:拿這把真鑰匙去換取該員工的「真實身份證 (Line ID)」
        profile_res = await client.get(
            "https://api.line.me/v2/profile", 
            headers={"Authorization": f"Bearer {token}"}
        )
        
        profile_data = profile_res.json()
        
        # 將官方認證、無法偽造的 Line ID 交給後面的打卡程式
        return profile_data["userId"]

# 保護你的打卡金庫
@app.post("/api/punch")
async def employee_punch(line_user_id: str = Depends(verify_liff_token)):
    # 💡 只要程式碼能執行到這裡,代表這個人 100% 是一個通過 Line 官方認證的真實帳號!
    # line_user_id 絕對無法偽造!你可以放心地用它去資料庫打卡。
    
    # ... 在這裡執行寫入 Supabase 資料庫的邏輯 ...
    
    return {
        "status": "success", 
        "message": f"User {line_user_id} punched in successfully."
    }

📍 3. [商業進階] 結合 GPS 實作雙重防偽 (Geofencing)

有了上面的 Token 驗證保護,已經能防止 99% 的駭客腳本攻擊。 但我們在商場上,還會遇到另一種「聰明的員工」: 他不是駭客,但他每天早上 8:50 躺在家裡的床上,打開 Line 的圖文選單點擊「上班打卡」。他的 Token 是真的,所以你的後端讓他通過了。這該怎麼防?

這時候,就必須結合我們在第一階段學過的 Geolocation API,實作**「電子圍籬 (Geofencing)」**!

  1. 在前端 (React LIFF) 送出請求時,強制要求取得手機的 latitudelongitude,並將這兩個數字一併裝在 JSON 裡送給後端。
  2. 在後端的打卡邏輯中,先將這組 GPS 座標與「公司的經緯度座標」進行比對。
  3. 利用數學的 Haversine 公式計算直線距離。如果算出來距離超過 100 公尺,後端就無情地拋出錯誤:「拒絕打卡:您目前距離公司 5.2 公里,請抵達辦公室後再試」。

這,就是結合 Line 驗證生態系與 GPS 定位,做出來的「絕對防弊企業級 SaaS 系統」。 光是憑藉著「防止代打卡、防止遠端打卡」這兩個賣點,這套系統在外面接案報價就是 10 萬台幣起跳! 下一章,我們將解鎖後端工程師的最後一項超能力:自動化排程 (Cron Jobs)。

解鎖完整教學內容

本章為付費內容。加入專案即可解鎖超過 5000 字的深度解析,包含 10 個以上神級 Prompt 與真實 Source Code 範例!