يمكنك استخدام App Check لحماية موارد الخلفية المخصّصة غير التابعة لـ Google في تطبيقك، مثل الخلفية المستضافة ذاتيًا. لإجراء ذلك، عليك تنفيذ كلا الإجراءين التاليين:
- عدِّل برنامج تطبيقك لإرسال رمز مميّز من App Check مع كل طلب إلى الخلفية، كما هو موضّح في صفحات iOS+ أو Android أو الويب أو Flutter أو Unity أو C++.
- عدِّل الخلفية لطلب رمز مميّز صالح من App Check مع كل طلب، كما هو موضّح في هذه الصفحة.
التحقّق من الرمز المميّز
للتحقّق من رموز App Check المميّزة في الخلفية، أضِف منطقًا إلى نقاط نهاية واجهة برمجة التطبيقات التي تنفّذ ما يلي:
تأكَّد من أنّ كل طلب يتضمّن رمز App Check مميزًا.
التحقّق من رمز App Check المميّز باستخدام مدير SDK
إذا نجحت عملية التحقّق، ستعرض حزمة تطوير البرامج (SDK) لمدير Firebase الرمز المميّز 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.
}
Java
إذا لم يسبق لك تثبيت مدير SDK للغة Java (الإصدار 9.12.0 أو إصدار أحدث)، عليك تثبيته.
بعد ذلك، في معالِجات نقاط نهاية واجهة برمجة التطبيقات أو فلاتر الطلبات، يمكنك استدعاء
FirebaseAppCheck.getInstance().verifyToken() (أو verifyTokenAsync())
ورفض الطلب في حال تعذّر تنفيذه. يستخدم المثال التالي فلتر Spring Boot للتحقّق من الرمز المميّز للطلبات الواردة:
import com.google.firebase.FirebaseApp;
import com.google.firebase.appcheck.FirebaseAppCheck;
import com.google.firebase.appcheck.FirebaseAppCheckException;
import com.google.firebase.appcheck.VerifyAppCheckTokenResponse;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.filter.OncePerRequestFilter;
@SpringBootApplication
@RestController
public class Application {
public static void main(String[] args) {
FirebaseApp.initializeApp();
SpringApplication.run(Application.class, args);
}
@GetMapping("/yourApiEndpoint")
public String yourApiEndpoint() {
// Handle request.
return "Success";
}
@Component
public static class AppCheckFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String appCheckToken = request.getHeader("X-Firebase-AppCheck");
if (appCheckToken == null || appCheckToken.isEmpty()) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
return;
}
try {
VerifyAppCheckTokenResponse appCheckResponse =
FirebaseAppCheck.getInstance().verifyToken(appCheckToken);
// If verifyToken() succeeds, continue with the filter chain.
filterChain.doFilter(request, response);
} catch (FirebaseAppCheckException e) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
}
}
غير ذلك
إذا كانت الخلفية مكتوبة بلغة أخرى، يمكنك استخدام مكتبة JWT للأغراض العامة، مثل تلك المتوفّرة على jwt.io، للتحقّق من رموز "فحص التطبيقات" المميّزة.
يجب أن يكمل منطق التحقّق من الرمز المميّز الخطوات التالية:
- احصل على مجموعة مفاتيح الويب JSON (JWK) العامة لميزة "فحص التطبيقات من Firebase" من نقطة نهاية JWKS لميزة "فحص التطبيقات":
https://firebaseappcheck.googleapis.com/v1/jwks - التحقّق من توقيع رمز فحص التطبيقات للتأكّد من أنّه صالح
- تأكَّد من أنّ عنوان الرمز المميّز يستخدم خوارزمية RS256.
- يُرجى التأكُّد من أنّ عنوان الرمز المميّز من نوع JWT.
- تأكَّد من أنّ الرمز المميّز صادر عن ميزة "فحص التطبيقات من Firebase" ضمن مشروعك.
- تأكَّد من عدم انتهاء صلاحية الرمز المميّز.
- تأكَّد من أنّ جمهور الرمز المميّز يتطابق مع مشروعك.
- اختياري: تأكَّد من أنّ موضوع الرمز المميّز يتطابق مع معرّف تطبيقك.
يمكن أن تختلف إمكانات مكتبات 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
الحماية من هجمات إعادة الإرسال (إصدار تجريبي)
لحماية نقطة نهاية من هجمات إعادة التشغيل، يمكنك استخدام رمز "فحص التطبيقات" المميّز بعد التحقّق منه لكي يتم استخدامه مرة واحدة فقط.
يؤدي استخدام ميزة "الحماية من إعادة التشغيل" إلى إضافة رحلة ذهاب وعودة في الشبكة إلى طلب التحقّق من الرمز المميّز، وبالتالي يزيد وقت الاستجابة في أي نقطة نهاية تستخدم هذه الميزة. لهذا السبب، ننصحك بتفعيل ميزة "الحماية من إعادة التشغيل" فقط على نقاط النهاية الحسّاسة بشكل خاص.
لاستخدام ميزة "الحماية من إعادة التشغيل"، اتّبِع الخطوات التالية:
في وحدة تحكّم Cloud، امنح دور "التحقّق من رمز ميزة فحص التطبيقات من Firebase" لحساب الخدمة المستخدَم للتحقّق من الرموز المميّزة.
- إذا بدأت تشغيل مدير SDK باستخدام بيانات اعتماد حساب خدمة مدير SDK التي نزّلتها من وحدة تحكّم Firebase، يكون قد تم منح الدور المطلوب.
- إذا كنت تستخدم الجيل الأول من Cloud Functions مع الإعدادات التلقائية لـ Admin SDK، امنح الدور إلى حساب الخدمة التلقائي في App Engine. يُرجى الاطّلاع على تغيير أذونات حساب الخدمة.
- إذا كنت تستخدم الجيل الثاني من Cloud Functions مع الإعدادات التلقائية لـ Admin SDK، امنح الدور إلى حساب خدمة Compute Engine التلقائي.
لاستخدام رمز مميّز، عليك تمرير
{ consume: true }(Node.js) أوconsume=True(Python) أوVerifyAppCheckTokenOptionsمعsetConsume(true)(Java) إلى طلب التحقّق من الرمز المميّز، أو طلبVerifyOneTimeToken()(Go)، ثم فحص عنصر النتيجة. إذا سبق أن تم استخدام الرمز المميّز، ارفض الطلب أو اتّخِذ إجراءً تصحيحيًا، مثل مطالبة المتصل باجتياز عمليات تحقّق أخرى: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.يؤدي ذلك إلى التحقّق من الرمز المميّز ثم وضع علامة عليه على أنّه تم استخدامه. سيتم ضبط
alreadyConsumedعلىtrueفي عمليات الاستدعاء المستقبلية لـverifyToken(appCheckToken, { consume: 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).متابعة
decodedToken, err := appCheck.VerifyOneTimeToken(r.Context(), appCheckToken[0]) if err != nil || (decodedToken.AlreadyConsumed != nil && *decodedToken.AlreadyConsumed) { w.WriteHeader(http.StatusUnauthorized) w.Write([]byte("Unauthorized.")) return } // If VerifyOneTimeToken() succeeds and AlreadyConsumed is not true, okay to // continue with the provided handler.يؤدي ذلك إلى التحقّق من الرمز المميّز ثم وضع علامة عليه على أنّه تم استخدامه. ستؤدي عمليات الاستدعاء المستقبلية لـ
appCheck.VerifyOneTimeToken()على الرمز المميّز نفسه إلى ضبطAlreadyConsumedعلىtrue. (يُرجى العِلم أنّappCheck.VerifyToken()لا يرفض الرمز المميّز المستخدَم أو يتحقّق مما إذا كان مستخدَمًا).Java
VerifyAppCheckTokenOptions options = VerifyAppCheckTokenOptions.builder() .setConsume(true) .build(); VerifyAppCheckTokenResponse appCheckResponse = FirebaseAppCheck.getInstance().verifyToken(appCheckToken, options); if (appCheckResponse.isAlreadyConsumed().orElse(false)) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"); return; } // If verifyToken() succeeds and isAlreadyConsumed() is not true, continue // with the filter chain.يؤدي ذلك إلى التحقّق من الرمز المميّز ثم وضع علامة عليه على أنّه تم استخدامه. عند استدعاء
verifyToken(appCheckToken, options)في المستقبل باستخدامsetConsume(true)على الرمز المميّز نفسه، يتم عرضOptional.of(true)منisAlreadyConsumed(). (يُرجى العِلم أنّverifyToken()لا يرفض الرمز المميّز المستخدَم أو حتى يتحقّق مما إذا كان مستخدَمًا في حال عدم ضبطsetConsume(true).)
عند تفعيل هذه الميزة لنقطة نهاية معيّنة، عليك أيضًا تعديل رمز عميل تطبيقك للحصول على رموز مميّزة محدودة الاستخدام وقابلة للاستهلاك من أجل استخدامها مع نقطة النهاية. يمكنك الاطّلاع على مستندات جهة العميل الخاصة بأنظمة Apple الأساسية وAndroid والويب.