التحقق من الرموز المميزة للتحقق من التطبيق من واجهة خلفية مخصصة

يمكنك حماية موارد تطبيقك غير التابعة لـ Firebase، مثل الواجهات الخلفية ذاتية الاستضافة، من خلال فحص التطبيق. للقيام بذلك، سوف تحتاج إلى القيام بكلا الإجراءين التاليين:

  • قم بتعديل عميل تطبيقك لإرسال رمز التحقق من التطبيق مع كل طلب إلى الواجهة الخلفية لديك، كما هو موضح في صفحات iOS+ و Android و web .
  • قم بتعديل الواجهة الخلفية لديك لتتطلب رمز التحقق من التطبيق صالحًا مع كل طلب، كما هو موضح في هذه الصفحة.

التحقق من الرمز المميز

للتحقق من الرموز المميزة لفحص التطبيق على الواجهة الخلفية لديك، أضف منطقًا إلى نقاط نهاية واجهة برمجة التطبيقات الخاصة بك التي تقوم بما يلي:

  • تأكد من أن كل طلب يتضمن رمز التحقق من التطبيق.

  • تحقق من رمز التحقق من التطبيق باستخدام 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 for 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 ، للتحقق من الرموز المميزة لفحص التطبيق.

يجب أن يكمل منطق التحقق من الرمز المميز الخطوات التالية:

  1. احصل على مجموعة Firebase App Check العامة JSON Web Key (JWK) من نقطة نهاية App Check JWKS: https://firebaseappcheck.googleapis.com/v1/jwks
  2. تحقق من توقيع رمز التحقق من التطبيق للتأكد من شرعيته.
  3. تأكد من أن رأس الرمز المميز يستخدم الخوارزمية RS256.
  4. تأكد من أن رأس الرمز المميز من النوع JWT.
  5. تأكد من إصدار الرمز المميز بواسطة Firebase App Check ضمن مشروعك.
  6. تأكد من عدم انتهاء صلاحية الرمز المميز.
  7. تأكد من أن جمهور الرمز المميز يطابق مشروعك.
  8. اختياري : تأكد من أن موضوع الرمز المميز يطابق معرف التطبيق الخاص بتطبيقك.

يمكن أن تختلف إمكانيات مكتبات JWT؛ تأكد من إكمال أي خطوات لم تعالجها المكتبة التي تختارها يدويًا.

ينفذ المثال التالي الخطوات الضرورية في روبي باستخدام جوهرة jwt كطبقة برمجية وسيطة.

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() ، وبالتالي يضيف زمن الوصول إلى أي نقطة نهاية تستخدمها. ولهذا السبب، نوصي بتمكين حماية إعادة التشغيل فقط على نقاط النهاية الحساسة بشكل خاص.

لاستخدام الحماية من إعادة التشغيل، قم بما يلي:

  1. في وحدة التحكم السحابية ، امنح دور "Firebase App Check Token Verifier" لحساب الخدمة المستخدم للتحقق من الرموز المميزة.

    • إذا قمت بتهيئة Admin SDK باستخدام بيانات اعتماد حساب خدمة Admin SDK التي قمت بتنزيلها من وحدة تحكم Firebase، فسيتم منح الدور المطلوب بالفعل.
    • إذا كنت تستخدم وظائف السحابة من الجيل الأول مع تكوين Admin SDK الافتراضي، فامنح الدور لحساب خدمة App Engine الافتراضي . راجع تغيير أذونات حساب الخدمة .
    • إذا كنت تستخدم وظائف السحابة من الجيل الثاني مع تكوين 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 والويب .