1단계: robots.txt 설정 (기본 권고)
User-agent: *
Disallow: /private/
- 모든 크롤러(
*)에 대해 /private/ 경로 접근 금지
- 하지만 법적 강제성은 없음
→ 크롤러가 이 파일을 무시하면 막을 수 없습니다
2단계: User-Agent 확인 및 차단
String userAgent = request.getHeader("User-Agent");
if (userAgent != null && userAgent.contains("Jsoup")) {
response.sendError(403, "Forbidden");
}
- Jsoup, python-requests, curl, bot 등 키워드를 필터링
- 예외적으로 정상 브라우저 User-Agent만 허용하는 정책 가능
3단계: IP 기반 접근 제한
- 방문 횟수/속도 감지 후, 과도한 요청 IP를 차단
- Geo-IP 차단도 가능 (해외 IP 차단 등)
예시
- 10초에 20건 이상 → 임시 차단
- Cloudflare, AWS WAF, Nginx rate limit 등 활용
4단계: 로그인/세션 기반 컨텐츠 보호
- 민감한 정보는 로그인 후에만 제공
- 세션 토큰 없으면 401 / 403 반환
- JWT 또는 OAuth2 인증 강화
5단계: JavaScript 렌더링 사용 (동적 콘텐츠)
- 데이터 로딩을 JavaScript로 수행 (예: Vue, React, Ajax)
- 단순 HTML 파서로는 정보 접근 불가 (Jsoup이나 Python BeautifulSoup 차단 가능)
대가
6단계: CAPTCHA 적용
- 로그인, 댓글 등 민감 경로에 Google reCAPTCHA v2/v3 적용
- 봇 자동화 요청을 완전히 차단할 수 있는 방법 중 하나
7단계: 동적 URL/의도적 난독화
- 페이지마다 URL을 자주 바꾸거나, 데이터를 암호화
- DOM 구조를 자주 바꿔서 크롤러가 구조를 예측하지 못하게 함
8단계: 방어 툴 or 서비스 사용
| 서비스 | 설명 |
|---|
| Cloudflare Bot Management | 자동화 탐지 및 방어 |
| AWS WAF | Web ACL로 접근 제어 |
| Imperva, Akamai | 상업용 보안 서비스 |
사람은 허용, 봇은 차단하려면?
- 마우스 이벤트/스크롤 이벤트 감지
- 헤더 정보, referrer, 쿠키 값 등을 기반으로 판단
- 행위 분석 (Behavior Analysis) 도입
정리: 크롤링 방지 레벨별 전략
| 레벨 | 방법 | 특징 |
|---|
| 1단계 | robots.txt | 약한 제어 (권고 수준) |
| 2단계 | User-Agent 차단 | 단순 봇 차단 |
| 3단계 | IP 제한 | 과도한 요청 방어 |
| 4단계 | 인증 요구 | 세션 없이 차단 가능 |
| 5단계 | JavaScript 렌더링 | 파싱 어렵게 함 |
| 6단계 | CAPTCHA | 인간 인증 |
| 7단계 | 구조 난독화 | 파서 우회 어려움 |
| 8단계 | 보안 서비스 | 고도화된 종합 방어 |
Spring Boot에서 User-Agent/IP 필터 만드는 방법
목표
- 의심스러운 User-Agent 차단 (예: Jsoup, curl, bot 등)
- 요청자의 IP 기반으로 차단 or 로깅
- 차단 시:
403 Forbidden 응답
방법 선택
| 방법 | 설명 | 사용 상황 |
|---|
Filter | 모든 요청 전처리 | 빠르고 낮은 수준에서 처리 |
HandlerInterceptor | 컨트롤러 진입 직전에 처리 | Spring MVC와 더 친화적 |
UserAgent & IP 필터 구현 (Filter 방식)
package com.example.demo.filter;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
@Component
public class RequestFilter implements Filter {
private final List<String> blockedAgents = Arrays.asList("jsoup", "curl", "python", "bot");
private final List<String> blockedIps = Arrays.asList("192.168.0.100", "203.0.113.42");
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String userAgent = req.getHeader("User-Agent");
String ip = getClientIp(req);
if (userAgent != null) {
for (String blocked : blockedAgents) {
if (userAgent.toLowerCase().contains(blocked)) {
res.sendError(HttpServletResponse.SC_FORBIDDEN, "Blocked User-Agent");
return;
}
}
}
if (blockedIps.contains(ip)) {
res.sendError(HttpServletResponse.SC_FORBIDDEN, "Blocked IP");
return;
}
chain.doFilter(request, response);
}
private String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
return ip.split(",")[0];
}
return request.getRemoteAddr();
}
}
| 조건 | 결과 |
|---|
User-Agent: Jsoup/1.15 | 403 Forbidden |
IP: 203.0.113.42 | 403 Forbidden |
| 정상 브라우저 | 통과 (200 OK) |
테스트
curl -H "User-Agent: Jsoup/1.15" http://localhost:8080
curl -H "User-Agent: Mozilla/5.0" http://localhost:8080
팁
- IP 차단을 동적으로 하고 싶다면
- DB나 Redis에 저장된 블랙리스트에서 IP 조회
- 차단 횟수를 기록해서 자동 차단 (RateLimiter, Redis 카운터 등)
Cloudflare, WAF 설정 예시
전제: 사이트가 Cloudflare로 DNS 설정되어 있어야 함 (CNAME, A, Proxy Enabled (orange cloud))
WAF 기능 개요 (Cloudflare 기준)
| 기능 | 설명 |
|---|
| Managed Rules | OWASP Top 10 공격 자동 차단 (SQLi, XSS 등) |
| Custom Rules | IP, User-Agent, Path 등 조건 지정 차단 |
| Rate Limiting | 요청 속도 제한 |
| Bot Management | 정교한 봇 탐지 및 CAPTCHA 처리 |
| Geo Blocking | 국가 단위 IP 차단 가능 |
Custom WAF Rule 설정 예시
목표
- Jsoup, curl, Python 등 User-Agent 차단
/api/secret/* 같은 민감 경로 접근 제한
Cloudflare Dashboard > Security > WAF > Custom Rules > Create Rule
설정 예시 1: User-Agent 차단
| 항목 | 값 |
|---|
| Rule name | Block Jsoup and curl |
| When incoming requests match... | |
(http.user_agent contains "jsoup") or
(http.user_agent contains "curl") or
(http.user_agent contains "python")
설정 예시 2: 특정 경로 접근 차단 (봇 의심 포함)
| 항목 | 값 |
|---|
| Rule name | Protect sensitive API |
| Expression | |
(http.request.uri.path contains "/api/secret") and
(not cf.client.bot)
| Then... | Managed Challenge or Block |
|---|
Rate Limiting 설정 예시
목표
/login 엔드포인트에 1분에 5회 이상 접근 시 차단
Cloudflare Dashboard → Security → Rate Limiting → Create Rule
| 항목 | 값 |
|---|
| Rule name | Login Brute Force Protection |
| Request URL | *yourdomain.com/login* |
| Method | POST |
| Threshold | 5 requests per 1 minute |
| Action | Challenge or Block |
Geo IP 차단 (국가별)
특정 국가에서만 허용하거나 차단
not ip.geoip.country in {"KR" "JP" "US"}
→ 위 외 국가에서 접근 시 차단
Bot Management 설정
Bot Score (1~100): 낮을수록 비정상 트래픽
예: 봇 점수 낮고 로그인 시도 시 차단
(http.request.uri.path contains "/login") and
(cf.bot_management.score < 30)
팁
| 항목 | 설명 |
|---|
| cf.client.bot | Cloudflare가 알려진 봇(Googlebot 등)인지 여부 |
| cf.threat_score | IP 신뢰도 점수 기반 필터링 |
| ip.src in $bad_ips | 사용자 정의 IP 리스트 활용 |
http.referer / uri.query | 리퍼러 및 쿼리 파라미터 기반 제한 가능 |
예시 정리
| 목적 | Cloudflare WAF 규칙 |
|---|
| Jsoup 차단 | http.user_agent contains "jsoup" |
| API 보호 | uri.path contains "/api" and not cf.client.bot |
| 국가 제한 | not ip.geoip.country in {"KR"} |
| 로그인 속도 제한 | Rate Limiting + POST + /login |
| 자동화 차단 | cf.bot_management.score < 30 |
기타 설정 권장
- Bot Fight Mode: 클라우드플레어 무료 플랜에서도 제공
- Security Level: High: 악성 IP 차단 강화
- JavaScript Challenge: 봇에게는 난해한 미니 JS 퍼즐 요구
정리
| 항목 | 용도 |
|---|
| WAF Rule | 사용자 정의 필터, 크롤링/SQLi 등 방어 |
| Rate Limit | 특정 경로/속도 제한 |
| Bot Management | 봇 탐지 후 자동 처리 |
| Geo IP Rule | 국가 단위 차단 |
| Managed Rules | OWASP 공격 사전 방어 |