fix(security): replace spoofable X-Forwarded-For with getRemoteAddr in rate limiter

X-Forwarded-For is client-controlled and trivially bypassable for rate
limiting. Replaced with HttpServletRequest.getRemoteAddr() which uses
the container-provided remote address. Added note about configuring
quarkus.http.proxy.proxy-address-forwarding for trusted proxy setups.
This commit is contained in:
AlexisLeDain
2026-04-09 16:07:46 +02:00
parent 893eca0369
commit 8f65048bc3
3 changed files with 35 additions and 12 deletions

View File

@@ -286,14 +286,20 @@ quarkus.vault.authentication.kubernetes.role=my-role
## レート制限 ## レート制限
**セキュリティ注意**: `X-Forwarded-For`を直接使用しないでください — クライアントが偽装できます。
サーブレットリクエストの実際のリモートアドレス、または認証済みIDを使用してください。
```java ```java
@ApplicationScoped @ApplicationScoped
public class RateLimitFilter implements ContainerRequestFilter { public class RateLimitFilter implements ContainerRequestFilter {
private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>(); private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>();
@Inject
HttpServletRequest servletRequest;
@Override @Override
public void filter(ContainerRequestContext requestContext) { public void filter(ContainerRequestContext requestContext) {
String clientId = getClientIdentifier(requestContext); String clientId = getClientIdentifier();
RateLimiter limiter = limiters.computeIfAbsent(clientId, RateLimiter limiter = limiters.computeIfAbsent(clientId,
k -> RateLimiter.create(100.0)); // 1秒あたり100リクエスト k -> RateLimiter.create(100.0)); // 1秒あたり100リクエスト
@@ -306,9 +312,10 @@ public class RateLimitFilter implements ContainerRequestFilter {
} }
} }
private String getClientIdentifier(ContainerRequestContext ctx) { private String getClientIdentifier() {
// IP、APIキー、またはユーザーIDを使用 // コンテナ提供のリモートアドレスを使用X-Forwarded-Forではない
return ctx.getHeaderString("X-Forwarded-For"); // 信頼できるプロキシの背後にある場合はquarkus.http.proxy.proxy-address-forwarding=trueを設定。
return servletRequest.getRemoteAddr();
} }
} }
``` ```

View File

@@ -333,14 +333,20 @@ public class SecretService {
## Rate Limiting (Hız Sınırlama) ## Rate Limiting (Hız Sınırlama)
**Güvenlik Notu**: `X-Forwarded-For` doğrudan kullanmayın — istemciler bunu taklit edebilir.
Servlet request'ten gerçek uzak adresi veya kimliği doğrulanmış bir kimlik (API anahtarı, JWT subject) kullanın.
```java ```java
@ApplicationScoped @ApplicationScoped
public class RateLimitFilter implements ContainerRequestFilter { public class RateLimitFilter implements ContainerRequestFilter {
private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>(); private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>();
@Inject
HttpServletRequest servletRequest;
@Override @Override
public void filter(ContainerRequestContext requestContext) { public void filter(ContainerRequestContext requestContext) {
String clientId = getClientIdentifier(requestContext); String clientId = getClientIdentifier();
RateLimiter limiter = limiters.computeIfAbsent(clientId, RateLimiter limiter = limiters.computeIfAbsent(clientId,
k -> RateLimiter.create(100.0)); // Saniyede 100 istek k -> RateLimiter.create(100.0)); // Saniyede 100 istek
@@ -353,9 +359,10 @@ public class RateLimitFilter implements ContainerRequestFilter {
} }
} }
private String getClientIdentifier(ContainerRequestContext ctx) { private String getClientIdentifier() {
// IP, API anahtarı veya kullanıcı ID'si kullanın // Konteyner tarafından sağlanan uzak adresi kullanın (X-Forwarded-For değil).
return ctx.getHeaderString("X-Forwarded-For"); // Güvenilir proxy arkasındaysanız quarkus.http.proxy.proxy-address-forwarding=true ayarlayın.
return servletRequest.getRemoteAddr();
} }
} }
``` ```

View File

@@ -333,14 +333,21 @@ public class SecretService {
## Rate Limiting ## Rate Limiting
**Security Note**: Never use `X-Forwarded-For` directly — clients can spoof it.
Use the actual remote address from the servlet request, or an authenticated
identity (API key, JWT subject) when available.
```java ```java
@ApplicationScoped @ApplicationScoped
public class RateLimitFilter implements ContainerRequestFilter { public class RateLimitFilter implements ContainerRequestFilter {
private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>(); private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>();
@Inject
HttpServletRequest servletRequest;
@Override @Override
public void filter(ContainerRequestContext requestContext) { public void filter(ContainerRequestContext requestContext) {
String clientId = getClientIdentifier(requestContext); String clientId = getClientIdentifier();
RateLimiter limiter = limiters.computeIfAbsent(clientId, RateLimiter limiter = limiters.computeIfAbsent(clientId,
k -> RateLimiter.create(100.0)); // 100 requests per second k -> RateLimiter.create(100.0)); // 100 requests per second
@@ -353,9 +360,11 @@ public class RateLimitFilter implements ContainerRequestFilter {
} }
} }
private String getClientIdentifier(ContainerRequestContext ctx) { private String getClientIdentifier() {
// Use IP, API key, or user ID // Use the container-provided remote address (not X-Forwarded-For).
return ctx.getHeaderString("X-Forwarded-For"); // If behind a trusted proxy, configure quarkus.http.proxy.proxy-address-forwarding=true
// so getRemoteAddr() returns the real client IP.
return servletRequest.getRemoteAddr();
} }
} }
``` ```