Za pomocą funkcji Sprawdzanie aplikacji możesz chronić zasoby swojej aplikacji inne niż Firebase, takie jak samodzielnie hostowane backendy. Aby to zrobić, musisz wykonać obie poniższe czynności:
- Zmodyfikuj swojego klienta aplikacji, aby wysyłał token sprawdzania aplikacji wraz z każdym żądaniem do backendu, zgodnie z opisem na stronach dotyczących systemów iOS+ , Android i sieci Web .
- Zmodyfikuj backend, aby wymagał prawidłowego tokena sprawdzania aplikacji przy każdym żądaniu, zgodnie z opisem na tej stronie.
Weryfikacja tokena
Aby zweryfikować tokeny sprawdzania aplikacji w 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 za pomocą pakietu Admin SDK.
Jeśli weryfikacja zakończy się pomyślnie, pakiet Admin SDK zwróci zdekodowany token sprawdzania aplikacji. Pomyślna weryfikacja oznacza, że token pochodzi z aplikacji należącej do Twojego projektu Firebase.
Odrzuć każde żądanie, które nie przejdzie żadnej kontroli. Na przykład:
Node.js
Jeśli nie masz jeszcze zainstalowanego pakietu Node.js Admin SDK , 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 punktów końcowych API wywołaj app_check.verify_token()
i odrzuć żądanie, jeśli się nie powiedzie. 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 nie masz jeszcze zainstalowanego pakietu Admin SDK dla Go , zrób to.
Następnie w procedurach obsługi punktów końcowych interfejsu API wywołaj appcheck.Client.VerifyToken()
i odrzuć żądanie, jeśli się nie powiedzie. W poniższym przykładzie funkcja opakowująca dodaje tę logikę do procedur obsługi punktów końcowych:
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 znaleziona na jwt.io , aby zweryfikować tokeny App Check.
Twoja logika weryfikacji tokena musi wykonać następujące kroki:
- Uzyskaj zestaw Firebase App Check public JSON Web Key (JWK) z punktu końcowego JWKS App Check:
https://firebaseappcheck.googleapis.com/v1/jwks
- Zweryfikuj podpis tokena sprawdzania aplikacji, aby upewnić się, że jest zgodny z prawem.
- Upewnij się, że nagłówek tokenu używa algorytmu RS256.
- Upewnij się, że nagłówek tokenu ma typ JWT.
- Upewnij się, że token został wydany przez Firebase App Check w ramach Twojego projektu.
- Upewnij się, że token nie wygasł.
- Upewnij się, że odbiorcy tokena odpowiadają Twojemu projektowi.
- Opcjonalnie : sprawdź, czy temat tokena jest zgodny z identyfikatorem aplikacji.
Możliwości bibliotek JWT mogą się różnić; pamiętaj, aby ręcznie wykonać wszystkie kroki, których nie obsługuje wybrana biblioteka.
Poniższy przykład wykonuje niezbędne kroki w języku 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 powtórek (beta)
Aby chronić punkt końcowy przed atakami powtórkowymi, możesz użyć tokenu sprawdzania aplikacji po jego zweryfikowaniu, aby można go było użyć tylko raz.
Korzystanie z ochrony przed powtórkami dodaje podróż w obie strony do wywołania verifyToken()
, a tym samym dodaje opóźnienie do każdego punktu końcowego, który z niej korzysta. Z tego powodu zalecamy włączenie ochrony przed odtwarzaniem tylko na szczególnie wrażliwych punktach końcowych.
Aby użyć ochrony przed powtórkami, wykonaj następujące czynności:
W konsoli Cloud nadaj kontu usługi używanemu do weryfikowania tokenów rolę „Firebase App Check Token Verifier”.
- Jeśli pakiet Admin SDK został zainicjowany 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 1. generacji z domyślną konfiguracją pakietu Admin SDK, nadaj 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, nadaj rolę domyślnemu kontu usługi obliczeniowej .
Następnie, aby skonsumować token, przekaż
{ consume: true }
do metodyverifyToken()
i sprawdź obiekt wynikowy; jeśli właściwośćalreadyConsumed
ma wartośćtrue
, odrzuć żądanie lub podejmij jakieś działanie naprawcze, takie jak wymaganie od wywołującego 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.
To weryfikuje token, a następnie oznacza go jako wykorzystany. Przyszłe wywołania
verifyToken(appCheckToken, { consume: true })
na tym samym tokenie ustawiąalreadyConsumed
natrue
. (Należy zauważyć, żeverifyToken()
nie odrzuca zużytego tokena ani nawet nie sprawdza, czy jest on zużyty, jeśliconsume
nie jest ustawiona.)
Po włączeniu tej funkcji dla określonego punktu końcowego należy również zaktualizować kod klienta aplikacji, aby uzyskać zużywalne tokeny o ograniczonym użyciu do użytku z punktem końcowym. Zapoznaj się z dokumentacją po stronie klienta dla platform Apple , Android i sieci Web .