從自訂後端驗證 App Check 權杖

您可以保護應用程式的非 Firebase 資源,例如自行管理的後端。 與 App Check 整合如要這麼做,您需要執行這兩項操作:

  • 修改應用程式用戶端,傳送 App Check 權杖和每個要求 如 iOS+Android,以及 網頁
  • 修改後端,在每次要求中要求有效的 App Check 權杖。 按照本頁所述的方式停用

權杖驗證

如要在後端驗證 App Check 權杖,請將邏輯新增至 API 端點 負責執行以下作業:

  • 檢查每個要求是否包含 App Check 權杖。

  • 使用 Admin SDK 驗證 App Check 權杖。

    如果驗證成功,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 for Go, 。

接著,在 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 權杖。

您的權杖驗證邏輯必須完成下列步驟:

  1. 從應用程式取得 Firebase App Check 公開 JSON Web Key (JWK) 集 檢查 JWKS 端點: https://firebaseappcheck.googleapis.com/v1/jwks
  2. 驗證 App Check 權杖的簽章,確保其合法性。
  3. 確認權杖的標頭使用演算法 RS256。
  4. 確認權杖的標頭含有 JWT 類型。
  5. 確認權杖是由 Firebase App Check 核發 專案。
  6. 確認權杖並未過期。
  7. 確認權杖的目標對象與專案相符。
  8. 選用:檢查權杖的主旨是否與應用程式的應用程式 ID 相符。

JWT 程式庫的功能可能有所不同。請務必手動完成 所選程式庫未處理的任何步驟。

以下範例使用 jwt 在 Ruby 中執行必要步驟 gem 做為 Rack 中介軟體層

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

重播防護措施 (Beta 版)

如要防止端點受到重送攻擊,可以使用 App Check 權杖 並確定其只能使用一次。

使用重送防護功能會新增網路往返至 verifyToken() 呼叫,因此會增加使用該端點的端點。因此 建議您只在非常敏感時,才啟用重播保護機制 端點。

如要使用重播防護功能,請執行下列操作:

  1. Cloud 控制台 授予「Firebase App Check 權杖驗證者」授予服務帳戶的角色 以便驗證權杖

    • 如果您使用 Admin SDK 服務帳戶初始化 Admin SDK 憑證,就必須具備必要的角色 。
    • 如果您是搭配預設管理員使用第 1 代 Cloud Functions SDK 設定,將角色授予 App Engine 預設服務 帳戶。請參閱變更服務帳戶權限
    • 如果您是搭配預設管理員使用第 2 代 Cloud Functions SDK 設定,將角色授予預設運算服務 帳戶
  2. 接著,如要取用符記,請將 { 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,以及 網頁