Verificar tokens de verificación de aplicaciones desde un backend personalizado

Puedes proteger los recursos de tu aplicación que no son de Firebase, como los backends autohospedados, con App Check. Para hacerlo, deberá realizar las siguientes dos cosas:

  • Modifique su cliente de aplicación para enviar un token de verificación de aplicación junto con cada solicitud a su backend, como se describe en las páginas para iOS+ , Android y web .
  • Modifique su backend para requerir un token de verificación de aplicación válido con cada solicitud, como se describe en esta página.

Verificación de tokens

Para verificar los tokens de App Check en su backend, agregue lógica a sus puntos finales de API que haga lo siguiente:

  • Verifique que cada solicitud incluya un token de verificación de aplicaciones.

  • Verifique el token de verificación de aplicaciones mediante el SDK de administrador.

    Si la verificación se realiza correctamente, el SDK de administración devuelve el token de verificación de aplicaciones descodificado. La verificación exitosa indica que el token se originó en una aplicación que pertenece a su proyecto de Firebase.

Rechace cualquier solicitud que no pase cualquiera de las comprobaciones. Por ejemplo:

Nodo.js

Si aún no ha instalado el SDK de administración de Node.js , hágalo.

Luego, usando el middleware Express.js como ejemplo:

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

Pitón

Si aún no ha instalado el SDK de administración de Python , hágalo.

Luego, en los controladores de puntos finales de su API, llame a app_check.verify_token() y rechace la solicitud si falla. En el siguiente ejemplo, una función decorada con @before_request realiza esta tarea para todas las solicitudes:

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

Ir

Si aún no ha instalado el SDK de administración para Go , hágalo.

Luego, en los controladores de puntos finales de su API, llame a appcheck.Client.VerifyToken() y rechace la solicitud si falla. En el siguiente ejemplo, una función contenedora agrega esta lógica a los controladores de puntos finales:

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

Otro

Si su backend está escrito en otro idioma, puede usar una biblioteca JWT de uso general, como la que se encuentra en jwt.io , para verificar los tokens de App Check.

La lógica de verificación de su token debe completar los siguientes pasos:

  1. Obtenga la clave web JSON pública (JWK) de Firebase App Check desde el punto final JWKS de App Check: https://firebaseappcheck.googleapis.com/v1/jwks
  2. Verifique la firma del token de App Check para asegurarse de que sea legítima.
  3. Asegúrese de que el encabezado del token utilice el algoritmo RS256.
  4. Asegúrese de que el encabezado del token tenga el tipo JWT.
  5. Asegúrese de que el token lo emita Firebase App Check en su proyecto.
  6. Asegúrese de que el token no haya caducado.
  7. Asegúrese de que la audiencia del token coincida con su proyecto.
  8. Opcional : compruebe que el asunto del token coincida con el ID de aplicación de su aplicación.

Las capacidades de las bibliotecas JWT pueden diferir; asegúrese de completar manualmente cualquier paso que no esté a cargo de la biblioteca que elija.

El siguiente ejemplo realiza los pasos necesarios en Ruby usando la gema jwt como capa de middleware 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

Protección de reproducción (beta)

Para proteger un punto final de ataques de repetición, puede consumir el token de verificación de aplicación después de verificarlo para que pueda usarse solo una vez.

El uso de la protección de reproducción agrega un viaje de ida y vuelta de la red a la llamada verifyToken() y, por lo tanto, agrega latencia a cualquier punto final que la use. Por este motivo, le recomendamos que habilite la protección de reproducción solo en puntos finales particularmente sensibles.

Para utilizar la protección de reproducción, haga lo siguiente:

  1. En la consola de la nube , otorgue la función "Firebase App Check Token Verifier" a la cuenta de servicio utilizada para verificar los tokens.

    • Si inicializó el SDK de administrador con las credenciales de la cuenta de servicio del SDK de administrador que descargó de Firebase console, la función requerida ya está otorgada.
    • Si usas Cloud Functions de primera generación con la configuración predeterminada del SDK de administrador, otorga la función a la cuenta de servicio predeterminada de App Engine . Consulte Cambiar los permisos de la cuenta de servicio .
    • Si utiliza Cloud Functions de segunda generación con la configuración predeterminada del SDK de administrador, otorgue la función a la cuenta de servicio informático predeterminada .
  2. Luego, para consumir un token, pase { consume: true } al método verifyToken() y examine el objeto de resultado; Si la propiedad alreadyConsumed es true , rechace la solicitud o tome algún tipo de acción correctiva, como exigir a la persona que llama que pase otras comprobaciones.

    Por ejemplo:

    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.
    

    Esto verifica el token y luego lo marca como consumido. Las invocaciones futuras de verifyToken(appCheckToken, { consume: true }) en el mismo token establecerán alreadyConsumed en true . (Tenga en cuenta que verifyToken() no rechaza un token consumido ni siquiera verifica si se ha consumido si no se establece consume ).

Cuando habilita esta característica para un punto final en particular, también debe actualizar el código de cliente de su aplicación para adquirir tokens consumibles de uso limitado para usar con el punto final. Consulte los documentos del lado del cliente para plataformas Apple , Android y web .