您可以使用 App Check 保護應用程式的非 Firebase 資源,例如自行管理的後端。如要這樣做,您必須同時完成下列兩項操作:
符記驗證
如要在後端驗證 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
如果您尚未安裝 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 Key (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
重播防護 (Beta 版)
為保護端點免於遭受重送攻擊,您可以在驗證 App Check 權杖後使用該權杖,這樣權杖就只能使用一次。
使用重播保護功能會為 verifyToken()
呼叫增加網路往返時間,因此會為使用該呼叫的任何端點增加延遲時間。因此,我們建議您只在特別敏感的端點啟用重播保護功能。
如要使用防重播機制,請執行下列操作:
在 Cloud 控制台中,將「Firebase App Check Token Verifier」角色授予用於驗證權杖的服務帳戶。
- 如果您使用從 Firebase 控制台下載的 Admin SDK 服務帳戶憑證,初始化 Admin SDK,則系統已授予必要的角色。
- 如果您使用第 1 代 Cloud Functions 搭配預設的 Admin SDK 設定,請將角色授予 App Engine 預設服務帳戶。請參閱「變更服務帳戶權限」。
- 如果您使用第 2 代 Cloud Functions 搭配預設的 Admin SDK 設定,請將角色授予預設運算服務帳戶。
接著,如要使用權杖,請將
{ 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
。(請注意,如果未設定consume
,verifyToken()
不會拒絕已使用的權杖,甚至不會檢查權杖是否已使用)。
為特定端點啟用這項功能時,您也必須更新應用程式用戶端程式碼,以便取得可消耗的限用權杖,用於與端點搭配使用。請參閱 Apple 平台、Android 和 網頁的用戶端說明文件。