İçeriğe geç

BFF'de Güvenlik ve Alternatifler — Token Handler, GraphQL, Gateway

İleri 9 dk Sık karşılaşılır

Önce şunu oku: BFF — Backend for Frontend

30 saniyede özet

Tarayıcıda bir script'in okuyabildiği her anahtarı (token) sayfaya sızan kötü bir script de okur. Token handler BFF anahtarı sunucuda tutar, tarayıcıya yalnızca script'in dokunamadığı bir cookie verir.

Bir yorum alanındaki küçük bir açık, sayfaya saldırganın script’ini yerleştirdi. Kullanıcının giriş anahtarı da aynı sayfada, localStorage’da duruyordu.

  1. Bayt: Yorum alanından sayfaya yabancı bir script sızmış!

  2. Sen: Sayfayı kapatırız, olur biter.

  3. Bayt: Kullanıcının giriş anahtarı da localStorage'daydı...

  4. Bayt: Otel kartını masada mı bırakmalı, resepsiyonda mı tutmalı?

Anahtar tarayıcıda nerede duruyor?

Access token localStorage'da. Sayfaya bir XSS script'i enjekte edildi. Script token'la ne yapabilir? Cevabı göster

Okuyup saldırganın sunucusuna gönderebilir. Token süresi dolana kadar her yerden kullanılır; sayfayı kapatmak saldırıyı durdurmaz.

Anahtar masada mı, resepsiyonda mı?
Adım adım oku
  1. Token localStorage'daysa sayfadaki her script onu okuyabilir, sitenin kendi kodu da, sızan kötü script de.
  2. Sızan script token'ı okuyup saldırganın sunucusuna yollar; saldırgan artık her yerden giriş yapabilir.
  3. Token handler kalıbında token BFF'te, yani sunucuda kalır; arka servislere onu BFF ekler.
  4. Tarayıcı yalnızca HttpOnly bir cookie taşır; script onu okuyamaz, bu yüzden dışarı da yollayamaz.

XSSCross-site scripting: saldırganın script'inin bir sayfada o sitenin kendi kodu gibi çalışması. Script'in erişebildiği her şeye, localStorage dahil, erişir.Sözlükte gör →, saldırganın script’inin sitenin kendi kodu gibi çalışmasıdır. Script’in erişebildiği her depolamaya o da erişir. Token’ı yalnızca JavaScript belleğinde tutmak bunu zorlaştırır, ama sayfa yenilenince oturum düşer.

Cross
Çapraz (İngilizcede X ile kısaltılır):
Site
...siteden gelen...
Scripting
...script. Saldırganın kodu, sitenin kendi kodu gibi çalışır.
Kafam karıştı, daha basit anlat

Anahtarı tarayıcıda saklarsan, sayfaya sızan kötü bir kod da onu bulabilir. Anahtarı cebinden alıp götürür, sen sayfayı kapatsan bile kullanmaya devam eder.

Hızlı kontrolOrta

Bir SPA access token'ı localStorage'da tutuyor. En büyük risk ne?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Token'ı yalnızca JavaScript belleğinde tutmak XSS'e karşı localStorage'dan daha iyidir. Pratikteki en büyük bedeli ne?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

token handlerTarayıcı uygulamasının OAuth token'larını BFF'in sunucu tarafında tutması; tarayıcıya yalnızca HttpOnly bir oturum cookie'si verilir ve BFF token'ı arka servislere kendisi ekler.Sözlükte gör → kalıbında OAuth akışını BFF tamamlar ve token’ları kendi tarafında saklar. Tarayıcı yalnızca bir HttpOnly cookieJavaScript'in okuyamadığı, yalnızca tarayıcının isteklere otomatik eklediği cookie. XSS bu cookie'nin değerini çalamaz; ama sayfa açıkken istek atabilir.Sözlükte gör → taşır; BFF arka servislere token’ı kendisi ekler.

