С помощью App Check вы можете защитить собственные ресурсы бэкэнда вашего приложения, не относящиеся к Google, например, ваш собственный бэкэнд, размещенный на вашем сервере. Для этого вам потребуется выполнить оба следующих действия:
- Измените клиентское приложение так, чтобы оно отправляло токен App Check вместе с каждым запросом на бэкэнд, как описано на страницах для iOS+ , Android , web , Flutter , Unity или C++ .
- Измените настройки вашей серверной части таким образом, чтобы при каждом запросе требовался действительный токен App Check , как описано на этой странице.
Проверка токена
Для проверки токенов App Check на вашем бэкэнде добавьте в конечные точки API следующую логику:
Убедитесь, что каждый запрос содержит токен App Check .
Проверьте токен App Check с помощью Admin SDK.
Если проверка прошла успешно, Admin SDK возвращает декодированный токен App Check . Успешная проверка означает, что токен был получен от приложения, принадлежащего вашему проекту Firebase.
Отклонять любой запрос, не прошедший хотя бы одну из проверок. Например:
Node.js
Если вы еще не установили Node.js Admin SDK , сделайте это.
Затем, используя в качестве примера промежуточное ПО 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
Если вы еще не установили Python Admin SDK , сделайте это.
Затем в обработчиках ваших API-конечных точек вызовите app_check.verify_token() и отклоните запрос, если он не удастся. В следующем примере функция, помеченная аннотацией @before_request выполняет эту задачу для всех запросов:
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.
...
Идти
Если вы еще не установили Admin SDK для Go , сделайте это.
Затем в обработчиках ваших API-конечных точек вызовите appcheck.Client.VerifyToken() и отклоните запрос, если он не удастся. В следующем примере функция-обертка добавляет эту логику в обработчики конечных точек:
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.
}
Другой
Если ваш бэкэнд написан на другом языке, вы можете использовать универсальную библиотеку JWT, например, ту, что находится на jwt.io , для проверки токенов App Check.
Ваша логика проверки токена должна выполнить следующие шаги:
- Получите открытый JSON-ключ (JWK) Firebase App Check, используя конечную точку JWKS App Check:
https://firebaseappcheck.googleapis.com/v1/jwks - Проверьте подпись токена App Check, чтобы убедиться в его подлинности.
- Убедитесь, что в заголовке токена используется алгоритм RS256.
- Убедитесь, что заголовок токена имеет тип JWT.
- Убедитесь, что токен выдан службой Firebase App Check в рамках вашего проекта.
- Убедитесь, что срок действия токена не истек.
- Убедитесь, что целевая аудитория токена соответствует вашему проекту.
- Необязательно : Убедитесь, что тема токена соответствует идентификатору приложения (App ID).
Возможности библиотек JWT могут различаться; обязательно выполните вручную все шаги, которые не предусмотрены выбранной вами библиотекой.
В следующем примере необходимые шаги выполняются на Ruby с использованием гема jwt в качестве промежуточного программного обеспечения 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
Защита от повторного воспроизведения (бета-версия)
Для защиты конечной точки от атак повторного воспроизведения можно использовать токен App Check после его проверки, чтобы он применялся только один раз.
Использование защиты от повторного воспроизведения добавляет сетевой запрос к вызову verifyToken() и, следовательно, увеличивает задержку для любой конечной точки, которая его использует. По этой причине мы рекомендуем включать защиту от повторного воспроизведения только на особо важных конечных точках.
Для использования защиты от повторного воспроизведения выполните следующие действия:
В консоли Cloud предоставьте учетной записи службы, используемой для проверки токенов, роль "Firebase App Check Token Verifier".
- Если вы инициализировали Admin SDK с использованием учетных данных сервисной учетной записи Admin SDK, загруженных из консоли Firebase, необходимая роль уже предоставлена.
- Если вы используете Cloud Functions первого поколения с конфигурацией Admin SDK по умолчанию, предоставьте роль учетной записи службы App Engine по умолчанию . См. раздел «Изменение разрешений учетной записи службы» .
- Если вы используете Cloud Functions второго поколения с конфигурацией Admin SDK по умолчанию, предоставьте роль учетной записи службы вычислений по умолчанию .
Затем, чтобы использовать токен, передайте методу
verifyToken(){ consume: true }и проверьте результирующий объект; если свойствоalreadyConsumedимеет значениеtrue, отклоните запрос или предпримите какие-либо корректирующие действия, например, потребуйте от вызывающей стороны пройти другие проверки.Например:
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.Это проверяет токен, а затем помечает его как использованный. При последующих вызовах
verifyToken(appCheckToken, { consume: true })для того же токена значениеalreadyConsumedбудет установлено вtrue. (Обратите внимание, чтоverifyToken()не отклоняет использованный токен и даже не проверяет, использован ли он, еслиconsumeне установлен.)
При включении этой функции для конкретной конечной точки необходимо также обновить клиентский код приложения, чтобы получить токены ограниченного использования, которые можно использовать с этой конечной точкой. См. документацию по клиентской части для платформ Apple , Android и веб-приложений .