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

يمكنك حماية موارد تطبيقك التي ليست ضِمن Firebase، مثل الخلفيات التي تتم استضافتها ذاتيًا. باستخدام ميزة "فحص التطبيقات" ولإجراء ذلك، سيتعيّن عليك إجراء ما يلي:

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

إثبات صحة الرمز المميّز

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

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

  • تحقَّق من الرمز المميّز لفحص التطبيقات باستخدام حزمة تطوير البرامج (SDK) الخاصة بالمشرف.

    إذا نجح إثبات الملكية، ستعرض حزمة تطوير البرامج (SDK) الخاصة بالمشرف عملية فحص التطبيقات التي تم فك ترميزها. الرمز المميز. تشير عملية إثبات الملكية الناجحة إلى أنّ الرمز المميّز نشأ من تطبيق. الذي ينتمي إلى مشروعك في Firebase.

رفض أي طلب أخفق في أي من التحققين. على سبيل المثال:

Node.js

إذا لم تكن قد ثبتّ SDK لمشرف Node.js من قبل، القيام بذلك.

ثم، باستخدام البرمجيات الوسيطة 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

إذا لم تكن قد ثبتّ SDK لمشرف Python من قبل، القيام بذلك.

بعد ذلك، في معالِجات نقطة نهاية واجهة برمجة التطبيقات، يمكنك استدعاء 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.
    ...

انتقال

إذا لم يسبق لك تثبيت SDK للمشرف for Go، القيام بذلك.

بعد ذلك، في معالِجات نقطة نهاية واجهة برمجة التطبيقات، يمكنك استدعاء 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. الحصول على مفتاح الويب JSON العام للتحقق من التطبيقات في Firebase الذي تم ضبطه من التطبيق التحقّق من نقطة نهاية JWKS: https://firebaseappcheck.googleapis.com/v1/jwks
  2. تحقَّق من توقيع الرمز المميّز لميزة "فحص التطبيقات" للتأكُّد من صحته.
  3. تأكَّد من أنّ عنوان الرمز المميّز يستخدم الخوارزمية RS256.
  4. تأكَّد من أنّ عنوان الرمز المميّز نوع JWT.
  5. تأكَّد من أنّ الرمز المميّز تم إصداره من خلال ميزة "التحقّق من التطبيقات في Firebase" ضمن مشروعك.
  6. تأكَّد من عدم انتهاء صلاحية الرمز المميّز.
  7. تأكَّد من أنّ جمهور الرمز المميّز يتطابق مع مشروعك.
  8. اختياري: تأكَّد من أنّ موضوع الرمز المميّز يتطابق مع رقم تعريف التطبيق في تطبيقك.

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

ينفذ المثال التالي الخطوات اللازمة في Ruby باستخدام jwt كطبقة من طبقات البرمجيات الوسيطة 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

ميزة "الحماية من إعادة التشغيل" (ميزة تجريبية)

لحماية نقطة نهاية من هجمات إعادة التشغيل، يمكنك استخدام الرمز المميّز لميزة "التحقّق من التطبيقات". بعد التحقق من صحتها بحيث يمكن استخدامها مرة واحدة فقط.

يؤدي استخدام ميزة "توفير إعادة التشغيل" إلى إضافة إرسال البيانات ذهابًا وإيابًا للشبكة إلى verifyToken(). وبالتالي تضيف وقت الاستجابة إلى أي نقطة نهاية تستخدمها. لهذا السبب، ننصحك بتفعيل ميزة "توفير الحماية بعد إعادة التشغيل" فقط على الأجهزة الحسّاسة والنقاط النهائية.

لاستخدام ميزة "حماية إعادة التشغيل"، عليك اتّباع الخطوات التالية:

  1. في جلسة المعمل، Cloud Console، منح "أداة إثبات ملكية الرمز المميّز لفحص تطبيقات Firebase" الدور في حساب الخدمة يُستخدم للتحقق من الرموز المميزة.

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