Özel bir arka uçtan Uygulama Kontrolü jetonlarını doğrulama

Uygulamanızın Firebase dışındaki kaynaklarını (ör. kendi kendine barındırılan arka uçlar) App Check ile koruyabilirsiniz. Bunun için aşağıdakilerin ikisini birden yapmanız gerekir:

  • Uygulama istemcinizi, iOS+, Android ve web sayfalarında açıklandığı gibi arka uçunuza her istekle birlikte bir App Check jetonu gönderecek şekilde değiştirin.
  • Arka uçunuzu, bu sayfada açıklandığı gibi her istek için geçerli bir App Check jetonu gerektirecek şekilde değiştirin.

Jeton doğrulama

Arka uçta App Check jetonlarını doğrulamak için API uç noktalarınıza aşağıdakileri yapan bir mantık ekleyin:

  • Her isteğin bir App Check jetonu içerdiğinden emin olun.

  • Yönetici SDK'sını kullanarak App Check jetonunu doğrulayın.

    Doğrulama başarılı olursa Yönetici SDK'sı, kodu çözülmüş App Check jetonunu döndürür. Doğrulama işleminin başarılı olması, jetonun Firebase projenize ait bir uygulamadan geldiğini gösterir.

Her iki kontrolde de başarısız olan istekler reddedilir. Örneğin:

Node.js

Node.js Admin SDK'yı henüz yüklemediyseniz yükleyin.

Ardından, Express.js ara yazılımını örnek olarak kullanarak:

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

Henüz Python Admin SDK'yı yüklemediyseniz yükleyin.

Ardından, API uç noktası işleyicilerinizde app_check.verify_token() işlevini çağırın ve başarısız olursa isteği reddedin. Aşağıdaki örnekte, @before_request ile süslenmiş bir işlev tüm istekler için bu görevi gerçekleştirir:

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

Henüz Go için Yönetici SDK'sını yüklemediyseniz yükleyin.

Ardından, API uç noktası işleyicilerinizde appcheck.Client.VerifyToken() işlevini çağırın ve başarısız olursa isteği reddedin. Aşağıdaki örnekte, bir sarmalayıcı işlevi uç nokta işleyicilerine aşağıdaki mantığı ekler:

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.
}

Diğer

Arka uçunuz başka bir dilde yazılmışsa App Check jetonlarını doğrulamak için jwt.io adresinde bulunanlar gibi genel amaçlı bir JWT kitaplığı kullanabilirsiniz.

Jeton doğrulama mantığınız aşağıdaki adımları tamamlamalıdır:

  1. AppCheck JWKS uç noktasından Firebase AppCheck herkese açık JSON Web Anahtarı (JWK) kümesini alın: https://firebaseappcheck.googleapis.com/v1/jwks
  2. Uygulama Kontrolü jetonunun imzasını doğrulayarak meşru olduğundan emin olun.
  3. Jetonun başlığında RS256 algoritmasının kullanıldığından emin olun.
  4. Jetonun başlığının JWT türüne sahip olduğundan emin olun.
  5. Jetonun, projeniz kapsamında Firebase Uygulama Kontrolü tarafından yayınlandığından emin olun.
  6. Jetonun geçerlilik süresinin dolmadığından emin olun.
  7. Jetonun kitlesinin projenizle eşleştiğinden emin olun.
  8. İsteğe bağlı: Jetonun öznesinin, uygulamanızın uygulama kimliğiyle eşleştiğinden emin olun.

JWT kitaplıklarının özellikleri farklılık gösterebilir. Seçtiğiniz kitaplık tarafından yönetilmeyen adımları manuel olarak tamamladığınızdan emin olun.

Aşağıdaki örnekte, Rack orta katmanı olarak jwt gem'i kullanarak Ruby'de gerekli adımlar uygulanmaktadır.

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

Tekrar koruması (beta)

Bir uç noktayı yeniden oynatma saldırılarına karşı korumak için App Check jetonunu doğruladıktan sonra yalnızca bir kez kullanılacak şekilde tüketebilirsiniz.

Tekrar oynatma koruması kullanıldığında verifyToken()aramaya bir ağ gidiş dönüş işlemi eklenir ve bu nedenle, bu korumayı kullanan tüm uç noktalara gecikme eklenir. Bu nedenle, yeniden oynatma korumasını yalnızca özellikle hassas uç noktalarda etkinleştirmenizi öneririz.

Tekrar oynatma korumasını kullanmak için aşağıdakileri yapın:

  1. Cloud Console'da, jetonları doğrulamak için kullanılan hizmet hesabına "Firebase App Check Token Verifier" rolünü verin.

    • Admin SDK'yı, Firebase konsolundan indirdiğiniz Admin SDK hizmet hesabı kimlik bilgileriyle başlattıysanız gerekli rol zaten verilmiştir.
    • 1. nesil Cloud Functions'ı varsayılan Yönetici SDK yapılandırmasıyla kullanıyorsanız rolü App Engine varsayılan hizmet hesabına verin. Hizmet hesabı izinlerini değiştirme başlıklı makaleyi inceleyin.
    • 2. nesil Cloud Functions'ı varsayılan Yönetici SDK yapılandırmasıyla kullanıyorsanız rolü Varsayılan bilgi işlem hizmet hesabına verin.
  2. Ardından, jeton kullanmak için { consume: true } değerini verifyToken() yöntemine iletin ve sonuç nesnesini inceleyin; alreadyConsumed mülkü true ise isteği reddedin veya arayandan diğer kontrolleri geçmesini istemek gibi düzeltici bir işlem yapın.

    Örneğin:

    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.
    

    Bu işlem, jetonu doğrular ve ardından jetonu tüketildi olarak işaretler. Aynı jetonda verifyToken(appCheckToken, { consume: true })'in gelecekteki çağrıları alreadyConsumed'yi true olarak ayarlar. (consume ayarlanmamışsa verifyToken()'ün, tüketilen bir jetonu reddetmediğini veya tüketilip tüketilmediğini kontrol etmediğini unutmayın.)

Belirli bir uç nokta için bu özelliği etkinleştirdiğinizde, uç noktayla birlikte kullanılacak sınırlı kullanıma yönelik jetonlar edinmek için uygulama istemci kodunuzu da güncellemeniz gerekir. Apple platformları, Android ve web için istemci tarafı dokümanları inceleyin.