요약

  • HTTP 431은 요청 콘텐츠가 아닌 요청 필드 섹션이 과대한 경우를 구분하지만, HTTP는 모든 구현과 모든 홉에 적용되는 단일 최대 필드 크기를 정의하지 않는다.
  • 이 거부는 필드 집합 전체 또는 문제의 특정 필드를 가리킬 수 있으며, 축소 후 재제출이 가능할 수 있지만 맥락을 무분별하게 삭제하면 인증, 라우팅, 조건 또는 의미가 달라질 수 있다.

콘텐츠보다 먼저 도착한 거부

클라이언트가 소박한 작업을 보낸다. 콘텐츠는 아주 작거나 아예 없다. 애플리케이션이 이 작업을 보기도 전에 게이트웨이가 이를 거부한다. 요청에는 긴 쿠키, 베어러 자격 증명, 여러 트레이싱 값, 표현 기본 설정, 전달 맥락이 누적되어 있다. 개별 필드가 특별히 과대해 보이지 않을 수도 있다. 합쳐서 어떤 수신자가 선택한 한도를 넘는 것이다.

바로 이 상태를 RFC 6585가 2012년431 Request Header Fields Too Large로 명명했다. 이 상태는 일반적인 400보다 정밀하다. 수신자는 요청의 필드가 너무 커서 처리를 원하지 않으며, 클라이언트는 축소 후 다시 시도할 수 있다.

“필드(fields)”라는 단어가 중요하다. RFC 9110은 서버가 처리하려는 수준을 초과하는 요청 콘텐츠를 위한 413을 정의한다. 431은 그 콘텐츠가 시작되기 전에 발생할 수 있다. 거부되는 것은 중개자와 오리진에게 작업을 어떻게 해석하고, 인가하고, 라우팅할지 알려 주는 제어 봉투(envelope)다.

HTTP는 답을 표준화했지, 상한을 표준화하지 않았다

RFC 9110은 하나의 필드 줄, 하나의 필드 값, 또는 요청 필드 섹션 전체에 대해 미리 정의된 한도를 의도적으로 두지 않는다. 그럼에도 실제 구현에는 유한한 메모리, 파싱 시간, 방어적 경계가 필요하기 때문에 수신자가 수용 범위를 스스로 정한다.

그 결과는 하나의 한도가 아니라 한도의 사슬이다. 클라이언트 라이브러리가 요청을 구성할 수 있다. 로컬 프록시가 이를 수용할 수 있다. 에지 게이트웨이가 이를 거부할 수 있다. 다른 배포 경로에서는 오리진까지 도달하고, 거기서는 다른 경계가 적용된다. 그러므로 같은 바이트가 한 경로에서는 통과하고 다른 경로에서는 실패할 수 있는데, 이는 어느 수신자도 보편적인 HTTP 숫자를 위반하지 않기 때문이다. 그런 숫자 자체가 존재하지 않기 때문이다.

431은 이러한 개별 예산들에 공용 언어를 부여한다. 다만 응답 경로와 운영 증거가 특정 구성 요소를 밝혀 주지 않는 한, 어느 구성 요소가 가장 작은 예산을 선택했는지는 알려 주지 않는다. 또한 가장 작은 한도를 올리는 것이 자동으로 안전하다는 것을 증명하지도 않는다.

하나의 필드와 전체 섹션은 다른 진단이다

RFC 6585는 두 가지 사용을 허용한다. 요청 필드 집합 전체가 과대할 수도 있고, 하나의 필드가 원인일 수도 있다. 특정 필드가 원인일 때, 응답 표현은 어느 필드였는지 명시해야 한다.

이 구분은 복구 방식을 바꾼다. 단일 병적 필드라면 그 의미가 허용하는 경우 축소, 교체(rotate) 또는 생략할 수 있다. 반면 합산 초과는 서로 다른 계층이 소유한 수십 개의 개별적으로 유효한 추가로 생길 수 있다. 가장 큰 필드를 제거해도 다음 홉의 한도를 여전히 넘을 수 있다. 필드 하나를 지목하는 것은 진단이지, 이제 다른 모든 예산이 맞아 들어간다는 증명이 아니다.

또한 필드의 식별은 성장을 누가 초래했는지를 결정하지 않는다. 쿠키는 수많은 교환에 걸쳐 기록된 상태를 모을 수 있다. 전달 필드는 중개자가 확장할 수 있다. 자격 증명이 교체되면 인증 자료가 바뀔 수 있다. 트레이싱 시스템은 호출 애플리케이션에 보이지 않게 맥락을 덧붙일 수 있다. 수신자가 보는 것은 필드 섹션이며, 책임은 경로 전반에 분산되어 있을 수 있다.

무음 잘라내기(truncation)가 안전한 복구가 아닌 이유

