您可以使用 App Check 保護應用的非 Firebase 資源,例如自託管後端。為此,您需要執行以下兩項操作:
- 修改您的應用程序客戶端,將 App Check 令牌與每個請求一起發送到後端,如iOS+ 、 Android和web頁面上所述。
- 修改您的後端,以便每個請求都需要有效的 App Check 令牌,如本頁所述。
令牌驗證
要驗證後端上的 App Check 令牌,請向 API 端點添加執行以下操作的邏輯:
檢查每個請求是否包含應用程序檢查令牌。
使用 Admin SDK 驗證應用程序檢查令牌。
如果驗證成功,Admin SDK 將返回解碼後的 App Check 令牌。驗證成功表示令牌源自屬於您的 Firebase 項目的應用。
拒絕任何未通過任一檢查的請求。例如:
Node.js
如果您尚未安裝Node.js Admin SDK ,請安裝。
然後,以 Express.js 中間件為例:
import express from "express";
import { initializeApp } from "firebase-admin/app";
import { getAppCheck } from "firebase-admin/app-check";
const expressApp = express();
const firebaseApp = initializeApp();
const appCheckVerification = async (req, res, next) => {
const appCheckToken = req.header("X-Firebase-AppCheck");
if (!appCheckToken) {
res.status(401);
return next("Unauthorized");
}
try {
const appCheckClaims = await getAppCheck().verifyToken(appCheckToken);
// If verifyToken() succeeds, continue with the next middleware
// function in the stack.
return next();
} catch (err) {
res.status(401);
return next("Unauthorized");
}
}
expressApp.get("/yourApiEndpoint", [appCheckVerification], (req, res) => {
// Handle request.
});
Python
如果您尚未安裝Python Admin SDK ,請安裝。
然後,在 API 端點處理程序中,調用app_check.verify_token()
並在失敗時拒絕請求。在以下示例中,用@before_request
修飾的函數對所有請求執行此任務:
import firebase_admin
from firebase_admin import app_check
import flask
import jwt
firebase_app = firebase_admin.initialize_app()
flask_app = flask.Flask(__name__)
@flask_app.before_request
def verify_app_check() -> None:
app_check_token = flask.request.headers.get("X-Firebase-AppCheck", default="")
try:
app_check_claims = app_check.verify_token(app_check_token)
# If verify_token() succeeds, okay to continue to route handler.
except (ValueError, jwt.exceptions.DecodeError):
flask.abort(401)
@flask_app.route("/yourApiEndpoint")
def your_api_endpoint(request: flask.Request):
# Handle request.
...
去
如果您尚未安裝Go 的 Admin SDK ,請安裝。
然後,在 API 端點處理程序中,調用appcheck.Client.VerifyToken()
並在失敗時拒絕請求。在以下示例中,包裝函數將此邏輯添加到端點處理程序中:
package main
import (
"context"
"log"
"net/http"
firebaseAdmin "firebase.google.com/go/v4"
"firebase.google.com/go/v4/appcheck"
)
var (
appCheck *appcheck.Client
)
func main() {
app, err := firebaseAdmin.NewApp(context.Background(), nil)
if err != nil {
log.Fatalf("error initializing app: %v\n", err)
}
appCheck, err = app.AppCheck(context.Background())
if err != nil {
log.Fatalf("error initializing app: %v\n", err)
}
http.HandleFunc("/yourApiEndpoint", requireAppCheck(yourApiEndpointHandler))
log.Fatal(http.ListenAndServe(":8080", nil))
}
func requireAppCheck(handler func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
wrappedHandler := func(w http.ResponseWriter, r *http.Request) {
appCheckToken, ok := r.Header[http.CanonicalHeaderKey("X-Firebase-AppCheck")]
if !ok {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("Unauthorized."))
return
}
_, err := appCheck.VerifyToken(appCheckToken[0])
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("Unauthorized."))
return
}
// If VerifyToken() succeeds, continue with the provided handler.
handler(w, r)
}
return wrappedHandler
}
func yourApiEndpointHandler(w http.ResponseWriter, r *http.Request) {
// Handle request.
}
其他
如果您的後端是用另一種語言編寫的,您可以使用通用 JWT 庫(例如jwt.io中的庫)來驗證 App Check 令牌。
您的令牌驗證邏輯必須完成以下步驟:
- 從 App Check JWKS 端點獲取 Firebase App Check 公共 JSON Web 密鑰 (JWK) 集:
https://firebaseappcheck.googleapis.com/v1/jwks
- 驗證 App Check 令牌的簽名以確保其合法。
- 確保令牌的標頭使用算法 RS256。
- 確保令牌的標頭具有 JWT 類型。
- 確保該令牌是由您的項目下的 Firebase App Check 頒發的。
- 確保令牌尚未過期。
- 確保代幣的受眾與您的項目相匹配。
- 可選:檢查令牌的主題是否與您的應用程序的應用程序 ID 匹配。
JWT 庫的功能可能有所不同;請務必手動完成您選擇的庫未處理的任何步驟。
以下示例使用jwt
gem 作為 Rack 中間件層在 Ruby 中執行必要的步驟。
require 'json'
require 'jwt'
require 'net/http'
require 'uri'
class AppCheckVerification
def initialize(app, options = {})
@app = app
@project_number = options[:project_number]
end
def call(env)
app_id = verify(env['HTTP_X_FIREBASE_APPCHECK'])
return [401, { 'Content-Type' => 'text/plain' }, ['Unauthenticated']] unless app_id
env['firebase.app'] = app_id
@app.call(env)
end
def verify(token)
return unless token
# 1. Obtain the Firebase App Check Public Keys
# Note: It is not recommended to hard code these keys as they rotate,
# but you should cache them for up to 6 hours.
uri = URI('https://firebaseappcheck.googleapis.com/v1/jwks')
jwks = JSON(Net::HTTP.get(uri))
# 2. Verify the signature on the App Check token
payload, header = JWT.decode(token, nil, true, jwks: jwks, algorithms: 'RS256')
# 3. Ensure the token's header uses the algorithm RS256
return unless header['alg'] == 'RS256'
# 4. Ensure the token's header has type JWT
return unless header['typ'] == 'JWT'
# 5. Ensure the token is issued by App Check
return unless payload['iss'] == "https://firebaseappcheck.googleapis.com/#{@project_number}"
# 6. Ensure the token is not expired
return unless payload['exp'] > Time.new.to_i
# 7. Ensure the token's audience matches your project
return unless payload['aud'].include? "projects/#{@project_number}"
# 8. The token's subject will be the app ID, you may optionally filter against
# an allow list
payload['sub']
rescue
end
end
class Application
def call(env)
[200, { 'Content-Type' => 'text/plain' }, ["Hello app #{env['firebase.app']}"]]
end
end
use AppCheckVerification, project_number: 1234567890
run Application.new
重放保護(測試版)
為了保護端點免受重放攻擊,您可以在驗證 App Check 令牌後使用它,以便它只能使用一次。
使用重放保護會向verifyToken()
調用添加網絡往返,因此會增加使用它的任何端點的延遲。因此,我們建議您僅在特別敏感的端點上啟用重播保護。
要使用重放保護,請執行以下操作:
在Cloud 控制台中,向用於驗證令牌的服務帳號授予“Firebase App Check Token Verifier”角色。
- 如果您使用從 Firebase 控制台下載的 Admin SDK 服務帳戶憑據初始化了 Admin SDK,則已授予所需的角色。
- 如果您使用具有默認 Admin SDK 配置的第一代 Cloud Functions,請將角色授予App Engine 默認服務帳戶。請參閱更改服務帳戶權限。
- 如果您使用具有默認 Admin SDK 配置的第二代 Cloud Functions,請將角色授予默認計算服務帳戶。
然後,要使用令牌,請將
{ consume: true }
傳遞給verifyToken()
方法並檢查結果對象;如果alreadyConsumed
屬性為true
,則拒絕請求或採取某種糾正措施,例如要求調用者通過其他檢查。例如:
const appCheckClaims = await getAppCheck().verifyToken(appCheckToken, { consume: true }); if (appCheckClaims.alreadyConsumed) { res.status(401); return next('Unauthorized'); } // If verifyToken() succeeds and alreadyConsumed is not set, okay to continue.
這會驗證令牌,然後將其標記為已使用。未來對同一令牌調用
verifyToken(appCheckToken, { consume: true })
將設置alreadyConsumed
為true
。 (請注意,verifyToken()
不會拒絕消耗的令牌,甚至如果未設置消耗,也不會檢查它是否已consume
。)
當您為特定端點啟用此功能時,您還必須更新應用程序客戶端代碼以獲取可消耗的限制使用令牌以與端點一起使用。請參閱Apple 平台、 Android和Web的客戶端文檔。