App Check-Tokens über ein benutzerdefiniertes Backend prüfen

Mit App Check können Sie benutzerdefinierte Backend-Ressourcen für Ihre App schützen, die nicht von Google stammen, z. B. Ihr eigenes selbst gehostetes Backend. Dazu müssen Sie beide folgenden Schritte ausführen:

  • Ändern Sie Ihren App-Client so, dass er mit jeder Anfrage an Ihr Backend ein App Check Token sendet. Eine Anleitung dazu finden Sie auf den Seiten für iOS+, Android, Web, Flutter, Unity oder C++.
  • Ändern Sie Ihr Backend so, dass für jede Anfrage ein gültiges App Check Token erforderlich ist, eine Anleitung dazu finden Sie auf dieser Seite.

Token-Bestätigung

Wenn Sie App Check Tokens in Ihrem Backend bestätigen möchten, fügen Sie Ihren API-Endpunkten Logik hinzu , die Folgendes ausführt:

  • Prüfen Sie, ob jede Anfrage ein App Check Token enthält.

  • Bestätigen Sie das App Check Token mit dem Admin SDK.

    Wenn die Bestätigung erfolgreich ist, gibt das Admin SDK das decodierte App Check Token zurück. Eine erfolgreiche Bestätigung bedeutet, dass das Token von einer App stammt, die zu Ihrem Firebase-Projekt gehört.

Lehnen Sie alle Anfragen ab, bei denen eine der beiden Prüfungen fehlschlägt. Beispiel:

Node.js

Installieren Sie das Node.js Admin SDK, falls noch nicht geschehen.

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 den Handlern Ihrer API-Endpunkte 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.
    ...

Go

Installieren Sie das Admin SDK für Go, falls noch nicht geschehen.

Rufen Sie dann in den Handlern Ihrer API-Endpunkte 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 Endpunkthandlern 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 allgemeine JWT-Bibliothek wie jwt.io verwenden, um App Check-Tokens zu bestätigen.

Ihre Token-Bestätigungslogik muss die folgenden Schritte ausführen:

  1. Rufen Sie den öffentlichen JSON-Webschlüsselsatz (JWKS) von Firebase App Check vom App Check-JWKS-Endpunkt ab: https://firebaseappcheck.googleapis.com/v1/jwks
  2. Bestätigen Sie die Signatur des App Check-Tokens, um sicherzustellen, dass es legitim ist.
  3. Prüfen Sie, ob der Header des Tokens den Algorithmus RS256 verwendet.
  4. Prüfen Sie, ob der Header des Tokens den Typ JWT hat.
  5. Prüfen Sie, ob das Token von Firebase App Check unter Ihrem Projekt ausgestellt wurde.
  6. Prüfen Sie, ob das Token abgelaufen ist.
  7. Prüfen Sie, ob die Zielgruppe des Tokens mit Ihrem Projekt übereinstimmt.
  8. Optional: Prüfen Sie, ob das Subjekt des Tokens mit der App-ID Ihrer App übereinstimmt.

Die Funktionen von JWT-Bibliotheken können sich unterscheiden. Führen Sie alle Schritte manuell aus, die von der ausgewählten Bibliothek nicht ausgeführt werden.

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

Wiedergabeschutz (Beta)

Wenn Sie einen Endpunkt vor Wiedergabeangriffen schützen möchten, können Sie das App Check-Token nach der Bestätigung verwenden, damit es nur einmal verwendet werden kann.

Durch die Verwendung des Wiedergabeschutzes wird dem Aufruf von verifyToken() eine Netzwerk-Roundtrip-Zeit hinzugefügt, was zu einer höheren Latenz für alle Endpunkte führt, die ihn verwenden. Aus diesem Grund empfehlen wir, den Wiedergabeschutz nur für besonders sensible Endpunkte zu aktivieren.

So verwenden Sie den Wiedergabeschutz:

  1. Erteilen Sie in der Cloud Console, dem Dienstkonto, das zum Bestätigen von Tokens verwendet wird, die Rolle "Firebase App Check-Token-Bestätiger".

    • Wenn Sie das Admin SDK mit den Anmeldedaten des Admin SDK-Dienstkontos initialisiert haben, die Sie in der Firebase Console heruntergeladen haben, ist die erforderliche Rolle bereits erteilt.
    • Wenn Sie Cloud Functions der 1. Generation mit der Standardkonfiguration des Admin SDK verwenden, erteilen Sie die Rolle dem App Engine-Standarddienstkonto. Weitere Informationen finden Sie unter Dienstkontoberechtigungen ändern.
    • Wenn Sie Cloud Functions der 2. Generation mit der Standardkonfiguration des Admin SDK verwenden, erteilen Sie die Rolle dem Compute-Standarddienstkonto.
  2. Wenn Sie ein Token verwenden möchten, übergeben Sie { consume: true } an die verifyToken() Methode und prüfen Sie das Ergebnisobjekt. Wenn die alreadyConsumed Property den Wert true hat, lehnen Sie die Anfrage ab oder ergreifen Sie Korrekturmaßnahmen, z. B. indem Sie den Aufrufer auffordern, weitere Prüfungen zu bestehen.

    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 bestätigt und dann als verwendet gekennzeichnet. Bei zukünftigen Aufrufen von verifyToken(appCheckToken, { consume: true }) für dasselbe Token wird auf alreadyConsumed gesetzt.true Hinweis: verifyToken() lehnt ein verwendetes Token nicht ab und prüft auch nicht, ob es verwendet wurde, wenn consume nicht festgelegt ist.

Wenn Sie diese Funktion für einen bestimmten Endpunkt aktivieren, müssen Sie auch den App-Clientcode aktualisieren, um verwendbare Tokens mit eingeschränkter Nutzung für die Verwendung mit dem Endpunkt zu erwerben. Weitere Informationen finden Sie in der clientseitigen Dokumentation für Apple-Plattformen, Android und das Web.