คุณสามารถปกป้องทรัพยากรที่ไม่ใช่ Firebase ของแอป เช่น แบ็กเอนด์ที่โฮสต์ด้วยตนเอง ด้วย App Check ในการดำเนินการนี้ คุณจะต้องทำทั้ง 2 อย่างต่อไปนี้
- แก้ไขไคลเอ็นต์ของแอปให้ส่งโทเค็น App Check ไปพร้อมกับคำขอแต่ละรายการ ลงในแบ็กเอนด์ของคุณ ตามที่อธิบายไว้ในหน้าสำหรับ iOS+ Android และ เว็บ
- แก้ไขแบ็กเอนด์ให้ต้องมีโทเค็น App Check ที่ถูกต้องกับทุกคำขอ ตามที่อธิบายไว้ในหน้านี้
การยืนยันโทเค็น
หากต้องการยืนยันโทเค็น 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
หากคุณยังไม่ได้ติดตั้ง 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.
...
Go
หากคุณยังไม่ได้ติดตั้ง Admin SDK for Go แบบนั้น
จากนั้น ในเครื่องจัดการปลายทาง API ให้เรียกใช้ appcheck.Client.VerifyToken()
และปฏิเสธคำขอหากล้มเหลว ในตัวอย่างต่อไปนี้ Wrapper
จะเพิ่มตรรกะนี้ลงในเครื่องจัดการปลายทาง
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) ของ Firebase App Check จากแอป
ตรวจสอบปลายทาง JWKS:
https://firebaseappcheck.googleapis.com/v1/jwks
- ยืนยันลายเซ็นของโทเค็น App Check เพื่อให้แน่ใจว่าถูกต้อง
- ตรวจสอบว่าส่วนหัวของโทเค็นใช้อัลกอริทึม RS256
- ตรวจสอบว่าส่วนหัวของโทเค็นเป็นประเภท JWT
- ตรวจสอบว่าโทเค็นออกโดย Firebase App Check ภายใต้
- ตรวจสอบว่าโทเค็นยังไม่หมดอายุ
- ตรวจสอบว่ากลุ่มเป้าหมายของโทเค็นตรงกับโปรเจ็กต์ของคุณ
- ไม่บังคับ: ตรวจสอบว่าเรื่องของโทเค็นตรงกับรหัสแอปของแอป
ความสามารถของไลบรารี JWT อาจแตกต่างกันไป อย่าลืมกรอกลับ ขั้นตอนที่ไม่ได้จัดการจากไลบรารีที่คุณเลือก
ตัวอย่างต่อไปนี้ดำเนินการตามขั้นตอนที่จำเป็นใน Ruby โดยใช้ 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()
ซึ่งจะเพิ่มเวลาในการตอบสนองไปยังปลายทางที่ใช้งาน ด้วยเหตุนี้
เราขอแนะนำให้คุณเปิดใช้การป้องกันการเล่นซ้ำสำหรับ
ปลายทาง
หากต้องการใช้การป้องกันการเล่นซ้ำ ให้ทำดังนี้
ใน Cloud Console ได้ ให้สิทธิ์ "Firebase App Check Token Verifier" ของบัญชีบริการ ที่ใช้ในการยืนยันโทเค็น
- หากคุณเริ่มต้น Admin SDK ด้วยบัญชีบริการของ Admin SDK ข้อมูลเข้าสู่ระบบที่คุณดาวน์โหลดจากคอนโซล Firebase บทบาทที่จําเป็นคือ อนุญาตแล้ว
- หากคุณใช้ Cloud Functions รุ่นที่ 1 กับผู้ดูแลระบบเริ่มต้น การกำหนดค่า SDK ให้บทบาทกับบริการ App Engine เริ่มต้น บัญชี ดูการเปลี่ยนสิทธิ์ของบัญชีบริการ
- หากคุณใช้ Cloud Functions รุ่นที่ 2 กับผู้ดูแลระบบเริ่มต้น กำหนดค่า 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 และ เว็บ