Zweryfikuj tokeny App Check z niestandardowego zaplecza

Za pomocą Sprawdzania aplikacji możesz chronić zasoby swojej aplikacji inne niż Firebase, takie jak własne backendy. Aby to zrobić, musisz wykonać obie poniższe czynności:

  • Zmodyfikuj klienta aplikacji, aby wysyłał token sprawdzania aplikacji wraz z każdym żądaniem do backendu, zgodnie z opisem na stronach dla systemów iOS+ , Android i sieci Web .
  • Zmodyfikuj swój backend tak, aby wymagał ważnego tokena sprawdzania aplikacji przy każdym żądaniu, zgodnie z opisem na tej stronie.

Weryfikacja tokena

Aby zweryfikować tokeny Sprawdzania aplikacji na swoim backendzie, dodaj logikę do punktów końcowych interfejsu API, która wykonuje następujące czynności:

  • Sprawdź, czy każde żądanie zawiera token sprawdzania aplikacji.

  • Zweryfikuj token sprawdzania aplikacji przy użyciu pakietu Admin SDK.

    Jeśli weryfikacja zakończy się pomyślnie, pakiet Admin SDK zwróci zdekodowany token sprawdzania aplikacji. Pomyślna weryfikacja wskazuje, że token pochodzi z aplikacji należącej do Twojego projektu Firebase.

Odrzuć każde żądanie, które nie przejdzie żadnego testu. Na przykład:

Node.js

Jeśli jeszcze nie zainstalowałeś zestawu SDK administratora Node.js , zrób to.

Następnie, używając oprogramowania pośredniego Express.js jako przykładu:

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.
});

Pyton

Jeśli jeszcze nie zainstalowałeś pakietu Python Admin SDK , zrób to.

Następnie w procedurach obsługi punktu końcowego interfejsu API wywołaj app_check.verify_token() i odrzuć żądanie, jeśli zakończy się niepowodzeniem. W poniższym przykładzie funkcja ozdobiona @before_request wykonuje to zadanie dla wszystkich żądań:

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

Iść

Jeśli jeszcze nie zainstalowałeś pakietu Admin SDK dla Go , zrób to.

Następnie w procedurach obsługi punktu końcowego interfejsu API wywołaj appcheck.Client.VerifyToken() i odrzuć żądanie, jeśli zakończy się niepowodzeniem. W poniższym przykładzie funkcja opakowująca dodaje następującą logikę do procedur obsługi punktu końcowego:

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

Inny

Jeśli Twój backend jest napisany w innym języku, możesz użyć biblioteki JWT ogólnego przeznaczenia, takiej jak ta znajdująca się na jwt.io , aby zweryfikować tokeny sprawdzania aplikacji.

Logika weryfikacji tokenu musi wykonać następujące kroki:

  1. Uzyskaj zestaw publicznego klucza internetowego JSON (JWK) Firebase App Check z punktu końcowego JWKS App Check: https://firebaseappcheck.googleapis.com/v1/jwks
  2. Sprawdź podpis tokena sprawdzania aplikacji, aby upewnić się, że jest on prawidłowy.
  3. Upewnij się, że nagłówek tokena korzysta z algorytmu RS256.
  4. Upewnij się, że nagłówek tokenu ma typ JWT.
  5. Upewnij się, że token został wystawiony przez Firebase App Check w ramach Twojego projektu.
  6. Upewnij się, że token nie wygasł.
  7. Upewnij się, że odbiorcy tokena odpowiadają Twojemu projektowi.
  8. Opcjonalnie : sprawdź, czy temat tokena odpowiada identyfikatorowi aplikacji Twojej aplikacji.

Możliwości bibliotek JWT mogą się różnić; pamiętaj o ręcznym wykonaniu wszystkich kroków, których nie obsługuje wybrana biblioteka.

Poniższy przykład wykonuje niezbędne kroki w Ruby, używając klejnotu jwt jako warstwy oprogramowania pośredniego 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

Ochrona przed powtórkami (beta)

Aby chronić punkt końcowy przed atakami polegającymi na powtarzaniu, możesz wykorzystać token Sprawdzania aplikacji po jego zweryfikowaniu, aby można go było użyć tylko raz.

Korzystanie z ochrony przed powtarzaniem dodaje objazd sieciowy do wywołania verifyToken() i w związku z tym zwiększa opóźnienie dla każdego punktu końcowego, który z niej korzysta. Z tego powodu zalecamy włączenie ochrony przed ponownym odtwarzaniem tylko na szczególnie wrażliwych punktach końcowych.

Aby skorzystać z ochrony przed ponownym odtwarzaniem, wykonaj następujące czynności:

  1. W konsoli Cloud nadaj rolę „Weryfikator tokenu sprawdzania aplikacji Firebase” kontu usługi używanemu do weryfikacji tokenów.

    • Jeśli zainicjowałeś pakiet Admin SDK przy użyciu poświadczeń konta usługi Admin SDK pobranych z konsoli Firebase, wymagana rola została już przyznana.
    • Jeśli używasz Cloud Functions pierwszej generacji z domyślną konfiguracją pakietu Admin SDK, przypisz rolę domyślnemu kontu usługi App Engine . Zobacz Zmiana uprawnień konta usługi .
    • Jeśli używasz Cloud Functions drugiej generacji z domyślną konfiguracją pakietu Admin SDK, przypisz rolę domyślnemu kontu usługi obliczeniowej .
  2. Następnie, aby wykorzystać token, przekaż { consume: true } do metody verifyToken() i sprawdź obiekt wynikowy; jeśli właściwość alreadyConsumed ma wartość true , odrzuć żądanie lub podejmij jakąś akcję naprawczą, taką jak wymaganie od osoby wywołującej przejścia innych kontroli.

    Na przykład:

    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.
    

    Spowoduje to weryfikację tokenu, a następnie oznaczanie go jako zużytego. Przyszłe wywołania verifyToken(appCheckToken, { consume: true }) na tym samym tokenie zostaną ustawione alreadyConsumed na true . (Zauważ, że verifyToken() nie odrzuca zużytego tokena ani nawet nie sprawdza, czy jest on zużyty, jeśli nie ustawiono consume .)

Po włączeniu tej funkcji dla określonego punktu końcowego należy również zaktualizować kod klienta aplikacji, aby uzyskać tokeny o ograniczonym użyciu, które można używać z punktem końcowym. Zobacz dokumentację po stronie klienta dla platform Apple , Android i Internetu .