Überprüfen Sie App Check-Tokens von einem benutzerdefinierten Backend aus

Mit App Check können Sie die Nicht-Firebase-Ressourcen Ihrer App, z. B. selbstgehostete Backends, schützen. Dazu müssen Sie beide der folgenden Schritte ausführen:

  • Ändern Sie Ihren App-Client so, dass er zusammen mit jeder Anfrage ein App-Check-Token an Ihr Backend sendet, wie auf den Seiten für iOS+ , Android und Web beschrieben.
  • Ändern Sie Ihr Backend so, dass bei jeder Anfrage ein gültiges App Check-Token erforderlich ist, wie auf dieser Seite beschrieben.

Token-Verifizierung

Um App Check-Tokens in Ihrem Backend zu überprüfen, fügen Sie Ihren API-Endpunkten eine Logik hinzu, die Folgendes bewirkt:

  • Stellen Sie sicher, dass jede Anfrage ein App Check-Token enthält.

  • Überprüfen Sie das App Check-Token mit dem Admin SDK.

    Wenn die Überprüfung erfolgreich ist, gibt das Admin SDK das entschlüsselte App Check-Token zurück. Eine erfolgreiche Überprüfung zeigt an, dass das Token von einer App stammt, die zu Ihrem Firebase-Projekt gehört.

Lehnen Sie jede Anfrage ab, die eine der beiden Prüfungen nicht besteht. Zum Beispiel:

Node.js

Wenn Sie das Node.js Admin SDK noch nicht installiert haben, tun Sie dies.

Verwenden Sie dann die Express.js-Middleware als Beispiel:

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

Wenn Sie das Python Admin SDK noch nicht installiert haben, tun Sie dies.

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 führt eine mit @before_request dekorierte Funktion diese Aufgabe für alle Anfragen aus:

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.
    ...

Gehen

Wenn Sie das Admin SDK für Go noch nicht installiert haben, tun Sie dies.

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 zu 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.
}

Andere

Wenn Ihr Backend in einer anderen Sprache geschrieben ist, können Sie eine universelle JWT-Bibliothek verwenden, z. B. eine unter jwt.io , um App Check-Tokens zu überprüfen.

Ihre Token-Verifizierungslogik muss die folgenden Schritte ausführen:

  1. Rufen Sie den öffentlichen JSON Web Key (JWK)-Satz für Firebase App Check vom App Check JWKS-Endpunkt ab: https://firebaseappcheck.googleapis.com/v1/jwks
  2. Überprüfen Sie die Signatur des App Check-Tokens, um sicherzustellen, dass es legitim ist.
  3. Stellen Sie sicher, dass der Header des Tokens den Algorithmus RS256 verwendet.
  4. Stellen Sie sicher, dass der Header des Tokens den Typ JWT hat.
  5. Stellen Sie sicher, dass das Token von Firebase App Check für Ihr Projekt ausgestellt wird.
  6. Stellen Sie sicher, dass das Token nicht abgelaufen ist.
  7. Stellen Sie sicher, dass die Zielgruppe des Tokens mit Ihrem Projekt übereinstimmt.
  8. Optional : Überprüfen Sie, ob der Betreff des Tokens mit der App-ID Ihrer App übereinstimmt.

Die Funktionen von JWT-Bibliotheken können unterschiedlich sein; Stellen Sie sicher, dass Sie alle Schritte manuell ausführen, die nicht von der von Ihnen ausgewählten Bibliothek ausgeführt werden.

Im folgenden Beispiel werden die erforderlichen Schritte in Ruby unter Verwendung des jwt Gems 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

Wiedergabeschutz (Beta)

Um einen Endpunkt vor Replay-Angriffen zu schützen, können Sie das App Check-Token nach der Überprüfung nutzen, sodass es nur einmal verwendet werden kann.

Durch die Verwendung des Wiedergabeschutzes wird ein Netzwerk-Roundtrip zum verifyToken() Aufruf hinzugefügt und somit die Latenz für jeden Endpunkt erhöht, der ihn verwendet. Aus diesem Grund empfehlen wir, den Wiedergabeschutz nur auf besonders sensiblen Endpunkten zu aktivieren.

Um den Wiedergabeschutz zu verwenden, gehen Sie wie folgt vor:

  1. Weisen Sie in der Cloud-Konsole dem Dienstkonto, das zur Überprüfung von Tokens verwendet wird, die Rolle „Firebase App Check Token Verifier“ zu.

    • Wenn Sie das Admin SDK mit den Anmeldeinformationen des Admin SDK-Dienstkontos initialisiert haben, die Sie von der Firebase-Konsole heruntergeladen haben, ist die erforderliche Rolle bereits gewährt.
    • Wenn Sie Cloud Functions der 1. Generation mit der standardmäßigen Admin SDK-Konfiguration verwenden, weisen Sie die Rolle dem App Engine-Standarddienstkonto zu. Siehe Dienstkontoberechtigungen ändern .
    • Wenn Sie Cloud Functions der 2. Generation mit der Standard-Admin-SDK-Konfiguration verwenden, weisen Sie die Rolle dem Standard-Rechendienstkonto zu.
  2. Um dann ein Token zu konsumieren, übergeben Sie { consume: true } an die Methode verifyToken() und untersuchen Sie das Ergebnisobjekt. Wenn die Eigenschaft alreadyConsumed “ den Wert „ true hat, lehnen Sie die Anfrage ab oder ergreifen Sie eine Korrekturmaßnahme, z. B. indem Sie den Aufrufer auffordern, weitere Prüfungen zu bestehen.

    Zum Beispiel:

    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 überprüft und dann als verbraucht gekennzeichnet. Zukünftige Aufrufe von verifyToken(appCheckToken, { consume: true }) für dasselbe Token setzen „ alreadyConsumed auf „ true . (Beachten Sie, dass verifyToken() ein verbrauchtes Token nicht ablehnt und nicht einmal prüft, ob es verbraucht ist, wenn consume nicht festgelegt ist.)

Wenn Sie diese Funktion für einen bestimmten Endpunkt aktivieren, müssen Sie auch Ihren App-Clientcode aktualisieren, um verbrauchbare Token mit begrenzter Nutzung für die Verwendung mit dem Endpunkt zu erwerben. Sehen Sie sich die clientseitigen Dokumente für Apple-Plattformen , Android und Web an.