Mit App Check können Sie benutzerdefinierte Backend-Ressourcen, die nicht von Google stammen, für Ihre App schützen, z. B. Ihr eigenes selbst gehostetes Backend. Dazu müssen Sie Folgendes tun:
- Ändern Sie Ihren App-Client so, dass er mit jeder Anfrage an Ihr Backend ein App Check-Token sendet, wie auf den Seiten für iOS+, Android, Web, Flutter, Unity oder C++ beschrieben.
- Ändern Sie Ihr Backend so, dass für jede Anfrage ein gültiges App Check-Token erforderlich ist, wie auf dieser Seite beschrieben.
Tokenbestätigung
Wenn Sie App Check-Tokens in Ihrem Backend prüfen möchten, fügen Sie Ihren API-Endpunkten Logik hinzu, die Folgendes tut:
Prüfen Sie, ob jede Anfrage ein App Check-Token enthält.
Prüfen Sie das App Check-Token mit dem Admin SDK.
Wenn die Überprüfung erfolgreich ist, gibt das Admin SDK das decodierte App Check-Token zurück. Eine erfolgreiche Überprüfung zeigt, dass das Token von einer App stammt, die zu Ihrem Firebase-Projekt gehört.
Lehnen Sie alle Anfragen ab, die eine der beiden Prüfungen nicht bestehen. Beispiel:
Node.js
Falls Sie das Node.js Admin SDK noch nicht installiert haben, tun Sie dies jetzt.
Beispiel mit Express.js-Middleware:
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
Installieren Sie das Python Admin SDK, falls noch nicht geschehen.
Rufen Sie dann in Ihren API-Endpunkt-Handlern app_check.verify_token() auf und lehnen Sie die Anfrage ab, wenn sie fehlschlägt. Im folgenden Beispiel übernimmt eine mit @before_request dekorierte Funktion diese Aufgabe für alle Anfragen:
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
Installieren Sie das Admin SDK für Go, falls noch nicht geschehen.
Rufen Sie dann in Ihren API-Endpunkt-Handlern appcheck.Client.VerifyToken() auf und lehnen Sie die Anfrage ab, wenn sie fehlschlägt. Im folgenden Beispiel fügt eine Wrapper-Funktion diese Logik den Endpunkt-Handlern hinzu:
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.
}
Sonstiges
Wenn Ihr Backend in einer anderen Sprache geschrieben ist, können Sie eine JWT-Bibliothek für allgemeine Zwecke wie die auf jwt.io verwenden, um App Check-Tokens zu überprüfen.
Ihre Token-Bestätigungslogik muss die folgenden Schritte ausführen:
- Rufen Sie den öffentlichen JSON Web Key (JWK) von Firebase App Check vom JWKS-Endpunkt von App Check ab:
https://firebaseappcheck.googleapis.com/v1/jwks - Prüfen Sie die Signatur des App Check-Tokens, um sicherzustellen, dass es legitim ist.
- Achten Sie darauf, dass im Header des Tokens der Algorithmus RS256 verwendet wird.
- Achten Sie darauf, dass der Header des Tokens den Typ JWT hat.
- Achten Sie darauf, dass das Token von Firebase App Check in Ihrem Projekt ausgestellt wird.
- Achten Sie darauf, dass das Token nicht abgelaufen ist.
- Achten Sie darauf, dass die Zielgruppe des Tokens mit Ihrem Projekt übereinstimmt.
- Optional: Prüfen Sie, ob das Subjekt des Tokens mit der App-ID Ihrer App übereinstimmt.
Die Funktionen von JWT-Bibliotheken können variieren. Führen Sie alle Schritte, die nicht von der ausgewählten Bibliothek ausgeführt werden, manuell aus.
Im folgenden Beispiel werden die erforderlichen Schritte in Ruby mit dem jwt-Gem als Rack-Middleware-Ebene ausgeführt.
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
Schutz vor Wiederholungsangriffen (Beta)
Wenn Sie einen Endpunkt vor Replay-Angriffen schützen möchten, können Sie das App Check-Token nach der Überprüfung verbrauchen, sodass es nur einmal verwendet werden kann.
Wenn Sie den Schutz vor Replay-Angriffen verwenden, wird dem Tokenüberprüfungsaufruf ein Netzwerk-Roundtrip hinzugefügt. Dadurch erhöht sich die Latenz für alle Endpunkte, die ihn verwenden. Aus diesem Grund empfehlen wir, den Replay-Schutz nur für besonders sensible Endpunkte zu aktivieren.
So verwenden Sie den Replay-Schutz:
Weisen Sie dem Dienstkonto, das zur Überprüfung von Tokens verwendet wird, in der Cloud Console die Rolle „Firebase App Check Token Verifier“ zu.
- Wenn Sie das Admin SDK mit den Anmeldedaten des Admin SDK-Dienstkontos initialisiert haben, die Sie aus der Firebase Console heruntergeladen haben, ist die erforderliche Rolle bereits zugewiesen.
- Wenn Sie Cloud Functions (1. Generation) mit der Admin SDK-Standardkonfiguration verwenden, weisen Sie die Rolle dem App Engine-Standarddienstkonto zu. Weitere Informationen finden Sie unter Dienstkontoberechtigungen ändern.
- Wenn Sie Cloud Functions (2nd gen) mit der Standardkonfiguration des Admin SDK verwenden, weisen Sie die Rolle dem Compute-Standarddienstkonto zu.
Wenn Sie ein Token verwenden möchten, übergeben Sie
{ consume: true }(Node.js) oderconsume=True(Python) an den Tokenbestätigungsaufruf und prüfen Sie dann das Ergebnisobjekt. Wenn das Token bereits verwendet wurde, lehnen Sie die Anfrage ab oder ergreifen Sie Korrekturmaßnahmen, z. B. indem Sie den Aufrufer auffordern, andere Prüfungen zu bestehen:Node.js
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.Dadurch wird das Token verifiziert und dann als verbraucht gekennzeichnet. Zukünftige Aufrufe von
verifyToken(appCheckToken, { consume: true })für dasselbe Token setzenalreadyConsumedauftrue. (Beachten Sie, dassverifyToken()ein verbrauchtes Token nicht ablehnt oder sogar prüft, ob es verbraucht ist, wennconsumenicht festgelegt ist.)Python
app_check_claims = app_check.verify_token(app_check_token, consume=True) if app_check_claims.get("already_consumed"): return "App Check token already consumed", 401 # If verify_token() succeeds and already_consumed is not set, okay to continue.Dadurch wird das Token verifiziert und dann als verwendet gekennzeichnet. Zukünftige Aufrufe von
app_check.verify_token(app_check_token, consume=True)für dasselbe Token setzenalready_consumedaufTrue. (Beachten Sie, dassapp_check.verify_token()ein verbrauchtes Token nicht ablehnt oder prüft, ob es verbraucht ist, wennconsumenicht festgelegt ist.)
Wenn Sie diese Funktion für einen bestimmten Endpunkt aktivieren, müssen Sie auch den Clientcode Ihrer App aktualisieren, um verbrauchbare Tokens mit eingeschränkter Nutzung für die Verwendung mit dem Endpunkt zu erhalten. Weitere Informationen finden Sie in der clientseitigen Dokumentation für Apple-Plattformen, Android und das Web.