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

Uygulamanız için Google dışı özel arka uç kaynaklarını (ör. kendi kendine barındırılan arka ucunuz) korumak üzere App Check kullanabilirsiniz. Bunun için aşağıdakilerin ikisini de yapmanız gerekir:

  • Uygulama istemcinizi, arka uçunuza her istekle birlikte bir App Check jetonu gönderecek şekilde değiştirin. Bu işlem, iOS+, Android, web, Flutter, Unity veya C++ sayfalarında açıklanmıştır.
  • Arka ucunuzu, bu sayfada açıklandığı gibi her istekte 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ğıdaki işlemleri yapan bir mantık ekleyin:

  • Her isteğin bir App Check jetonu içerdiğini kontrol edin.

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

    Doğrulama başarılı olursa Admin SDK, kodu çözülmüş App Check jetonunu döndürür. Başarılı doğrulama, jetonun Firebase projenize ait bir uygulamadan geldiğini gösterir.

Bu kontrollerden herhangi birinde başarısız olan istekleri reddedin. Örneğin:

Node.js

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

Ardından, örnek olarak Express.js ara yazılımını 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 yüklemediyseniz Python Admin SDK'yı 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, bu görevi tüm istekler için 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 yüklemediyseniz Go için Admin SDK'yı yükleyin.

Ardından, API uç nokta 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şlev, bu mantığı uç nokta işleyicilerine 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. Firebase App Check JWKS uç noktasından Firebase App Check genel JSON Web Anahtarı (JWK) kümesini alın: https://firebaseappcheck.googleapis.com/v1/jwks
  2. Meşru olduğundan emin olmak için Uygulama Kontrolü jetonunun imzasını doğrulayın.
  3. Jetonun başlığında RS256 algoritmasının kullanıldığından emin olun.
  4. Jetonun başlığının JWT türünde olduğundan emin olun.
  5. Jetonun, projeniz kapsamında Firebase Uygulama Kontrolü tarafından verildiğinden emin olun.
  6. Jetonun geçerlilik süresinin dolmadığından emin olun.
  7. Jetonun hedef kitlesinin projenizle eşleştiğinden emin olun.
  8. İsteğe bağlı: Jetonun konusunun uygulamanızın uygulama kimliğiyle eşleştiğini kontrol edin.

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

Aşağıdaki örnekte, jwt gem'i bir Rack ara katman yazılımı katmanı olarak kullanarak Ruby'de gerekli adımlar gerçekleştirilir.

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ı tekrar oynatma saldırılarına karşı korumak için, doğruladıktan sonra App Check jetonunu yalnızca bir kez kullanılacak şekilde tüketebilirsiniz.

Yeniden oynatma koruması kullanmak, verifyToken() görüşmesine bir ağ gidiş dönüşü ekler ve bu nedenle, bunu kullanan tüm uç noktalara gecikme ekler. Bu nedenle, yeniden oynatma korumasını yalnızca özellikle hassas uç noktalarda etkinleştirmenizi öneririz.

Yeniden 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 Jeton Doğrulayıcı" 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.
    • Varsayılan Admin SDK yapılandırmasıyla 1. nesil Cloud Functions kullanıyorsanız rolü App Engine varsayılan hizmet hesabına verin. Hizmet hesabı izinlerini değiştirme başlıklı makaleyi inceleyin.
    • Varsayılan Admin SDK yapılandırmasıyla 2. nesil Cloud Functions kullanıyorsanız rolü varsayılan işlem hizmeti hesabına verin.
  2. Ardından, jetonu kullanmak için { consume: true } değerini verifyToken() yöntemine iletin ve sonuç nesnesini inceleyin. alreadyConsumed özelliği true ise isteği reddedin veya arayanın başka kontrolleri geçmesini zorunlu kılmak gibi bir düzeltici 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ğrulayıp kullanılmış olarak işaretler. Aynı jetonla gelecekteki verifyToken(appCheckToken, { consume: true }) çağrıları, alreadyConsumed değerini true olarak ayarlar. (verifyToken() ayarlanmamışsa verifyToken()'nın kullanılan bir jetonu reddetmediğini veya kullanılıp kullanılmadığını kontrol etmediğini unutmayın.)consume

Bu özelliği belirli bir uç nokta için etkinleştirdiğinizde, uç nokta ile kullanılmak üzere tüketilebilir sınırlı kullanım jetonları almak için uygulama istemci kodunuzu da güncellemeniz gerekir. Apple platformları, Android ve web için istemci tarafı dokümanlarına bakın.