RFC 9110은 요청 필드가 서버가 처리하려는 수준을 초과할 때 적절한 4xx 응답을 요구한다. 그리고 과대한 요청 필드를 무시하면 요청 스머글링(request smuggling) 노출이 커진다고 경고한다.

그 이유는 기계적이기 이전에 의미적이다. 필드는 자격 증명, 전제 조건, 라우팅 권한, 메시지 해석을 담을 수 있다. 어떤 참여자는 필드를 무시하고 다른 참여자는 그에 따라 행동하면 하나의 요청에 두 가지 이해가 생길 수 있다. 편리한 잘라내기는 조건부 쓰기를 무조건부로 바꾸고, 자격 증명을 없애며, 메시지 경계와 제어 데이터에 대해 두 홉이 불일치하게 남길 수 있다.

RFC 9110은 또한 서버가 요청을 적용하기 전에 요청 헤더 섹션 전체를 수신할 것을 요구한다. 뒤에 오는 필드에는 조건, 인증 자격 증명 또는 의도적으로 오도적인 중복이 있을 수 있다. 조기에 애플리케이션이 결정을 내리면, 아직 도착하지 않은 꼬리 부분이 이미 수행된 작업의 권한을 바꿀 수 있다.

안전한 분기는 명시적 거부 뒤에 이어지는 신중한 재구성이다. 클라이언트 또는 소유 애플리케이션이 어느 상태를 압축하고, 갱신하고, 버릴 수 있는지 결정한다. 수신자가 입력을 조용히 삭제함으로써 그 인가를 스스로 만들어서는 안 된다.

재제출은 허가이지, 약속이 아니다

RFC 6585는 필드를 축소하면 요청을 재제출할 수 있다고 말한다. 이는 가능성이지 보장이 아니다. 다른 중개자가 더 낮은 한도를 가질 수 있다. 같은 필드가 필수일 수 있다. 요청이 이미 낡았을 수 있다. 효과가 있는 메서드에서는 전송 실패가 이전 시도가 실제로 작용했는지에 대한 불확실성을 남길 수 있다.

건전한 재시도는 권위 있는 상태에서 작업을 재구성한다. 필수 자격 증명과 조건을 보존하고, 애플리케이션이 지원하는 곳에 멱등성 메커니즘을 부여하며, 작업이 여전히 유용한지 검증한다. 손상된 요청을 더 빨리 반복한다고 안전해지지는 않는다.

특정 수신자의 판정이 캐시된 법칙이 되어서는 안 된다

RFC 6585는 캐시가 431 응답을 저장하는 것을 금지한다. 이 거부는 특정 요청 필드 섹션, 경로, 수신자 예산에 귀속된다. 이를 나중에 재생하면 축소된 요청을 거부하고, 일시적 구성 상태를 보존하며, 한 홉의 경계를 다른 경로에 투영할 수 있다.

새로운 수신자는 자신의 현재 증거에 기반하여 새로운 431을 발행할 수 있다. 캐시는 어제의 파싱 결정을 재사용 가능한 콘텐츠로 바꿀 수 없다.

공격 중에는 설명이 봉쇄에 양보할 수 있다

서버가 431을 생성할 의무는 없다. RFC 6585는 공격 중에는 연결을 끊거나 다른 조치를 취하는 쪽이 더 적절할 수 있다고 밝힌다. 상세한 응답을 구성할 만큼 입력을 파싱하는 것 자체가 그 한도가 지키려는 메모리와 CPU를 소모하며, 정확한 임계값을 알려 주면 공격자에게 지도를 개선해 줄 수 있다.

여기서 단계적 권한이 생긴다. 자원이 허용될 때는 정밀한 응답이 정당한 클라이언트의 요청 복구를 돕는다. 적대적 압박 아래에서는 수신자가 더 적은 설명으로 더 일찍 멈출 수 있다. 다만 연결 종료만으로는 필드 크기가 원인이었다는 증명이 되지 않으며, 관측 체계는 내부적으로 이 구분을 보존해야 한다.

431의 좁은 진실

이 상태 코드는 요청 본문이 크다는 것, 인터넷 전역의 어떤 최대치가 넘어섰다는 것, 클라이언트가 악의적으로 행동했다는 것을 말하지 않는다. 말하는 것은 하나의 수신자가 제어 봉투가 자신이 처리할 의사가 있는 수준을 초과했기 때문에 요청을 거절했다는 것뿐이다.

이 좁은 진실은 표준화할 가치가 있었다. 감춰진 구현 한도를 복구 가능하게 만들면서 한도 자체는 국지적으로 유지했기 때문이다. 오래 지속되는 교훈은 모든 헤더를 더 작게 만드는 것이 아니라, 제어 맥락을 한도 내에 두고, 소유권을 명확히 하고, 재구성 가능하게 유지하는 것이다. 그리고 절대로 봉투를 맞추기 위해 권한을 조용히 제거하지 않는 것이다.

출처