Uygulamanız için Google dışı özel arka uç kaynaklarını (ör. kendi barındırdığınız arka uç) korumak üzere App Check kullanabilirsiniz. Bunun için aşağıdakilerin ikisini de yapmanız gerekir:
- Uygulama istemcinizi, arka ucunuza gönderilen her istekle birlikte bir App Check jetonu gönderecek şekilde değiştirin. Bu işlem, iOS+, Android, web, Flutter, Unity veya C++ sayfalarında açıklanmıştır.
- Arka ucunuzu, bu sayfada açıklandığı gibi her istekte geçerli bir App Check jetonu gerektirecek şekilde değiştirin.
Jeton doğrulama
Arka ucunuzda App Check jetonlarını doğrulamak için API uç noktalarınıza aşağıdaki işlemleri yapan bir mantık ekleyin:
Her isteğin bir App Check jetonu içerdiğinden emin olun.
Admin SDK'yı kullanarak App Check jetonunu doğrulayın.
Doğrulama başarılı olursa Admin SDK, kodu çözülmüş App Check jetonunu döndürür. Doğrulamanın başarılı olması, jetonun Firebase projenize ait bir uygulamadan geldiğini gösterir.
Kontrollerden herhangi birinde başarısız olan istekleri reddedin. Örneğin:
Node.js
Node.js Admin SDK'yı henüz yüklemediyseniz yükleyin.
Ardından, Express.js ara katman yazılımını örnek olarak kullanarak:
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'yı henüz yüklemediyseniz yükleyin.
Ardından, API uç nokta işleyicilerinizde app_check.verify_token() yöntemini çağırın ve başarısız olursa isteği reddedin. Aşağıdaki örnekte, @before_request ile dekore edilmiş bir işlev, tüm istekler için bu görevi gerçekleştirir:
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
Go için Admin SDK'yı henüz yüklemediyseniz yükleyin.
Ardından, API uç nokta işleyicilerinizde appcheck.Client.VerifyToken() yöntemini çağırın ve başarısız olursa isteği reddedin. Aşağıdaki örnekte, bir sarmalayıcı işlevi bu mantığı uç nokta işleyicilerine ekler:
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
Java için Admin SDK'yı (9.12.0 veya sonraki bir sürüm) henüz yüklemediyseniz yükleyin.
Ardından, API uç nokta işleyicilerinizde veya istek filtrelerinizde FirebaseAppCheck.getInstance().verifyToken() (veya verifyTokenAsync()) yöntemini çağırın ve başarısız olursa isteği reddedin. Aşağıdaki örnekte, gelen isteklerin jetonunu doğrulamak için bir Spring Boot filtresi kullanılmaktadır:
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");
}
}
}
}
Diğer
Arka ucunuz başka bir dilde yazılmışsa Uygulama Kontrolü jetonlarını doğrulamak için jwt.io adresinde bulunanlar gibi genel amaçlı bir JWT kitaplığı kullanabilirsiniz.
Jeton doğrulama mantığınız aşağıdaki adımları tamamlamalıdır:
- App Check JWKS uç noktasından Firebase Uygulama Kontrolü herkese açık JSON Web Anahtarı (JWK) Kümesi'ni alın:
https://firebaseappcheck.googleapis.com/v1/jwks - Uygulama Kontrolü jetonunun imzasını doğrulayarak geçerli olduğundan emin olun.
- Jetonun başlığında RS256 algoritmasının kullanıldığından emin olun.
- Jetonun başlığının JWT türünde olduğundan emin olun.
- Jetonun, projeniz kapsamında Firebase Uygulama Kontrolü tarafından verildiğinden emin olun.
- Jetonun süresinin dolmadığından emin olun.
- Jetonun kitlesinin projenizle eşleştiğinden emin olun.
- İsteğe bağlı: Jetonun konusunun, uygulamanızın uygulama kimliğiyle eşleştiğinden emin olun.
JWT kitaplıklarının özellikleri farklılık gösterebilir. Seçtiğiniz kitaplık tarafından gerçekleştirilmeyen adımları manuel olarak tamamladığınızdan emin olun.
Aşağıdaki örnekte, Rack ara katman yazılımı katmanı olarak jwt gem'i kullanılarak Ruby'de gerekli adımlar gerçekleştirilmektedir.
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
Tekrar oynatma koruması (beta)
Bir uç noktayı tekrar oynatma saldırılarına karşı korumak için Uygulama Kontrolü jetonunu doğruladıktan sonra kullanabilirsiniz. Böylece jeton yalnızca bir kez kullanılabilir.
Yeniden oynatma korumasının kullanılması, jeton doğrulama çağrısına bir ağ gidiş dönüşü ekler ve bu nedenle, bunu kullanan tüm uç noktalara gecikme ekler. Bu nedenle, tekrar oynatma korumasını yalnızca özellikle hassas uç noktalarda etkinleştirmenizi öneririz.
Yeniden oynatma korumasını kullanmak için aşağıdakileri yapın:
Cloud Console'da, jetonları doğrulamak için kullanılan hizmet hesabına "Firebase Uygulama Kontrolü Jeton Doğrulayıcı" rolünü verin.
- Admin SDK'yı Firebase konsolundan indirdiğiniz Admin SDK hizmet hesabı kimlik bilgileriyle başlattıysanız gerekli rol zaten verilmiştir.
- Varsayılan Admin SDK yapılandırmasıyla 1. nesil Cloud Functions kullanıyorsanız rolü App Engine varsayılan hizmet hesabına verin. Hizmet hesabı izinlerini değiştirme başlıklı makaleyi inceleyin.
- Varsayılan Admin SDK yapılandırmasıyla 2. nesil Cloud Functions kullanıyorsanız rolü varsayılan Compute hizmet hesabına verin.
Jeton kullanmak için jeton doğrulama çağrısına
{ consume: true }(Node.js),consume=True(Python) veyasetConsume(true)ileVerifyAppCheckTokenOptions(Java) iletin ya daVerifyOneTimeToken()(Go) çağrısı yapın ve ardından sonuç nesnesini inceleyin. Jeton daha önce kullanıldıysa isteği reddedin veya düzeltici işlem yapın (ör. arayanın diğer kontrolleri geçmesini zorunlu kılın):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.Bu işlem, jetonu doğrular ve ardından kullanıldı olarak işaretler. Aynı jeton üzerinde
verifyToken(appCheckToken, { consume: true })için gelecekteki çağrılaralreadyConsumeddeğerinitrueolarak ayarlayacak.consumeayarlanmamışsaverifyToken(), kullanılmış bir jetonu reddetmez, hatta kullanılıp kullanılmadığını kontrol etmez.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.Bu işlem, jetonu doğrular ve ardından kullanıldı olarak işaretler. Aynı jeton üzerinde gelecekteki
app_check.verify_token(app_check_token, consume=True)çağrılarıalready_consumeddeğeriniTrueolarak ayarlayacak. (consumeayarlanmamışsaapp_check.verify_token(), tüketilen bir jetonu reddetmez, hatta tüketilip tüketilmediğini kontrol etmez.)Go
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.Bu işlem, jetonu doğrular ve ardından kullanıldı olarak işaretler. Aynı jeton üzerinde
appCheck.VerifyOneTimeToken()işlevinin gelecekteki çağrılarıAlreadyConsumeddeğerinitrueolarak ayarlar. (appCheck.VerifyToken(), kullanılmış bir jetonu reddetmez, hatta kullanılıp kullanılmadığını kontrol bile etmez.)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.Bu işlem, jetonu doğrular ve ardından kullanıldı olarak işaretler. Aynı jeton üzerinde
setConsume(true)ileverifyToken(appCheckToken, options)işlevinin gelecekteki çağrıları,isAlreadyConsumed()kaynağındanOptional.of(true)döndürür. (setConsume(true)ayarlanmamışsaverifyToken(), tüketilen bir jetonu reddetmez, hatta tüketilip tüketilmediğini kontrol etmez.)
Bu özelliği belirli bir uç nokta için etkinleştirdiğinizde, uç noktayla kullanılacak tüketilebilir sınırlı kullanım jetonları almak için uygulama istemci kodunuzu da güncellemeniz gerekir. Apple platformları, Android ve web için istemci tarafı belgelerine bakın.