Skip to content

Commit 77b8943

Browse files
authored
fix: 修正禮物發放端點可被 GET 觸發與數量未驗證漏洞 (ZD-2026-00801) (#500)
/api/send/<target_user_id> 原本沒有指定 methods,Flask 預設為 GET, 配合 SameSite=Lax 的 session cookie 在跨站頂層導航(連結、img、跳轉) 仍會被帶上,攻擊者可誘騙已登入的禮物管理員點擊惡意連結觸發發放。 同時 count 沒有上下限檢查,可送出超大值或負數直接改動 point/ticket 餘額。 - 路由改為 methods=["POST"],跨站 POST 不會帶上 SameSite=Lax cookie - count 限制在 1~100000 之間,比照 Discord 斜線指令 (cog/admin_gift.py) 拒絕 count <= 0 的邏輯
1 parent 1983e6b commit 77b8943

1 file changed

Lines changed: 17 additions & 2 deletions

File tree

app.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
# How long an OAuth "state" nonce (see /login and /callback) stays valid.
3737
OAUTH_STATE_MAX_AGE_SECONDS = 300
3838

39+
# Upper bound for a single /api/send gift, mirroring the Discord slash-command
40+
# gift path (cog/admin_gift.py) rejecting non-positive counts.
41+
GIFT_AMOUNT_MAX = 100000
42+
3943
discord_client_id = os.getenv("DISCORD_CLIENT_ID")
4044
discord_client_secret = os.getenv("DISCORD_CLIENT_SECRET")
4145
discord_redirect_uri = os.getenv("DISCORD_REDIRECT_URI")
@@ -151,8 +155,11 @@ def listt():
151155
return response.json()
152156

153157

154-
@app.route("/api/send/<int:target_user_id>")
155-
# api/send/{recipient}?gift_type={電電點|抽獎券}count={count}
158+
@app.route("/api/send/<int:target_user_id>", methods=["POST"])
159+
# POST api/send/{recipient}?gift_type={電電點|抽獎券}count={count}
160+
# POST-only so a cross-site top-level navigation (link/img/redirect) can't
161+
# trigger this as a simple GET request; SameSite=Lax still blocks the cookie
162+
# on cross-site POSTs.
156163
def send(target_user_id):
157164
if not flask.session:
158165
return flask.jsonify({"result": "you must login", "status": 403})
@@ -187,6 +194,14 @@ def send(target_user_id):
187194
gift_amount = int(gift_amount) # 確保 count 是整數
188195
except ValueError:
189196
return flask.jsonify({"result": "Invalid count value", "status": 400})
197+
# 不能發送 0 以下或超過上限的數量,跟 Discord 斜線指令的驗證保持一致
198+
if not 0 < gift_amount <= GIFT_AMOUNT_MAX:
199+
return flask.jsonify(
200+
{
201+
"result": f"count must be between 1 and {GIFT_AMOUNT_MAX}",
202+
"status": 400,
203+
}
204+
)
190205
# 確保目標用戶存在
191206
user_data = discord_api.get_user(target_user_id)
192207
if "error" in user_data:

0 commit comments

Comments
 (0)