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

يمكنك استخدام App Check لحماية موارد الخلفية المخصّصة غير التابعة لـ Google في تطبيقك، مثل الخلفية المستضافة ذاتيًا. لإجراء ذلك، عليك تنفيذ كلا الإجراءَين التاليَين:

  • عدِّل برنامج تطبيقك لإرسال رمز مميّز من App Check مع كل طلب إلى الخلفية، كما هو موضّح في صفحات iOS+ أو Android أو الويب أو Flutter أو Unity أو C++‎.
  • عدِّل الخلفية لطلب رمز مميّز صالح من App Check مع كل طلب، كما هو موضّح في هذه الصفحة.

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

للتحقّق من رموز App Check المميّزة في الخلفية، أضِف منطقًا إلى نقاط نهاية واجهة برمجة التطبيقات ينفّذ ما يلي:

  • تأكَّد من أنّ كل طلب يتضمّن رمز App Check مميزًا.

  • التحقّق من رمز App Check المميّز باستخدام مدير SDK

    في حال نجاح عملية التحقّق، يعرض "مدير SDK" الرمز المميّز App Check الذي تم فك ترميزه. يشير التحقّق الناجح إلى أنّ الرمز المميّز نشأ من تطبيق ينتمي إلى مشروعك على Firebase.

ارفض أي طلب يتعذّر فيه إجراء أي من عمليتَي التحقّق. على سبيل المثال:

Node.js

إذا لم يسبق لك تثبيت Node.js مدير 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

إذا لم يسبق لك تثبيت Python مدير SDK، يُرجى تثبيته.

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

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

ينفّذ المثال التالي الخطوات اللازمة في Ruby باستخدام gem 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

الحماية من هجمات إعادة الإرسال (إصدار تجريبي)

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

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

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

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

    • إذا بدأت حزمة تطوير البرامج (SDK) للمشرف باستخدام بيانات اعتماد حساب خدمة حزمة تطوير البرامج (SDK) للمشرف التي نزّلتها من وحدة تحكّم Firebase، يكون الدور المطلوب قد تم منحه بالفعل.
    • إذا كنت تستخدم الجيل الأول من Cloud Functions مع الإعدادات التلقائية لـ Admin SDK، امنح الدور إلى حساب الخدمة التلقائي في App Engine. يُرجى الاطّلاع على تغيير أذونات حساب الخدمة.
    • إذا كنت تستخدم الجيل الثاني من Cloud Functions مع الإعدادات التلقائية لـ Admin SDK، امنح الدور إلى حساب خدمة Compute Engine التلقائي.
  2. لاستخدام رمز مميّز، مرِّر { consume: true } (Node.js) أو consume=True (Python) إلى طلب التحقّق من الرمز المميّز، ثم افحص عنصر النتيجة. إذا سبق أن تم استخدام الرمز المميّز، ارفض الطلب أو اتّخِذ إجراءً تصحيحيًا، مثل مطالبة المتصل باجتياز عمليات تحقّق أخرى:

    Node.js

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

    Python

    app_check_claims = app_check.verify_token(app_check_token, consume=True)
    
    if app_check_claims.get("already_consumed"):
        return "App Check token already consumed", 401
    
    # If verify_token() succeeds and already_consumed is not set, okay to continue.
    

    يؤدي ذلك إلى التحقّق من الرمز المميّز ثم وضع علامة عليه على أنّه تم استخدامه. ستؤدي عمليات الاستدعاء المستقبلية لـ app_check.verify_token(app_check_token, consume=True) على الرمز المميّز نفسه إلى ضبط already_consumed على True. (يُرجى العِلم أنّ app_check.verify_token() لا يرفض الرمز المميّز المستخدَم أو يتحقّق مما إذا تم استخدامه في حال عدم ضبط consume.)

عند تفعيل هذه الميزة لنقطة نهاية معيّنة، يجب أيضًا تعديل رمز عميل التطبيق للحصول على رموز مميّزة محدودة الاستخدام يمكن استخدامها مع نقطة النهاية. اطّلِع على مستندات جهة العميل الخاصة بأنظمة Apple الأساسية وAndroid والويب.