Puoi proteggere le risorse non Firebase della tua app, come i backend self-hosted, con App Check. Per fare ciò, dovrai eseguire entrambe le seguenti operazioni:
- Modifica il client dell'app in modo che invii un token App Check insieme a ogni richiesta al tuo back-end, come descritto nelle pagine per iOS+ , Android e Web .
- Modifica il tuo back-end per richiedere un token App Check valido per ogni richiesta, come descritto in questa pagina.
Verifica token
Per verificare i token App Check sul tuo back-end, aggiungi una logica ai tuoi endpoint API che esegua le seguenti operazioni:
Verifica che ogni richiesta includa un token App Check.
Verifica il token App Check utilizzando Admin SDK.
Se la verifica ha esito positivo, Admin SDK restituisce il token App Check decodificato. Una verifica riuscita indica che il token ha avuto origine da un'app appartenente al tuo progetto Firebase.
Rifiuta qualsiasi richiesta che fallisce entrambi i controlli. Per esempio:
Node.js
Se non hai già installato Node.js Admin SDK , fallo.
Quindi, utilizzando il middleware Express.js come esempio:
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.
});
Pitone
Se non hai già installato Python Admin SDK , fallo.
Quindi, nei gestori dell'endpoint API, chiama app_check.verify_token()
e rifiuta la richiesta se fallisce. Nell'esempio seguente, una funzione decorata con @before_request
esegue questa attività per tutte le richieste:
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.
...
Andare
Se non hai già installato Admin SDK for Go , fallo.
Quindi, nei gestori dell'endpoint API, chiama appcheck.Client.VerifyToken()
e rifiuta la richiesta se fallisce. Nell'esempio seguente, una funzione wrapper aggiunge questa logica ai gestori di endpoint:
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.
}
Altro
Se il tuo backend è scritto in un'altra lingua, puoi utilizzare una libreria JWT generica, come quella trovata su jwt.io , per verificare i token App Check.
La tua logica di verifica del token deve completare i seguenti passaggi:
- Ottieni la chiave Web JSON (JWK) pubblica di Firebase App Check impostata dall'endpoint JWKS di App Check:
https://firebaseappcheck.googleapis.com/v1/jwks
- Verifica la firma del token App Check per assicurarti che sia legittimo.
- Assicurarsi che l'intestazione del token utilizzi l'algoritmo RS256.
- Assicurati che l'intestazione del token sia di tipo JWT.
- Assicurati che il token sia emesso da Firebase App Check nell'ambito del tuo progetto.
- Assicurarsi che il token non sia scaduto.
- Assicurati che il pubblico del token corrisponda al tuo progetto.
- Facoltativo : verifica che l'oggetto del token corrisponda all'ID app della tua app.
Le funzionalità delle librerie JWT possono differire; assicurati di completare manualmente tutti i passaggi non gestiti dalla libreria scelta.
L'esempio seguente esegue i passaggi necessari in Ruby utilizzando jwt
gem come layer 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
Protezione dalla riproduzione (beta)
Per proteggere un endpoint dagli attacchi di riproduzione, puoi utilizzare il token App Check dopo averlo verificato in modo che possa essere utilizzato solo una volta.
L'utilizzo della protezione dalla riproduzione aggiunge un round trip di rete alla chiamata verifyToken()
e quindi aggiunge latenza a qualsiasi endpoint che lo utilizza. Per questo motivo, ti consigliamo di abilitare la protezione dalla riproduzione solo su endpoint particolarmente sensibili.
Per utilizzare la protezione dalla riproduzione, procedi come segue:
Nella console Cloud , concedi il ruolo "Firebase App Check Token Verifier" all'account di servizio utilizzato per verificare i token.
- Se hai inizializzato Admin SDK con le credenziali dell'account di servizio Admin SDK che hai scaricato dalla console Firebase, il ruolo richiesto è già concesso.
- Se utilizzi Cloud Functions di prima generazione con la configurazione SDK Admin predefinita, concedi il ruolo all'account di servizio predefinito di App Engine . Vedere Modifica delle autorizzazioni dell'account di servizio .
- Se utilizzi Cloud Functions di seconda generazione con la configurazione SDK di amministrazione predefinita, concedi il ruolo all'account di servizio di calcolo predefinito .
Quindi, per consumare un token, passa
{ consume: true }
al metodoverifyToken()
ed esamina l'oggetto risultato; se la proprietàalreadyConsumed
ètrue
, rifiuta la richiesta o intraprendi qualche tipo di azione correttiva, come richiedere al chiamante di superare altri controlli.Per esempio:
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.
Questo verifica il token e quindi lo contrassegna come consumato. Invocazioni future di
verifyToken(appCheckToken, { consume: true })
sullo stesso token imposterannoalreadyConsumed
sutrue
. (Si noti cheverifyToken()
non rifiuta un token consumato e nemmeno controlla se è consumato seconsume
non è impostato.)
Quando abiliti questa funzionalità per un determinato endpoint, devi anche aggiornare il codice client dell'app per acquisire token consumabili a utilizzo limitato da utilizzare con l'endpoint. Consulta i documenti lato client per le piattaforme Apple , Android e Web .