WAF : Request Header Filtering, User-Agent 제어방법

Request Header Filtering: User-Agent 제어

Request Header Filtering: User-Agent 제어 AtoZ

User-Agent는 클라이언트(브라우저, 모바일 앱, 검색엔진 봇 등)가 서버에 자신의 정체를 알리기 위해 보내는 요청 헤더(Request Header)의 한 종류입니다. 서버는 이 User-Agent 값을 보고 요청의 출처를 파악하여 다양한 제어를 수행할 수 있습니다.

이 글에서는 User-Agent를 제어하는 이유부터 구체적인 기술 구현 방법, 그리고 모범 사례까지 'A to Z' 형식으로 상세하게 설명합니다.

1. 왜 User-Agent를 제어해야 하는가? (The "Why")

User-Agent 필터링은 다음과 같은 다양한 목적으로 사용됩니다.

  • 악성 봇(Malicious Bot) 차단: 웹사이트의 콘텐츠를 무단으로 수집(Scraping)하거나, 취약점을 스캔하는 등 악의적인 활동을 하는 봇의 접근을 차단합니다.
  • 특정 크롤러(Crawler) 제어: Google, Naver 같은 검색엔진 봇의 접근은 허용하되, 원치 않는 검색엔진이나 마케팅 봇의 접근은 차단하여 불필요한 트래픽과 서버 부하를 줄입니다.
  • 서비스별 접근 제어: 특정 모바일 앱(예: MyAwesomeApp/1.0)에서만 API 접근을 허용하거나, 웹 브라우저와 앱의 요청을 구분하여 다른 콘텐츠를 제공할 수 있습니다.
  • 오래된 브라우저 지원 중단 안내: Internet Explorer 구 버전과 같이 보안에 취약하거나 특정 기능을 지원하지 않는 브라우저 사용자에게 업데이트를 권장하거나 서비스 이용이 제한됨을 안내합니다.
  • A/B 테스팅 및 통계 수집: 특정 브라우저나 기기 사용자 그룹에만 새로운 기능을 노출하거나, 사용자 환경 통계를 정교하게 수집하는 데 활용됩니다.

2. 제어 전략: 블랙리스트 vs. 화이트리스트

User-Agent를 제어하는 방식은 크게 두 가지 전략으로 나뉩니다.

전략 설명 장점 단점 추천 사용 사례
블랙리스트 (Blacklist) 차단할 특정 User-Agent 목록을 만들어 해당 요청을 거부하는 방식입니다. 구현이 간단하고, 정상적인 대부분의 사용자는 영향을 받지 않습니다. 새로운 악성 봇이 나타날 때마다 목록을 계속 업데이트해야 합니다. 일반적인 웹사이트에서 알려진 악성 봇이나 원치 않는 크롤러를 차단할 때
화이트리스트 (Whitelist) 허용할 특정 User-Agent 목록을 만들어, 그 외의 모든 요청을 거부하는 방식입니다. 매우 강력한 보안을 제공합니다. (Default-Deny) 허용 목록에 없는 정상적인 사용자(예: 신규 브라우저)의 접근을 차단할 수 있어 관리가 까다롭습니다. 특정 클라이언트(자사 앱 등)만 접근해야 하는 폐쇄적인 API 서버

일반적으로 블랙리스트 방식이 더 널리 사용되며, 두 가지를 혼합한 하이브리드 전략을 사용하기도 합니다.

3. 제어 방법: 구현 계층별 AtoZ (The "How")

User-Agent 필터링은 인프라의 여러 계층에서 구현할 수 있습니다. 앞단에서 처리할수록 애플리케이션 서버의 부하를 줄일 수 있어 효율적입니다.

A. 웹 서버 (Web Server)

가장 기본적인 제어 지점입니다. 웹 서버 설정을 통해 특정 User-Agent를 차단할 수 있습니다.

Nginx

map 지시어를 사용하는 것이 if문보다 성능상 권장됩니다. 특정 User-Agent를 변수($block_user_agent)로 매핑하고, 이 변수 값을 보고 접근을 차단합니다.

# nginx.conf

# 1. 차단할 User-Agent 목록을 map으로 정의 (정규표현식 사용 가능)
# ~* : 대소문자 구분 없는 정규표현식 매칭
map $http_user_agent $block_user_agent {
    default 0;
    ~* (AhrefsBot|SemrushBot|MJ12bot|DotBot) 1; # 알려진 마케팅/백링크 봇
    ~* (python-requests|curl|wget)             1; # 의심스러운 라이브러리/툴
    "BadBot/1.0"                               1; # 특정 문자열
}

server {
    listen 80;
    server_name yourdomain.com;

    # 2. map 변수 값이 1이면 403 Forbidden 에러 반환
    if ($block_user_agent) {
        return 403;
    }

    # ... 기타 설정 ...
}

Apache

.htaccess 파일과 mod_rewrite 모듈을 사용하여 구현합니다.

# .htaccess

# mod_rewrite 활성화
RewriteEngine On

