Za pomocą App Check możesz chronić zasoby niestandardowego backendu spoza Google dla swojej aplikacji, np. własny backend hostowany samodzielnie. Aby to zrobić, musisz wykonać obie te czynności:
- Zmodyfikuj klienta aplikacji, aby wysyłał token App Check wraz z każdym żądaniem do backendu, zgodnie z opisem na stronach dotyczących iOS+, Androida, internetu, Fluttera, Unity lub C++.
- Zmodyfikuj backend, aby wymagał prawidłowego tokena App Check w każdym żądaniu, zgodnie z opisem na tej stronie.
Weryfikacja tokena
Aby weryfikować tokeny App Check w backendzie, dodaj do punktów końcowych interfejsu API logikę, która będzie wykonywać te czynności:
Sprawdzaj, czy każde żądanie zawiera token App Check.
Weryfikuj token App Check za pomocą pakietu Admin SDK.
Jeśli weryfikacja się powiedzie, pakiet Admin SDK zwróci zdekodowany App Check token. Pomyślna weryfikacja oznacza, że token pochodzi z aplikacji należącej do Twojego projektu w Firebase.
Odrzucaj wszystkie żądania, które nie przejdą żadnego z tych testów. Przykład:
Node.js
Jeśli nie masz jeszcze zainstalowanego pakietu Admin SDK dla Node.js, zrób to.
Następnie, używając jako przykładu oprogramowania pośredniczącego Express.js:
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
Jeśli nie masz jeszcze zainstalowanego pakietu Admin SDK dla Pythona, zrób to.
Następnie w procedurach obsługi punktów końcowych interfejsu API wywołaj funkcję app_check.verify_token() i odrzuć żądanie, jeśli się nie powiedzie. W tym przykładzie funkcja oznaczona dekoratorem @before_request wykonuje to zadanie w przypadku 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.
...
Go
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 funkcję appcheck.Client.VerifyToken() i odrzuć żądanie, jeśli się nie powiedzie. W tym 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.
}
Inne
Jeśli backend jest napisany w innym języku, do weryfikacji tokenów Sprawdzania aplikacji możesz użyć ogólnej biblioteki JWT, np. takiej, którą znajdziesz na stronie jwt.io.
Logika weryfikacji tokena musi wykonać te czynności:
- Pobierz publiczny zestaw kluczy internetowych JSON (JWK) Sprawdzania aplikacji Firebase z punktu końcowego JWKS Sprawdzania aplikacji:
https://firebaseappcheck.googleapis.com/v1/jwks. - Sprawdź podpis tokena Sprawdzania aplikacji, aby upewnić się, że jest on prawidłowy.
- Upewnij się, że nagłówek tokena używa algorytmu RS256.
- Upewnij się, że nagłówek tokena ma typ JWT.
- Upewnij się, że token został wydany przez Sprawdzanie aplikacji Firebase w Twoim projekcie.
- Upewnij się, że token nie utracił ważności.
- Upewnij się, że odbiorca tokena jest zgodny z Twoim projektem.
- 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 czynności, których nie obsługuje wybrana biblioteka.
Ten przykład wykonuje niezbędne czynności w Ruby za pomocą klejnotu jwt jako warstwy oprogramowania pośredniczącego 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 atakami typu replay (beta)
Aby chronić punkt końcowy przed atakami typu replay, możesz zużyć token Sprawdzania aplikacji po jego zweryfikowaniu, aby można go było użyć tylko raz.
Użycie ochrony przed atakami typu replay powoduje dodanie do wywołania verifyToken() dodatkowej podróży w sieci, a tym samym zwiększa opóźnienie w przypadku każdego punktu końcowego, który z niej korzysta. Z tego powodu zalecamy włączenie ochrony przed atakami typu replay tylko w przypadku szczególnie wrażliwych punktów końcowych.
Aby użyć ochrony przed atakami typu replay:
W konsoli Cloud, przyznaj rolę „Weryfikator tokenów Sprawdzania aplikacji Firebase” kontu usługi używanemu do weryfikacji tokenów.
- Jeśli pakiet Admin SDK został zainicjowany za pomocą danych logowania konta usługi Admin SDK pobranych z konsoli Firebase, wymagana rola jest już przyznana.
- Jeśli używasz Cloud Functions 1 generacji z domyślną konfiguracją pakietu Admin SDK, przyznaj rolę domyślnemu kontu usługi App Engine. Więcej informacji znajdziesz w artykule Zmienianie uprawnień konta usługi.
- Jeśli używasz Cloud Functions 2 generacji z domyślną konfiguracją pakietu Admin SDK, przyznaj rolę domyślnemu kontu usługi Compute.
Aby zużyć token, przekaż
{ consume: true }do metodyverifyToken()i sprawdź obiekt wyniku. Jeśli właściwośćalreadyConsumedma wartośćtrue, odrzuć żądanie lub podejmij działania naprawcze, np. poproś dzwoniącego o przejście innych testów.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 zweryfikowanie tokena, a następnie oznaczenie go jako zużytego. Kolejne wywołania elementu
verifyToken(appCheckToken, { consume: true })w przypadku tego samego tokena spowodują ustawienie wartościalreadyConsumednatrue. (Pamiętaj, że jeśli parametrconsumenie jest ustawiony, funkcjaverifyToken()nie odrzuci zużytego tokena ani nie sprawdzi, czy jest on zużyty).
Gdy włączysz tę funkcję w przypadku określonego punktu końcowego, musisz też zaktualizować kod klienta aplikacji, aby uzyskiwać zużywalne tokeny o ograniczonym użyciu do użytku z tym punktem końcowym. Więcej informacji znajdziesz w dokumentacji po stronie klienta dotyczącej platform Apple, Androida i internetu.