Você pode proteger os recursos do seu aplicativo que não são do Firebase, como back-ends auto-hospedados, com o App Check. Para fazer isso, você precisará fazer o seguinte:
- Modifique o cliente do seu aplicativo para enviar um token do App Check junto com cada solicitação ao seu back-end, conforme descrito nas páginas para iOS+ , Android e web .
- Modifique seu back-end para exigir um token válido do App Check em cada solicitação, conforme descrito nesta página.
Verificação de token
Para verificar os tokens do App Check em seu back-end, adicione uma lógica aos endpoints da API que faça o seguinte:
Verifique se cada solicitação inclui um token do App Check.
Verifique o token do App Check usando o Admin SDK.
Se a verificação for bem-sucedida, o Admin SDK retornará o token decodificado do App Check. A verificação bem-sucedida indica que o token foi originado de um aplicativo pertencente ao seu projeto do Firebase.
Rejeite qualquer solicitação que falhe em qualquer uma das verificações. Por exemplo:
Node.js
Se você ainda não instalou o SDK Admin do Node.js , faça-o.
Então, usando o middleware Express.js como exemplo:
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.
});
Pitão
Se você ainda não instalou o Python Admin SDK , faça-o.
Em seguida, nos manipuladores de endpoint da API, chame app_check.verify_token()
e rejeite a solicitação se ela falhar. No exemplo a seguir, uma função decorada com @before_request
executa esta tarefa para todas as solicitações:
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.
...
Ir
Se você ainda não instalou o Admin SDK for Go , faça-o.
Em seguida, nos manipuladores de endpoint da API, chame appcheck.Client.VerifyToken()
e rejeite a solicitação se ela falhar. No exemplo a seguir, uma função wrapper adiciona esta lógica aos manipuladores de 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.
}
Outro
Se seu back-end for escrito em outra linguagem, você poderá usar uma biblioteca JWT de uso geral, como a encontrada em jwt.io , para verificar tokens do App Check.
A lógica de verificação do token deve concluir as seguintes etapas:
- Obtenha o conjunto de chave JSON Web (JWK) pública do Firebase App Check no endpoint JWKS do App Check:
https://firebaseappcheck.googleapis.com/v1/jwks
- Verifique a assinatura do token do App Check para garantir que ele seja legítimo.
- Certifique-se de que o cabeçalho do token use o algoritmo RS256.
- Certifique-se de que o cabeçalho do token tenha o tipo JWT.
- Certifique-se de que o token seja emitido pelo Firebase App Check no seu projeto.
- Certifique-se de que o token não expirou.
- Certifique-se de que o público do token corresponda ao seu projeto.
- Opcional : verifique se o assunto do token corresponde ao ID do aplicativo do seu aplicativo.
Os recursos das bibliotecas JWT podem ser diferentes; certifique-se de concluir manualmente todas as etapas não executadas pela biblioteca escolhida.
O exemplo a seguir executa as etapas necessárias em Ruby usando a gema jwt
como uma camada de 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
Proteção de repetição (beta)
Para proteger um endpoint contra ataques de repetição, você pode consumir o token do App Check depois de verificá-lo para que ele possa ser usado apenas uma vez.
O uso da proteção de reprodução adiciona uma viagem de ida e volta de rede à chamada verifyToken()
e, portanto, adiciona latência a qualquer endpoint que a utilize. Por esse motivo, recomendamos que você habilite a proteção de reprodução somente em endpoints particularmente sensíveis.
Para usar a proteção de reprodução, faça o seguinte:
No console do Cloud , conceda o papel "Firebase App Check Token Verifier" à conta de serviço usada para verificar tokens.
- Se você inicializou o SDK Admin com as credenciais da conta de serviço do SDK Admin baixadas do Console do Firebase, a função necessária já foi concedida.
- Se você estiver usando Cloud Functions de primeira geração com a configuração padrão do SDK Admin, conceda o papel à conta de serviço padrão do App Engine . Consulte Alteração das permissões da conta de serviço .
- Se você estiver usando o Cloud Functions de segunda geração com a configuração padrão do SDK Admin, conceda o papel à conta de serviço de computação padrão .
Então, para consumir um token, passe
{ consume: true }
para o métodoverifyToken()
e examine o objeto resultante; se a propriedadealreadyConsumed
fortrue
, rejeite a solicitação ou tome algum tipo de ação corretiva, como exigir que o chamador passe em outras verificações.Por exemplo:
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.
Isso verifica o token e o sinaliza como consumido. Invocações futuras de
verifyToken(appCheckToken, { consume: true })
no mesmo token definirãoalreadyConsumed
comotrue
. (Observe queverifyToken()
não rejeita um token consumido nem verifica se ele foi consumido seconsume
não estiver definido.)
Ao ativar esse recurso para um endpoint específico, você também deve atualizar o código do cliente do aplicativo para adquirir tokens consumíveis de uso limitado para uso com o endpoint. Consulte a documentação do lado do cliente para plataformas Apple , Android e web .