Вы можете защитить ресурсы вашего приложения, не относящиеся к Firebase, например, собственные серверные части, с помощью проверки приложений. Для этого вам нужно будет выполнить оба следующих действия:
- Измените клиент приложения, чтобы отправлять токен проверки приложения вместе с каждым запросом на серверную часть, как описано на страницах для iOS+ , Android и Интернета .
- Измените серверную часть, чтобы при каждом запросе требовался действительный токен проверки приложения, как описано на этой странице.
Проверка токена
Чтобы проверить токены проверки приложений на серверной части, добавьте в конечные точки API логику, которая выполняет следующие действия:
Убедитесь, что каждый запрос включает токен проверки приложения.
Проверьте токен проверки приложения с помощью Admin SDK.
Если проверка прошла успешно, Admin SDK возвращает декодированный токен проверки приложения. Успешная проверка означает, что токен был получен из приложения, принадлежащего вашему проекту 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 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 , для проверки токенов проверки приложений.
Ваша логика проверки токена должна выполнить следующие шаги:
- Получите открытый набор веб-ключей JSON (JWK) Firebase App Check из конечной точки JWKS App Check:
https://firebaseappcheck.googleapis.com/v1/jwks
. - Проверьте подпись токена проверки приложений, чтобы убедиться, что он является законным.
- Убедитесь, что заголовок токена использует алгоритм RS256.
- Убедитесь, что заголовок токена имеет тип JWT.
- Убедитесь, что токен выдан Firebase App Check для вашего проекта.
- Убедитесь, что срок действия токена не истек.
- Убедитесь, что аудитория токена соответствует вашему проекту.
- Необязательно : убедитесь, что тема токена соответствует идентификатору приложения вашего приложения.
Возможности библиотек 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
Защита от повторного воспроизведения (бета)
Чтобы защитить конечную точку от повторных атак, вы можете использовать токен проверки приложения после его проверки, чтобы его можно было использовать только один раз.
Использование защиты от воспроизведения добавляет к вызову verifyToken()
двустороннюю передачу по сети и, следовательно, увеличивает задержку для любой конечной точки, которая его использует. По этой причине мы рекомендуем включать защиту от воспроизведения только на особо важных конечных точках.
Чтобы использовать защиту от воспроизведения, сначала убедитесь, что роль IAM «Firebase App Check Token Verifier» включена для служебной учетной записи, которую вы используете с Admin SDK. В Cloud Functions это обычно учетная запись службы вычислений по умолчанию; на других платформах это обычно учетная запись службы Admin SDK. Вы можете включить роли IAM в облачной консоли .
Затем, чтобы использовать токен, передайте { consume: true }
методу verifyToken()
и проверьте объект результата; если свойство 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 и Интернета .