https://github.com/devslab-kr/ssrf-guard
SSRF protection for Spring Boot — whitelist-based outbound HTTP guard with private-network blocking, redirect validation, and TOCTOU mitigation.
https://github.com/devslab-kr/ssrf-guard
java owasp security spring-boot spring-boot-starter ssrf web-security
Last synced: 12 days ago
JSON representation
SSRF protection for Spring Boot — whitelist-based outbound HTTP guard with private-network blocking, redirect validation, and TOCTOU mitigation.
- Host: GitHub
- URL: https://github.com/devslab-kr/ssrf-guard
- Owner: devslab-kr
- License: apache-2.0
- Created: 2025-09-23T13:42:19.000Z (8 months ago)
- Default Branch: main
- Last Pushed: 2026-05-23T06:02:30.000Z (12 days ago)
- Last Synced: 2026-05-23T06:20:14.057Z (12 days ago)
- Topics: java, owasp, security, spring-boot, spring-boot-starter, ssrf, web-security
- Language: Java
- Size: 879 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.ko.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.ko.md
- License: LICENSE
- Notice: NOTICE
Awesome Lists containing this project
README
# ssrf-guard
[English](README.md) · **한국어**
> JVM용 SSRF(Server-Side Request Forgery) 방어 — 화이트리스트 기반 outbound HTTP 가드. 사설망 차단, 리다이렉트 재검증, TOCTOU 완화, 그리고 **Spring AI 툴 URL 검증**으로 LLM 에이전트 SSRF 표면까지 차단.
[](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard)
[](https://github.com/devslab-kr/ssrf-guard/actions/workflows/ci.yml)
[](https://codecov.io/gh/devslab-kr/ssrf-guard)
[](LICENSE)
[](https://openjdk.org/projects/jdk/21/)
[](https://spring.io/projects/spring-boot)
📖 **[문서 → ssrf-guard.devslab.kr](https://ssrf-guard.devslab.kr/ko/)**
## 모듈 매트릭스 (v3.0.0)
쓰는 HTTP 클라이언트에 맞는 모듈만 고르세요. `ssrf-guard-core`는 transitive로 따라옴.
| 모듈 | 용도 | Spring? |
|---|---|---|
| **`ssrf-guard`** | 메타 — RestClient + HttpClient5 (v2.0.0 호환) | ✅ |
| `ssrf-guard-restclient` | Spring 6.1+ `RestClient` | ✅ |
| `ssrf-guard-resttemplate` | Spring `RestTemplate` | ✅ |
| `ssrf-guard-webclient` | Spring WebFlux `WebClient` | ✅ |
| `ssrf-guard-feign` | Spring Cloud OpenFeign | ✅ |
| **`ssrf-guard-springai`** ⭐ | Spring AI `ToolCallback` URL 검증 — LLM 에이전트 SSRF 차단 | ✅ |
| `ssrf-guard-httpclient5` | Apache HttpClient 5 직접 | — |
| `ssrf-guard-jdkhttp` | `java.net.http.HttpClient` | — |
| `ssrf-guard-okhttp` | OkHttp | — |
## 무엇을 하나
서비스의 모든 outbound HTTP 호출이 소켓 열리기 전 4단계 SSRF 필터를 통과합니다:
1. **URL 단계 체크 (최전선)** — 스킴 / 호스트 / 포트 / IP 리터럴 / userinfo, 가장 저렴한 게이트에서 DNS 전에 거부. 난독화된 IP 우회 (`http://2130706433/` → `127.0.0.1`) 차단.
2. **DNS 시점 화이트리스트 재검증** — 호스트 정책을 한 번 더 적용.
3. **사설망 IP 필터** — loopback, RFC-1918, link-local (AWS 메타데이터 `169.254.169.254` 포함), CGNAT, IPv6 ULA, **IPv4-mapped IPv6 + 6to4 unmapping** (`::ffff:10.0.0.5`와 `2002:0a00::`을 사설로 정확 분류).
4. **리다이렉트 재검증** — 3xx 홉마다 동일 룰. 공격자가 `example.com` 화이트리스트 후 `169.254.169.254`로 redirect 못 시킴.
resolver가 검증한 동일 `InetAddress` 배열이 HttpClient의 `Socket.connect()`에 전달됨 — TOCTOU 윈도우 닫힘.
## Spring AI 툴 콜 — 새로운 SSRF 표면
LLM 에이전트가 URL을 툴 인자로 받으면 기본적으로 SSRF 벡터:
```java
@Tool("Fetch a URL")
String fetchUrl(String url) {
return restClient.get().uri(url).retrieve().body(String.class);
// ↑ 공격자가 URL 컨트롤 — SSRF 한 줄
}
```
`ssrf-guard-springai`가 모든 `ToolCallback`을 감싸 URL 형식 인자를 정책 검증 후에만 실행되도록 막고, 거부 시 LLM이 해석/복구할 수 있는 구조화된 에러 문자열 반환.
```java
ToolCallback[] raw = ToolCallbacks.from(new MyTools());
ToolCallback[] safe = SsrfGuardedToolCallbacks.wrap(raw, urlPolicy);
```
자동 설정으로 — 모든 `@Bean ToolCallback`이 `BeanPostProcessor`로 자동 wrap.
## 설치
### Maven
```xml
kr.devslab
ssrf-guard
3.0.0
```
### Gradle (Kotlin DSL)
```kotlin
implementation("kr.devslab:ssrf-guard:3.0.0")
```
> **v2.0.0에서 업그레이드?** 메타 `kr.devslab:ssrf-guard:3.0.0`이 v2.0.0 API 유지 — `-core`, `-httpclient5`, `-restclient`를 transitive로 끌어옴. `kr.devslab.ssrfguard.security.*`를 직접 import한 코드는 업데이트 필요 — [v3.0.0 changelog](CHANGELOG.md#300--multi-module--llm-agent-ssrf-defense) 패키지 매핑 참고.
## 설정
```yaml
ssrf:
guard:
enabled: true # 마스터 스위치
allowed-schemes: [ "http", "https" ]
allowed-ports: [ -1, 80, 443 ] # -1 = 스킴 기본 포트
block-private-networks: true
reject-ip-literal-hosts: true # NEW v3.0.0 — http://127.0.0.1, http://2130706433 등 차단
reject-user-info: true # NEW v3.0.0 — https://user:pass@host/... 차단
follow-redirects: true
# 정확 일치 화이트리스트
exact-hosts:
- api.partner.com
- billing.example.org
# 서픽스 화이트리스트 — `partner.com`은 `partner.com` 및 그 서브도메인 매치,
# 단 `badpartner.com`은 매치 안 됨 (라벨 경계 매치).
suffixes:
- partner.com
- example.org
connect-timeout: 5s
read-timeout: 10s
```
스타터가 classpath에 있으면 Spring Boot가 만들어주는 모든 `RestClient`가 자동으로 정책 적용 — 사용자가 별도 와이어업 안 해도 됨.
## 사용
```java
@Service
public class PartnerApi {
private final RestClient client;
public PartnerApi(RestClient.Builder builder) {
this.client = builder.build();
}
public Customer fetch(long id) {
// 화이트리스트 호스트 → 통과. 리스트 밖이면 연결 열리기 전에
// SsrfGuardException 발생.
return client.get()
.uri("https://api.partner.com/customers/{id}", id)
.retrieve()
.body(Customer.class);
}
}
```
화이트리스트 아닐 때:
```text
kr.devslab.ssrfguard.core.SsrfGuardException: Host not allowed: evil.com
(reason=blocked_host, scheme=https, host=evil.com)
at kr.devslab.ssrfguard.core.UrlPolicy.reject(...)
```
`SsrfGuardException extends SecurityException` — v2.0.0의 `catch (SecurityException e)` 코드는 그대로 동작. 새 타입으로 catch하면 `e.reason()` (`BlockReason` enum) 접근 가능.
## 관찰성 (Micrometer 자동)
```
ssrf_guard_blocked_total{reason="blocked_private_ip", scheme="http"} 42
ssrf_guard_allowed_total{scheme="https"} 13042
```
차단마다 구조화된 WARN 로그:
```
WARN k.d.s.core.UrlPolicy : ssrf-guard: Host not allowed: evil.com (reason=blocked_host, scheme=https, host=evil.com)
```
태그가 bounded — Prometheus / Datadog / CloudWatch 문제 없음.
## 자동 구성이 등록하는 빈 (RestClient 모듈)
`ssrf.guard.enabled=true`(기본값)이면 RestClient 자동설정이 활성화되어 다음을 등록:
- `SafeDnsResolver` — 화이트리스트 + 사설 IP 필터. Apache HttpClient 5 connection manager에 연결
- `CloseableHttpClient` — resolver와 (리다이렉트 활성 시) `SafeRedirectStrategy` 와이어업
- `HttpComponentsClientHttpRequestFactory` — connect/read timeout 적용
- `UrlPolicy` — 최전선 URL 단계 게이트 (스킴, 호스트, 포트, IP 리터럴, userinfo)
- `SsrfGuardClientHttpRequestInterceptor` — Spring `ClientHttpRequestInterceptor`, 정책에 위임
- `SsrfGuardMetrics` — `MeterRegistry` 있으면 Micrometer 기반, 없으면 no-op
- `RestClientCustomizer` (`ssrfRestClientCustomizer`) — factory + interceptor를 Spring Boot 자동 `RestClient.Builder`에 핀
모듈마다 자기 자동설정 보유 — `SsrfGuardRestTemplateAutoConfiguration`, `SsrfGuardWebClientAutoConfiguration`, `SsrfGuardFeignAutoConfiguration`, `SsrfGuardSpringAiAutoConfiguration`. 동일 `UrlPolicy`와 `SsrfGuardMetrics` 빈 공유. 모든 빈이 `@ConditionalOnMissingBean` — 일부만 교체 가능.
## 요구사항
- Java 21+
- Spring Boot 3.5+ (Spring 기반 모듈)
- Spring AI 1.0+ (`springai` 모듈)
- Spring Cloud 2024.0+ (`feign` 모듈)
- Apache HttpClient 5 (`-httpclient5`, `-restclient`, `-resttemplate`이 transitive로 끌어옴)
## 라이선스
Apache License 2.0 — [LICENSE](LICENSE), [NOTICE](NOTICE) 참고.
---
[Devslab](https://devslab.kr) 제작 · [DevsLab 오픈소스 툴킷](https://github.com/devslab-kr) 일부.