노트

요청 제한 (Throttling)

Rate Limiting (Throttling)

백엔드#http#security · 연결된 개념 11개

쉽게 말하면

요청 제한은 ATM 하루 출금 한도처럼 한 클라이언트가 일정 시간에 보낼 수 있는 요청 수를 묶어 두는 거예요. 누가 로그인 시도를 마구 퍼붓거나 실수로 무한 재시도해도 서버가 버틸 수 있죠.

비유의 한계 출금 한도처럼 구간마다 깔끔히 초기화하는 고정 윈도는 경계 직전·직후에 두 배가 몰릴 수 있어요. 그래서 슬라이딩 윈도나 토큰 버킷을 쓰고, 서버가 여러 대면 카운터를 Redis로 공유해요.

요청 제한(rate limiting, throttling)은 일정 시간 동안 한 클라이언트가 보낼 수 있는 요청 수를 묶어 두는 장치다. 한도를 넘으면 429 Too Many Requests로 거절하고, 보통 Retry-After 헤더로 언제 다시 시도하면 되는지 알려 준다.

왜 필요한가

  • 무차별 대입(brute force) 로그인, 스크래핑, 실수로 생긴 무한 재시도로부터 서버와 다운스트림 자원을 보호한다
  • 사용자·요금제별로 공정하게 자원을 나눈다
  • 권한(permission)이 "할 수 있나"를 정한다면, 요청 제한은 "얼마나 자주"를 정한다(인증과 인가)

기준과 알고리즘

  • 누구를 셀까: 비로그인은 IP, 로그인은 사용자 ID, 외부 고객은 API 키
  • 고정 윈도(fixed window): 1분 단위로 카운터를 센다. 단순하지만 경계에서 순간적으로 두 배가 몰릴 수 있다
  • 슬라이딩 윈도(sliding window): 최근 N초를 기준으로 센다. 경계 문제를 줄인다(슬라이딩 윈도우)
  • 토큰 버킷(token bucket): 일정 속도로 토큰이 차고 요청마다 하나씩 쓴다. 짧은 버스트를 허용하면서 평균 속도를 제한한다

서버가 여러 대면 카운터를 공유해야 하므로 Redis (인메모리 저장소)의 INCR과 TTL로 구현하는 경우가 많다.

DRF에서

REST_FRAMEWORK = {
    "DEFAULT_THROTTLE_CLASSES": [
        "rest_framework.throttling.AnonRateThrottle",
        "rest_framework.throttling.UserRateThrottle",
    ],
    "DEFAULT_THROTTLE_RATES": {"anon": "100/day", "user": "1000/day", "review-create": "5/minute"},
}
 
class ReviewCreate(generics.CreateAPIView):
    throttle_classes = [ScopedRateThrottle]
    throttle_scope = "review-create"
  • AnonRateThrottle(IP 기준)·UserRateThrottle(사용자 ID 기준)은 API 전체에 걸쳐 클라이언트당 카운터 하나를 쓴다. 특정 엔드포인트만 따로 제한하려면 ScopedRateThrottle과 throttle_scope를 쓴다
  • DRF 문서도 말하듯 이것은 남용 방지 수준이지 강력한 보안 장치가 아니다. 공격 방어는 게이트웨이·WAF(Web Application Firewall) 수준에서 함께 한다

클라이언트 쪽에서 재시도할 때는 지수 백오프(exponential backoff)로 간격을 늘리고, 재시도되는 요청은 멱등하게 만든다.

출처: RFC 6585: 429 Too Many Requests · DRF Throttling · ScopedRateThrottle

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • 커넥션 드레이닝과 무중단 재시작

    배포 중 서버를 재시작하는 몇 초 동안 로드밸런서가 그 서버로 요청을 보내면 502가 난다. 먼저 로드밸런서에서 빼고(드레이닝), 진행 중인 요청을 마친 뒤 재시작하고, 준비되면 다시 넣는다.

  • RTT와 연결 준비 비용

    새 서버에 요청하려면 DNS 조회와 TCP·TLS 핸드셰이크를 먼저 거쳐야 해서, 첫 요청은 RTT 몇 번만큼 늦게 시작한다. 이미 만든 연결을 다시 쓰면 이 비용을 건너뛴다.

  • 롱 태스크

    메인 스레드를 50ms 넘게 붙잡는 작업. W3C Long Tasks 명세는 이벤트 루프 태스크(뒤따르는 마이크로태스크 처리 포함), 렌더링 갱신 단계, 이벤트 루프 밖에서 브라우저가 UI 스레드로 하는 일 가운데 50ms를 넘는 것을 롱 태스크로 본다. 태스크는 도중에 끊기지 않으므로 그동안 클릭·키 입력 처리와 화면 갱신이 모두 밀린다(이벤트 루프).

  • CSRF

    CSRF(Cross-Site Request Forgery, 사이트 간 요청 위조)는 로그인한 사용자가 악성 사이트를 방문했을 때, 그 사이트가 사용자의 브라우저를 시켜 사용자 모르게 원래 사이트에 요청을 보내게 하는 공격이다. 공격자는 토큰을 훔치지 않는다. 브라우저가 쿠키를 알아서 붙여 준다는 점을 이용한다.

  • DRF 파서·렌더러와 콘텐츠 협상

    DRF에서 파서(Parser)는 요청 본문을 파이썬 자료형으로 바꾸고, 렌더러(Renderer)는 응답 데이터를 클라이언트가 받을 형식으로 바꾼다. 어떤 렌더러를 쓸지는 요청의 Accept 헤더를 보고 고르는데, 이를 콘텐츠 협상(content negotiation)이라 한다.

보기 옵션