Spring Cloud Gateway üzerinde BFF
spring.cloud.gateway.routes:
- id: orders
uri: lb://orders
predicates: [ Path=/api/orders/** ]
filters: [ TokenRelay= ] # oturumdaki token'ı Authorization başlığına koyar

Bedeli CSRF’tir. Cookie’ler kendiliğinden gönderilir; CSRFTarayıcının isteğe otomatik eklediği kimlik bilgilerini başka bir sitenin sömürmesi. Token elle eklenen bir başlıktaysa bu saldırı yüzeyi yoktur; cookie'deyse vardır.Sözlükte gör → için SameSite=Lax/Strict ve state değiştiren isteklerde bir CSRF token gerekir.

Bir internet bankacılığı sitesinde bu kalıbın nasıl durduğuna küçük bir örnekle bakalım. İyi haber: havale gönderen sayfa token’ı hiç görmüyor.

Derinleş · İnternet bankacılığı: token BFF'te, cookie tarayıcıda 4 dosya · ~82 satır · ilk okumada atlayabilirsin
Proje dosyaları

web-bff/src/main/resources/ application.yml BFF girişi kendisi yapar, token'ı oturumda tutar ve TokenRelay ile arka servislere ekler; oturum cookie'si kilitli.

web-bff/src/main/resources/application.yml
# spring-session-data-redis is on the classpath: sessions, and the tokens in them,
# live in Redis, so any BFF pod can serve the next request.
spring:
security:
oauth2:
client:
registration:
bank:
client-id: internet-banking-web
client-secret: ${BFF_CLIENT_SECRET} # stays on the server
authorization-grant-type: authorization_code
scope: openid,accounts.read,transfers.write
provider:
bank:
issuer-uri: https://auth.bank.example/realms/retail
cloud:
gateway:
server:
webflux:
routes:
- id: transfers
uri: lb://transfer-service
predicates:
- Path=/api/transfers/**
filters:
- TokenRelay= # puts the session's access token in Authorization: Bearer
server:
reactive:
session:
cookie:
name: BANK_SESSION
http-only: true # no script can read it
secure: true # sent over HTTPS only
same-site: strict # not sent on requests started by other sites

web-bff/src/main/java/com/bank/bff/ SecurityConfig.java Güvenlik ayarı: CSRF değeri bir cookie ile sayfaya verilir ve başlıkta geri beklenir.

web-bff/src/main/java/com/bank/bff/SecurityConfig.java
@Configuration
@EnableWebFluxSecurity
class SecurityConfig {
@Bean
SecurityWebFilterChain security(ServerHttpSecurity http) {
return http
.authorizeExchange(auth -> auth.anyExchange().authenticated())
.oauth2Login(Customizer.withDefaults()) // the BFF runs the OAuth flow
.csrf(csrf -> csrf
.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse())
// Plain token: the page copies the XSRF-TOKEN cookie into X-XSRF-TOKEN.
.csrfTokenRequestHandler(new ServerCsrfTokenRequestAttributeHandler()))
.build();
}
// The CSRF token is created lazily; subscribing to it here makes sure the cookie is written.
@Bean
WebFilter csrfCookieFilter() {
return (exchange, chain) -> exchange
.<Mono<CsrfToken>>getAttributeOrDefault(CsrfToken.class.getName(), Mono.empty())
.then(chain.filter(exchange));
}
}

web/src/transfer/ sendTransfer.ts Sayfadaki havale çağrısı token görmez: oturum cookie'si kendiliğinden gider, CSRF değeri başlığa kopyalanır.

web/src/transfer/sendTransfer.ts
function csrfToken(): string {
const value = /(?:^|; )XSRF-TOKEN=([^;]*)/.exec(document.cookie)?.[1];
return value ? decodeURIComponent(value) : '';
}
// No token anywhere in the browser: the HttpOnly session cookie travels on its own.
export function sendTransfer(toIban: string, amountKurus: number): Promise<Response> {
return fetch('/api/transfers', {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json', 'X-XSRF-TOKEN': csrfToken() },
body: JSON.stringify({ toIban, amountKurus }),
});
}

web/src/transfer/ sendTransfer.localStorage.ts Şöyle de yazılabilirdi: token localStorage'da. Bak, sayfaya sızan bir script de aynı satırı çalıştırabiliyor.

web/src/transfer/sendTransfer.localStorage.ts
// Another way to write it: the page keeps the access token itself.
export function sendTransfer(toIban: string, amountKurus: number): Promise<Response> {
// Any script on the page can run this same line, including one injected through a comment field.
const token = localStorage.getItem('access_token');
return fetch('https://api.bank.example/transfers', {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({ toIban, amountKurus }),
});
}
Kafam karıştı, daha basit anlat

Anahtar sunucuda, kasada durur. Tarayıcıya sadece bir vestiyer fişi verilir. Sayfadaki kötü kod bu fişi okuyamaz, tarayıcı onu yalnızca bu siteye giden isteklere ekler.

Hızlı kontrolOrta

Token handler (BFF) kalıbında access token nerede durur ve tarayıcı ne taşır?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Token handler ile oturum artık bir cookie'ye bağlı. Hangi yeni risk ortaya çıkar ve nasıl kapatılır?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Kendin gör

Token nerede durmalı — tarayıcıda mı, BFF'te mi?

Tohum 290229

SPA: localStorage

Tarayıcı

access_token (localStorage)

BFF

—

Arka servisler

Authorization: Bearer …

  1. Oynat ya da adımla.
Hız
Adım 0

Şu an ne oldu?

SPA: localStorage

Bir olay gerçekleşmek üzere.

Görevler0/4

  • Bir XSS ile token'ı saldırganın sunucusuna taşıaçık

    İpucu

    Script'in doğrudan okuyabildiği bir depolama.

  • Sayfa yenilemesiyle kullanıcıyı oturumdan düşüraçık

    İpucu

    Token hiçbir kalıcı yerde durmuyorsa?

  • Başka bir siteden gelen sahte bir isteği başarılı kılaçık

    İpucu

    Cookie'ler kendiliğinden gönderilir — tarayıcıya bunu yasaklamadıysan.

  • Token handler ile sahte isteği reddetaçık

    İpucu

    BFF ve doğru cookie ayarı.

Olay günlüğü (0)

Henüz olay yok. Oynat veya adımla.

  1. Olayı XSS yap. localStorage: token çalındı.
  2. Belleğe geç. Token dışarı çıkmadı; olayı “sayfa yenilendi” yap — oturum düştü.
  3. BFF seç, olayı XSS yap. Sayfa içinde istek atılabiliyor, ama token çalınamıyor.
  4. Olayı CSRF yap, SameSite kapalı. Sahte istek geçti. SameSite’ı aç.
Hızlı kontrolOrta

Token handler'a geçtin. XSS riski tamamen ortadan kalktı mı?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

BFF, GraphQL, gateway: hangisi ne zaman?

YapıCevapladığı soruRiski
GatewayBütün istemciler için ortak işler nerede?İş mantığı girerse darboğaz
BFFBu istemcinin ekranı ve güvenliği için ne gerekiyor?İstemci sayısıyla çoğalır
GraphQLİstemci hangi alanları istediğini kendisi söylesin mi?Pahalı sorgular, N+1

Üçü birbirinin rakibi değil. Önde bir gateway, web için token handler bir BFF, istemciler çeşitlendikçe bir GraphQL katmanı birlikte durabilir.

Hızlı kontrolOrta

Her ihtiyacı en uygun yapıyla eşleştir.

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Sınıflandırılmamış

API Gateway

İstemciden bağımsız ortak işler

    BFF

    Tek bir istemci türüne göre biçimlenmiş uç

      GraphQL

      İstemci ihtiyacı olan alanları kendisi seçer

        Tuzaklar

        SameSite=None bir oturum cookie’si. Başka sitelerden gelen isteklere de eklenir.

        Pod belleğinde token. İstek başka pod’a düşünce oturum kaybolur; Redis gibi paylaşılan bir depo gerekir.

        XSS’i çözülmüş sanmak. Token handler hasarı sınırlar; XSS’i önlemek CSP ve doğru çıktı kodlamanın işi.

        Arka servise geniş kapsamlı token. Her servis yalnızca ihtiyacı olan kapsamı görmeli.

        Hızlı kontrolİleri

        BFF'in oturum cookie'si böyle ayarlanmış. Güvenlik incelemesinde hangi satırlar işaretlenir?

        Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

        Hatalı satıra dokun, sonra kontrol et.

        Set-Cookie
        HTTPUTF-8LF

        Kendini sına

        Şimşek turu1/5

        localStorage'daki bir değeri sayfaya sızan kötü bir script de okuyabilir.

        Soru 1/2İleri

        Token handler BFF üç pod'la çalışıyor ve token'ları her pod kendi belleğinde tutuyor. Ne olur?

        Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

        Aklında kalacak üç şey

        1. 1 localStorage'daki bir token'ı sayfaya sızan bir script okuyup dışarı taşıyabilir. Bellekte tutmak bunu zorlaştırır, ama sayfa yenilenince oturum düşer.
        2. 2 Token handler'da token'lar BFF'te durur, tarayıcı yalnızca HttpOnly bir cookie taşır. Kötü script hâlâ zarar verebilir, ama token'ı çalamaz.
        3. 3 Cookie ile çalışan oturum CSRF riskini geri getirir: SameSite=Lax/Strict ve gerekirse CSRF token şarttır.
        Sonraki kapı İki servisin veritabanını tek bir büyük transaction'a bağlasan olmaz mı? Neden çoğu ekip sagayı seçiyor? Dağıtık Transaction — 2PC Neden Nadiren, Saga Neden Sık · 10 dk

        4 kart sonraki derste seni bekliyor

        0/5 kart bu dersten toplandı