
API 安全模式與反模式保護(hù) REST API、webhook 和服務(wù)間通信的參考。在007 audit、007 threat-model和 API 代碼審查時(shí)使用。1. 認(rèn)證模式API 密鑰# 正確API 密鑰在請(qǐng)求頭中Authorization:ApiKey sk-live-abc123def456# 錯(cuò)誤API 密鑰在 URL 中會(huì)記錄在服務(wù)器日志、瀏覽器歷史、referrer 頭中GET /api/data?api_keysk-live-abc123def456# 最佳實(shí)踐api_keys:-使用前綴識(shí)別密鑰sk-live-、sk-test-、pk--哈希存儲(chǔ)SHA-256而非明文-定期輪換最長(zhǎng) 90 天-限定到特定權(quán)限/資源-按密鑰進(jìn)行速率限制-一旦泄露立即撤銷-不同環(huán)境使用不同密鑰開發(fā)/預(yù)發(fā)布/生產(chǎn)OAuth 2.0# 按客戶端類型推薦的流程oauth2_flows:server_to_server:client_credentialsweb_app_with_backend:authorization_code PKCEsingle_page_app:authorization_code PKCE無客戶端密鑰mobile_app:authorization_code PKCENEVER_USE:implicit_grant# 已棄用令牌暴露在 URL 中# 令牌最佳實(shí)踐tokens:access_token_lifetime:15_minutes# 短壽命refresh_token_lifetime:7_days# 使用時(shí)輪換refresh_token_rotation:true# 每次刷新令牌store_tokens:httponly_secure_cookie# 非 localStoragerevocation:implement_revocation_endpointJWT 最佳實(shí)踐# 正確正確的 JWT 配置jwt_config{algorithm:RS256,# 非對(duì)稱而非使用弱密鑰的 HS256expiration:900,# 最長(zhǎng) 15 分鐘issuer:auth.example.com,# 始終驗(yàn)證audience:api.example.com,# 始終驗(yàn)證required_claims:[sub,exp,iat,iss,aud],}# 需要檢測(cè)的錯(cuò)誤模式j(luò)wt_antipatterns[algorithm: none,# 無簽名驗(yàn)證algorithm: HS256,# 使用弱/共享密鑰exp: far_future,# 永不過期的令牌no audience check,# 跨服務(wù)重復(fù)使用令牌secret in code,# 硬編碼的簽名密鑰JWT in URL parameter,# 被記錄、緩存、通過 referrer 泄露]# 關(guān)鍵始終驗(yàn)證defvalidate_jwt(token:str)-dict:returnjwt.decode(token,keyPUBLIC_KEY,# 非弱共享密鑰algorithms[RS256],# 顯式指定不從令牌頭讀取audienceapi.example.com,issuerauth.example.com,options{require:[exp,iat,sub]},)2. 速率限制策略令牌桶# 最適合允許突發(fā)流量同時(shí)維持平均速率classTokenBucket: capacity100, refill_rate10/sec 允許 100 個(gè)請(qǐng)求的突發(fā)然后每秒 10 個(gè)持續(xù)。 def__init__(self,capacity:int,refill_rate:float):self.capacitycapacity self.tokenscapacity self.refill_raterefill_rate self.last_refilltime.time()defallow_request(self)-bool:self._refill()ifself.tokens1:self.tokens-1returnTruereturnFalse滑動(dòng)窗口# 最適合平滑的速率限制無突發(fā)許可# 在時(shí)間窗口中跟蹤請(qǐng)求統(tǒng)計(jì)最近 N 秒內(nèi)的請(qǐng)求數(shù)# Redis 實(shí)現(xiàn)ZADD ZRANGEBYSCORE ZCARD按用戶速率限制rate_limits:unauthenticated:requests_per_minute:20requests_per_hour:100authenticated_free:requests_per_minute:60requests_per_hour:1000authenticated_paid:requests_per_minute:300requests_per_hour:10000# 始終包含響應(yīng)頭headers:X-RateLimit-Limit:60X-RateLimit-Remaining:45X-RateLimit-Reset:1620000060# Unix 時(shí)間戳Retry-After:30# 在 429 響應(yīng)中3. 輸入驗(yàn)證模式驗(yàn)證frompydanticimportBaseModel,Field,validatorclassCreateUserRequest(BaseModel):name:strField(min_length1,max_length100)email:strField(regexr^[a-zA-Z0-9_.-][a-zA-Z0-9-]\.[a-zA-Z0-9-.]$)age:intField(ge13,le150)role:strField(defaultuser)# 如果用戶嘗試設(shè)置admin則忽略validator(role)defrestrict_role(cls,v):ifvnotin(user,viewer):# 僅允許安全角色returnuserreturnvclassConfig:extraforbid# 拒絕未知字段防止批量賦值類型檢查和大小限制validation_rules:string_fields:max_length:10_000# 無無界字符串strip_whitespace:truereject_null_bytes:true# \x00 可能導(dǎo)致問題numeric_fields:define_min_max:true# 始終設(shè)置邊界reject_nan_infinity:true# 可能破壞數(shù)學(xué)運(yùn)算array_fields:max_items:100# 無無界數(shù)組validate_each_item:truefile_uploads:max_size:10MBallowed_types:[image/jpeg,image/png,application/pdf]validate_magic_bytes:true# 不要僅信任 Content-Type 頭scan_for_malware:truequery_parameters:max_page_size:100default_page_size:20max_query_length:5004. Webhook 安全HMAC 簽名驗(yàn)證importhmacimporthashlibimporttimedefverify_webhook(payload:bytes,headers:dict,secret:str)-bool:完整的 webhook 驗(yàn)證簽名 時(shí)間戳。signatureheaders.get(X-Webhook-Signature)timestampheaders.get(X-Webhook-Timestamp)ifnotsignatureornottimestamp:returnFalse# 1. 防止重放攻擊5 分鐘窗口ifabs(time.time()-int(timestamp))300:returnFalse# 2. 計(jì)算預(yù)期簽名signed_payloadf{timestamp}.{payload.decode()}expectedhmac.new(secret.encode(),signed_payload.encode(),hashlib.sha256).hexdigest()# 3. 常量時(shí)間比較防止時(shí)序攻擊returnhmac.compare_digest(fsha256{expected},signature)Webhook 最佳實(shí)踐webhook_security:sending:-使用 HMAC-SHA256 簽名每個(gè)有效載荷-在簽名中包含時(shí)間戳-發(fā)送唯一事件 ID 以實(shí)現(xiàn)冪等性-僅使用 HTTPS-實(shí)施指數(shù)退避重試-定期輪換簽名密鑰receiving:-在任何處理之前驗(yàn)證簽名-拒絕超過 5 分鐘的請(qǐng)求重放保護(hù)-實(shí)施冪等性存儲(chǔ)已處理的事件 ID-快速返回 200異步處理-不要盲目信任有效載荷數(shù)據(jù)驗(yàn)證模式-對(duì)傳入 webhook 進(jìn)行速率限制-記錄所有 webhook 事件以供審計(jì)5. CORS 配置# 危險(xiǎn)允許一切# Access-Control-Allow-Origin: *# Access-Control-Allow-Credentials: true # 對(duì) * 來源無效# 安全顯式白名單CORS_CONFIG{allowed_origins:[https://app.example.com,https://admin.example.com,],allowed_methods:[GET,POST,PUT,DELETE],allowed_headers:[Authorization,Content-Type],allow_credentials:True,max_age:3600,# 預(yù)檢緩存1 小時(shí)expose_headers:[X-RateLimit-Remaining],}# 需要檢測(cè)的反模式cors_antipatterns[Access-Control-Allow-Origin: *,# 過于寬松reflect Origin header as Allow-Origin,# 實(shí)際上等于帶憑證的 *Access-Control-Allow-Origin: null,# 可被利用Allow-Origin without credentials but with auth,# 不一致]6. 安全頭檢查清單# 所有 API 響應(yīng)必需的安全頭security_headers:# 防止 MIME 嗅探X-Content-Type-Options:nosniff# 防止點(diǎn)擊劫持針對(duì) HTML 響應(yīng)X-Frame-Options:DENY# XSS 保護(hù)舊瀏覽器X-XSS-Protection:0# 禁用改用 CSP# HTTPS 強(qiáng)制Strict-Transport-Security:max-age31536000; includeSubDomains; preload# 內(nèi)容安全策略針對(duì) HTML 響應(yīng)Content-Security-Policy:default-src self; script-src self; style-src self# Referrer 策略Referrer-Policy:strict-origin-when-cross-origin# 權(quán)限策略Permissions-Policy:camera(), microphone(), geolocation()# 移除服務(wù)器信息頭Server:REMOVE_THIS_HEADERX-Powered-By:REMOVE_THIS_HEADER# 敏感數(shù)據(jù)的緩存控制Cache-Control:no-store, no-cache, must-revalidate, privatePragma:no-cache7. 常見 API 漏洞BOLA / IDOR對(duì)象級(jí)授權(quán)失效# 有漏洞無所有權(quán)檢查app.get(/api/users/{user_id}/orders)defget_orders(user_id:int):returndb.query(Order).filter(Order.user_iduser_id).all()# 任何已認(rèn)證用戶都可以訪問其他用戶的訂單# 安全強(qiáng)制所有權(quán)檢查app.get(/api/users/{user_id}/orders)defget_orders(user_id:int,current_user:UserDepends(get_current_user)):ifcurrent_user.id!user_idandnotcurrent_user.is_admin:raiseHTTPException(403,禁止訪問)returndb.query(Order).filter(Order.user_iduser_id).all()批量賦值# 有漏洞接受請(qǐng)求中的所有字段app.put(/api/users/{user_id})defupdate_user(user_id:int,data:dict):db.query(User).filter(User.iduser_id).update(data)# 攻擊者發(fā)送 {role: admin, is_verified: true}# 安全可更新字段的顯式白名單classUserUpdateRequest(BaseModel):name:str|NoneNoneemail:str|NoneNone# role 和 is_verified 不包含在內(nèi)app.put(/api/users/{user_id})defupdate_user(user_id:int,data:UserUpdateRequest):db.query(User).filter(User.iduser_id).update(data.dict(exclude_unsetTrue))數(shù)據(jù)過度暴露# 有漏洞返回整個(gè)數(shù)據(jù)庫模型app.get(/api/users/{user_id})defget_user(user_id:int):returndb.query(User).get(user_id).__dict__# 返回id, name, email, password_hash, ssn, internal_notes, ...# 安全顯式響應(yīng)模式classUserResponse(BaseModel):id:intname:stremail:str# 僅公開字段app.get(/api/users/{user_id},response_modelUserResponse)defget_user(user_id:int):returndb.query(User).get(user_id)8. 冪等性模式# 防止重復(fù)處理同一請(qǐng)求# 對(duì)以下操作至關(guān)重要支付、webhook、任何非冪等操作classIdempotencyMiddleware: 客戶端發(fā)送Idempotency-Key: unique-uuid-here 服務(wù)器存儲(chǔ)結(jié)果重試時(shí)返回緩存響應(yīng)。 def__init__(self,cache):self.cachecache# Redis 或類似asyncdefprocess(self,idempotency_key:str,handler):# 1. 檢查是否已處理cachedawaitself.cache.get(fidempotency:{idempotency_key})ifcached:returncached# 返回與首次相同的響應(yīng)# 2. 加鎖防止并發(fā)重復(fù)處理lockawaitself.cache.lock(flock:{idempotency_key},timeout30)ifnotlock:raiseHTTPException(409,請(qǐng)求已在處理中)try:# 3. 處理請(qǐng)求resultawaithandler()# 4. 緩存結(jié)果24 小時(shí) TTLawaitself.cache.set(fidempotency:{idempotency_key},result,ttl86400,)returnresultfinally:awaitlock.release()何時(shí)需要冪等性密鑰require_idempotency_key:-POST /payments-POST /transfers-POST /orders-POST /webhooks/*# 使用事件 ID 作為密鑰-任何非冪等變更操作naturally_idempotent:# 無需密鑰-GET所有-PUT完整替換-DELETE按 ID快速安全審查檢查清單認(rèn)證 [ ] 所有端點(diǎn)需要認(rèn)證除非明確公開 [ ] API 密鑰在請(qǐng)求頭中而非 URL 中 [ ] JWT 使用 RS256 和短有效期 [ ] 公開客戶端使用 OAuth 2.0 PKCE [ ] 實(shí)施了令牌輪換 授權(quán) [ ] 每次數(shù)據(jù)訪問有所有權(quán)檢查BOLA 預(yù)防 [ ] 每個(gè)特權(quán)操作有角色檢查 [ ] 批量賦值保護(hù)顯式字段白名單 [ ] 響應(yīng)模式過濾敏感字段 輸入/輸出 [ ] 所有輸入有模式驗(yàn)證 [ ] 所有字段、數(shù)組和文件有大小限制 [ ] 參數(shù)化查詢無字符串拼接 [ ] 通用錯(cuò)誤消息無堆棧跟蹤 傳輸 [ ] 所有地方使用 HTTPSTLS 1.2 [ ] 設(shè)置了安全頭 [ ] CORS 已顯式配置 [ ] 已啟用 HSTS 運(yùn)維 [ ] 每用戶/IP 的速率限制 [ ] 帶有關(guān)聯(lián) ID 的請(qǐng)求日志 [ ] webhook 簽名已驗(yàn)證 [ ] 變更操作的冪等性密鑰 [ ] 依賴項(xiàng)已掃描 CVE