يمكنك استخدام App Check لحماية موارد الخلفية المخصّصة غير التابعة لـ Google لتطبيقك، مثل الخلفية المستضافة ذاتيًا. لإجراء ذلك، عليك تنفيذ ما يلي:
- عدِّل برنامج تطبيقك لإرسال رمز مميّز App Check مع كل طلب إلى الخلفية، كما هو موضّح في صفحات iOS+ أو Android أو الويب أو Flutter أو Unity أو C++.
- عدِّل الخلفية لتتطلّب رمزًا مميزًا صالحًا من App Check مع كل طلب، كما هو موضّح في هذه الصفحة.
التحقّق من صحة الرمز المميّز
لإثبات صحة رموز App Check على الخادم الخلفي، أضِف منطقًا إلى نقاط نهاية واجهة برمجة التطبيقات التي تنفّذ ما يلي:
تأكَّد من أنّ كل طلب يتضمّن رمزًا مميزًا App Check.
تحقَّق من صحة الرمز المميّز App Check باستخدام Admin SDK.
في حال نجاح عملية التحقّق، تعرض حزمة تطوير البرامج (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
إذا لم يسبق لك تثبيت Python Admin 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.
...
Go
إذا لم يسبق لك تثبيت Admin 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، للتحقّق من رموز App Check المميّزة.
يجب أن يكمل منطق التحقّق من الرمز المميز الخطوات التالية:
- احصل على مجموعة مفاتيح الويب JSON (JWK) العامة الخاصة بخدمة App Check من Firebase من نقطة نهاية JWKS الخاصة بخدمة App Check:
https://firebaseappcheck.googleapis.com/v1/jwks
- تحقَّق من توقيع رمز App Check المميّز للتأكّد من أنّه صالح.
- تأكَّد من أنّ عنوان الرمز المميّز يستخدم الخوارزمية RS256.
- تأكَّد من أنّ عنوان الرمز المميّز يتضمّن النوع JWT.
- تأكَّد من أنّ الرمز المميّز صادر عن خدمة App Check من Firebase ضمن مشروعك.
- تأكَّد من عدم انتهاء صلاحية الرمز المميّز.
- تأكَّد من أنّ الجمهور المستهدف للرمز المميز يتطابق مع مشروعك.
- اختياري: تأكَّد من أنّ موضوع الرمز المميّز يتطابق مع معرّف تطبيقك.
قد تختلف إمكانات مكتبات 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
حماية إعادة التشغيل (إصدار تجريبي)
لحماية نقطة نهاية من الهجمات التي تهدف إلى إعادة استخدام الرموز، يمكنك استخدام رمز App Check المميّز بعد التحقّق منه لكي لا يمكن استخدامه إلا مرة واحدة.
يؤدي استخدام ميزة "الحماية من إعادة الإرسال" إلى إضافة رحلة ذهاب وعودة إلى الشبكة في verifyToken()
المكالمة، وبالتالي إضافة وقت استجابة إلى أي نقطة نهاية تستخدمها. لهذا السبب، ننصحك بتفعيل ميزة الحماية من إعادة الإرسال فقط على نقاط النهاية الحسّاسة بشكل خاص.
لاستخدام ميزة "الحماية من إعادة التشغيل"، اتّبِع الخطوات التالية:
في Cloud Console، امنح دور "مدقّق صحة الرموز المميزة في App Check" لحساب الخدمة المستخدَم للتحقّق من صحة الرموز المميزة.
- إذا بدأت Admin SDK باستخدام بيانات اعتماد حساب خدمة Admin SDK التي نزّلتها من وحدة تحكّم Firebase، سيتم منح الدور المطلوب تلقائيًا.
- إذا كنت تستخدم الجيل الأول من Cloud Functions مع الإعداد التلقائي لحزمة SDK للمشرف، امنح الدور لحساب الخدمة التلقائي في App Engine. اطّلِع على تغيير أذونات حساب الخدمة.
- إذا كنت تستخدم الجيل الثاني من Cloud Functions مع الإعداد التلقائي لحزمة تطوير البرامج (SDK) الخاصة بالمشرف، امنح الدور لحساب خدمة الحوسبة التلقائي.
بعد ذلك، لاستهلاك الرمز المميّز، مرِّر
{ 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 والويب.