نشانه‌های App Check را از یک باطن سفارشی تأیید کنید

می‌توانید با App Check از منابع غیر Firebase برنامه‌تان، مانند backends خود میزبانی‌شده محافظت کنید. برای انجام این کار، باید هر دو مورد زیر را انجام دهید:

  • کلاینت برنامه خود را تغییر دهید تا یک نشانه App Check به همراه هر درخواست به باطن شما ارسال شود، همانطور که در صفحات iOS+ ، Android و وب توضیح داده شده است.
  • همانطور که در این صفحه توضیح داده شده است، باطن خود را طوری تغییر دهید که با هر درخواست، به یک نشانه معتبر App Check نیاز داشته باشید.

تایید رمز

برای تأیید نشانه‌های App Check در باطن خود، منطقی را به نقاط انتهایی API خود اضافه کنید که کارهای زیر را انجام می‌دهد:

  • بررسی کنید که هر درخواست دارای نشانه App Check باشد.

  • با استفاده از Admin SDK، کد App Check تأیید کنید.

    اگر تأیید موفقیت آمیز باشد، 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 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 for Go را نصب نکرده‌اید، این کار را انجام دهید.

سپس، در کنترل‌کننده‌های نقطه پایانی API، با appcheck.Client.VerifyToken() تماس بگیرید و در صورت عدم موفقیت درخواست را رد کنید. در مثال زیر، یک تابع wrapper این منطق را به کنترل کننده های نقطه پایانی اضافه می کند:

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 استفاده کنید.

منطق تأیید رمز شما باید مراحل زیر را انجام دهد:

  1. مجموعه کلید وب JSON عمومی (JWK) بررسی برنامه Firebase را از نقطه پایانی JWKS بررسی برنامه دریافت کنید: https://firebaseappcheck.googleapis.com/v1/jwks
  2. برای اطمینان از قانونی بودن امضای رمز برنامه بررسی کنید.
  3. اطمینان حاصل کنید که هدر توکن از الگوریتم RS256 استفاده می کند.
  4. اطمینان حاصل کنید که هدر توکن دارای نوع JWT باشد.
  5. مطمئن شوید که توکن توسط Firebase App Check تحت پروژه شما صادر شده است.
  6. اطمینان حاصل کنید که توکن منقضی نشده است.
  7. اطمینان حاصل کنید که مخاطبان توکن با پروژه شما مطابقت دارند.
  8. اختیاری : بررسی کنید که موضوع رمز با شناسه برنامه برنامه شما مطابقت داشته باشد.

قابلیت های کتابخانه های JWT می تواند متفاوت باشد. مطمئن شوید که تمام مراحلی که توسط کتابخانه انتخابی شما انجام نشده است را به صورت دستی کامل کنید.

مثال زیر مراحل لازم را در Ruby با استفاده از jwt gem به عنوان لایه میان افزار 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() اضافه می‌کند و بنابراین به هر نقطه پایانی که از آن استفاده می‌کند، تأخیر اضافه می‌کند. به همین دلیل، توصیه می‌کنیم که محافظت از پخش مجدد را فقط در نقاط انتهایی حساس فعال کنید.

برای استفاده از محافظت از پخش مجدد، موارد زیر را انجام دهید:

  1. در کنسول Cloud ، نقش «تأییدکننده نشانه بررسی برنامه Firebase» را به حساب سرویس مورد استفاده برای تأیید نشانه‌ها اعطا کنید.

    • اگر Admin SDK را با اعتبار حساب سرویس Admin SDK که از کنسول Firebase دانلود کرده‌اید مقداردهی کنید، نقش مورد نیاز قبلاً داده شده است.
    • اگر از نسل اول توابع Cloud با پیکربندی پیش‌فرض Admin SDK استفاده می‌کنید، این نقش را به حساب سرویس پیش‌فرض App Engine اختصاص دهید. به تغییر مجوزهای حساب سرویس مراجعه کنید.
    • اگر از نسل دوم توابع Cloud با پیکربندی پیش‌فرض Admin SDK استفاده می‌کنید، این نقش را به حساب خدمات محاسباتی پیش‌فرض اعطا کنید.
  2. سپس برای مصرف یک نشانه، { 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 و وب مراجعه کنید.