אמת את אסימוני בדיקת האפליקציה מקצה אחורי מותאם אישית

אתה יכול להגן על המשאבים של האפליקציה שלך שאינם Firebase, כגון גיבויים מתארחים בעצמם, באמצעות App Check. כדי לעשות זאת, תצטרך לבצע את שתי הפעולות הבאות:

  • שנה את לקוח האפליקציה שלך כדי לשלוח אסימון App Check יחד עם כל בקשה לקצה העורפי שלך, כמתואר בדפים עבור iOS+ , Android ואינטרנט .
  • שנה את הקצה העורפי שלך כדי לדרוש אסימון App Check תקף בכל בקשה, כמתואר בדף זה.

אימות אסימון

כדי לאמת אסימוני בדיקת אפליקציות בקצה העורפי שלך, הוסף היגיון לנקודות הקצה של ה-API שלך שעושה את הפעולות הבאות:

  • בדוק שכל בקשה כוללת אסימון App Check.

  • אמת את אסימון App Check באמצעות ה-Admin SDK.

    אם האימות יצליח, ה-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() ודחה את הבקשה אם היא נכשלת. בדוגמה הבאה, פונקציית עטיפה מוסיפה את ההיגיון הזה למטפלי נקודות הקצה:

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 Public Web Key (JWK) מנקודת הקצה App Check JWKS: https://firebaseappcheck.googleapis.com/v1/jwks
  2. אמת את החתימה של אסימון App Check כדי לוודא שהיא לגיטימית.
  3. ודא שהכותרת של האסימון משתמשת באלגוריתם RS256.
  4. ודא שלכותרת האסימון יש סוג JWT.
  5. ודא שהאסימון מונפק על ידי Firebase App Check תחת הפרויקט שלך.
  6. ודא שתוקף האסימון לא פג.
  7. ודא שהקהל של האסימון מתאים לפרויקט שלך.
  8. אופציונלי : בדוק שהנושא של האסימון תואם למזהה האפליקציה של האפליקציה שלך.

היכולות של ספריות JWT עשויות להיות שונות; הקפד להשלים ידנית את כל השלבים שאינם מטופלים על ידי הספרייה שתבחר.

הדוגמה הבאה מבצעת את השלבים הדרושים ברובי באמצעות ה- 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. במסוף הענן , הענק את התפקיד "מאמת קוד בדיקת אסימון של אפליקציה של Firebase" לחשבון השירות המשמש לאימות אסימונים.

    • אם אתחולת את ה-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 ואינטרנט .