# 차단할 User-Agent 조건을 설정 (OR 조건으로 여러 개 추가 가능)
RewriteCond %{HTTP_USER_AGENT} AhrefsBot [NC,OR]
RewriteCond %{HTTP_USER_AGENT} SemrushBot [NC,OR]
RewriteCond %{HTTP_USER_AGENT} "python-requests" [NC]

# 위 조건에 하나라도 해당하면 403 Forbidden 페이지로 보냄
# [F] = Forbidden (403), [L] = Last (이후 규칙 처리 중단)
RewriteRule .* - [F,L]
[NC] 플래그는 'No Case'를 의미하며, 대소문자를 구분하지 않습니다.

B. WAF (Web Application Firewall) / CDN

Cloudflare, AWS WAF, Akamai 같은 서비스를 이용하면 코드나 서버 설정을 직접 수정하지 않고도 강력한 필터링이 가능합니다.

  • 작동 방식: 서비스 대시보드에서 보안 규칙을 생성합니다.
    1. 규칙 조건으로 'Header'를 선택하고 헤더 이름으로 'User-Agent'를 지정합니다.
    2. 매칭 조건(포함, 일치, 정규표현식 등)과 차단할 User-Agent 문자열을 입력합니다.
    3. 해당 규칙에 매칭될 때 수행할 액션(예: Block, Challenge)을 설정합니다.
  • 장점:
    • 애플리케이션 서버에 도달하기 전에 차단하여 서버 부하가 전혀 없습니다.
    • 미리 정의된 '악성 봇 목록'을 제공하여 손쉽게 적용할 수 있습니다.
    • GUI 기반으로 설정이 간편합니다.

C. 애플리케이션 레벨 (Application Level)

웹 서버나 WAF에서 처리하기 힘든 복잡한 비즈니스 로직이 필요할 때 애플리케이션 코드 내에서 직접 제어합니다.

Java (Spring Boot - Filter)

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

@Component
public class UserAgentFilter implements Filter {

    private final List blockedUserAgents = Arrays.asList("BadBot", "python-requests");

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        String userAgent = httpRequest.getHeader("User-Agent");

        if (userAgent != null && blockedUserAgents.stream().anyMatch(userAgent::contains)) {
            httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");
            return;
        }

        chain.doFilter(request, response);
    }
}

Node.js (Express - Middleware)

const express = require('express');
const app = express();

const blockUserAgent = (req, res, next) => {
    const userAgent = req.get('User-Agent') || '';
    const blockedAgents = ['BadBot', 'python-requests'];

    const isBlocked = blockedAgents.some(agent => userAgent.includes(agent));

    if (isBlocked) {
        res.status(403).send('Forbidden');
    } else {
        next();
    }
};

// 미들웨어를 모든 라우트에 적용
app.use(blockUserAgent);

app.get('/', (req, res) => {
    res.send('Hello World!');
});

app.listen(3000);

4. 모범 사례 및 고려사항

  • 정규 표현식 최적화: 복잡하고 비효율적인 정규 표현식은 오히려 서버 성능을 저하시킬 수 있습니다. (ReDoS 공격에 유의)
  • 대소문자 처리: User-Agent 문자열은 대소문자가 다를 수 있으므로, 비교 시에는 항상 대소문자를 구분하지 않도록 처리하는 것이 안전합니다. (예: Nginx의 ~*, Apache의 [NC])
  • 오탐(False Positive) 최소화: 너무 광범위한 키워드(예: 'bot')로 차단하면 Googlebot 같은 정상적인 봇이나 일부 브라우저까지 차단할 수 있습니다. 최대한 구체적인 문자열로 차단하고, 항상 테스트를 거쳐야 합니다.
  • 로깅(Logging)은 필수: 어떤 User-Agent가 차단되었는지 로그를 남겨야 합니다. 이를 통해 오탐 여부를 분석하고 차단 목록을 지속적으로 개선할 수 있습니다.
  • 지속적인 업데이트: 악성 봇의 User-Agent는 계속해서 변화하고 우회 기술도 발전하므로, 차단 목록은 정기적으로 검토하고 업데이트해야 합니다.
  • User-Agent는 절대적인 보안 수단이 아님: User-Agent 헤더는 클라이언트가 쉽게 변조할 수 있습니다. 따라서 User-Agent 필터링은 IP 기반 차단, Rate Limiting(요청 빈도 제한) 등 다른 보안 장치와 함께 계층적 방어(Defense in Depth)의 일부로 사용해야 합니다.

5. 결론

User-Agent 필터링은 서비스의 안정성을 높이고 보안을 강화하며, 특정 사용자 그룹에 맞춤형 경험을 제공하는 효과적인 수단입니다.

제어는 WAF/CDN > 웹 서버 > 애플리케이션 순으로 앞단에서 처리하는 것이 효율적입니다. 서비스의 규모, 보안 요구사항, 개발 리소스 등을 종합적으로 고려하여 가장 적합한 계층에서, 올바른 전략(블랙리스트/화이트리스트)으로 구현하는 것이 중요합니다. 항상 로깅과 모니터링을 통해 규칙을 지속적으로 개선해 나가는 노력이 동반되어야 합니다.