Copyright © 2025 World Wide Web Consortium. W3C® liability, trademark and permissive document license rules apply.
이 문서는 개발자가 웹 애플리케이션에 대한 네트워크 오류 보고 정책을 선언할 수 있는 메커니즘을 정의합니다. 사용자 에이전트는 이 정책을 사용하여 요청한 리소스를 성공적으로 가져오지 못하게 한 네트워크 오류를 보고할 수 있습니다.
이 절은 문서가 발행된 시점의 상태를 설명합니다. 현재 W3C 발행 목록과 이 기술 보고서의 최신 버전은 W3C 표준 및 초안 색인 https://www.w3.org/TR/ 에서 확인할 수 있습니다.
이 문서는 Web Performance Working Group에서 권고안 절차를 이용하여 Working Draft로 발행한 것입니다.
Working Draft로서의 발행은 W3C 및 그 회원사의 승인을 의미하지 않습니다.
이 문서는 초안으로, 언제든지 갱신, 대체 또는 폐기될 수 있습니다. 해당 문서는 작업 중인 자료 외에 인용하는 것이 적절하지 않습니다.
이 문서는 W3C 특허 정책에 따라 운영되는 그룹이 작성하였습니다. W3C는 그룹 산출물과 관련하여 공개 특허 고지 목록을 관리합니다. 해당 페이지에는 특허 고지 방법에 대한 안내도 있습니다. 특정 특허에 대해 실질적인 지식이 있는 개인은, 그 특허에 필수 청구항이 포함되어 있다고 판단될 경우, W3C 특허 정책 6조에 따라 정보를 공개해야 합니다.
이 문서는 2023년 11월 3일 W3C 프로세스 문서에 의해 관리됩니다.
웹 애플리케이션의 성능 특성을 정확하게 측정하는 것은 사이트 개발자가 웹 애플리케이션을 어떻게 개선할 수 있을지 이해하는 데 중요한 요소입니다. 최악의 경우는 네트워크 오류로 인해 애플리케이션 또는 특정 리소스가 로드되지 않는 상황이며, 이러한 실패를 해결하기 위해 개발자는 언제, 어디서, 왜 이러한 실패가 발생하는지 사용자 에이전트의 지원이 필요합니다.
현재는 응용 프로그램 개발자가 엔드유저로부터 실시간 웹 애플리케이션 가용성 데이터를 확보할 수 없습니다. 예를 들어, 사용자가 DNS 조회 실패, 연결 시간 초과, 연결 리셋 등 네트워크 오류로 인해 페이지 로드에 실패한 경우, 사이트 개발자는 이를 탐지하거나 해결할 수 없습니다. 이러한 네트워크 오류는 서버 측에서만으로는 감지할 수 없는데, 클라이언트가 정의상 서버에 성공적으로 연결을 수립하지 못했을 수 있기 때문입니다.
기존 방식(예: synthetic monitoring)은 미리 정해진 지리적 위치에 모니터링 노드를 배치함으로써 부분 해결책을 제공하지만, 추가 인프라 투자가 필요하고, 실제 최종 사용자를 위한 진정한 글로벌 및 거의 실시간의 가용성 데이터를 제공하지는 못합니다.
Network Error Logging(NEL)은 웹 애플리케이션이 특정 오리진의 네트워크 오류를 사용자 에이전트가 보고할 수 있도록 보고 정책을 선언하는 메커니즘을 정의함으로써 이 문제를
해결합니다. 웹 애플리케이션은 원하는 NEL HTTP 응답 헤더 필드를 제공함으로써 NEL을 사용하도록 선택할 수 있습니다. 이 정책은 사용자
에이전트에게 해당 오리진에 대한 요청의 정보를 기록하고, 이전에 Reporting API를 통해 구성된 엔드포인트 그룹에 정보를 전달하도록
지시합니다. 이름에서 알 수 있듯, NEL 보고서는 주로 오류를 설명하는 데 사용됩니다. 하지만, 다양한 클라이언트 집단에서 오류 비율을 확인하려면
성공한 요청 수 또한 알아야 합니다. 이러한 성공 요청 역시 NEL 메커니즘을 통해 보고할 수 있습니다.
예를 들어, 사용자 에이전트가 https://www.example.com에서 TCP 연결이 중단되어 리소스 가져오기에 실패한 경우, 사용자 에이전트는 Reporting
API를 통해 다음과 같은 보고서를 큐에 추가합니다:
"network-error"report_to 필드로 구성된 엔드포인트 그룹{
"referrer": "https://referrer.com/",
"sampling_fraction": 1.0,
"server_ip": "192.0.2.42",
"protocol": "http/1.1",
"elapsed_time": 321,
"phase": "connection",
"type": "tcp.aborted"
}
보고서에 포함되는 필드와 형식에 대한 설명은 5.4 네트워크 오류 보고서 생성에서 확인할 수 있으며, NEL 등록 및 보고 절차에 대한 실제 예시는 7. 예시를 참고하세요.
비표준으로 표시된 섹션뿐만 아니라, 본 명세의 모든 작성 가이드라인, 다이어그램, 예제, 주석 등은 비표준입니다. 이 명세서의 나머지 부분은 표준입니다.
본 문서에서 MAY, MUST, MUST NOT, OPTIONAL, REQUIRED, SHOULD 등은 BCP 14 [RFC2119] [RFC8174]에서 설명한 대로, 오직 이와 같이 대문자로 등장하는 경우에만 해당 의미로 해석되어야 합니다.
알고리즘의 일부로 명령문 형태("선행 공백 문자를 모두 제거한다" 또는 "false를 반환하고 이 단계를 중단한다" 등)로 기술된 요구 사항은, 알고리즘 도입 시 사용된 키워드("must", "should", "may" 등)의 의미로 해석해야 합니다.
어트리뷰트, 메서드, 오브젝트에 대한 요구 사항은 사용자 에이전트에 대한 요구 사항으로 해석해야 합니다.
알고리즘이나 구체적인 단계로 표기된 적합성 요구 조건은 최종 결과가 동등하다면 어떤 방식으로든 구현할 수 있습니다. (특히 본 명세에 정의된 알고리즘은 따라가기 쉽게 작성된 것이며, 성능을 의도한 것은 아닙니다.)
네트워크 요청은 사용자 에이전트가 특정 HTTP-네트워크 fetch를 통해 네트워크 상의 리소스를 요청(request) 하려고 시도할 때 발생합니다.
request는 사용자 에이전트가 오프라인임이 확인될 때(즉, navigator.
onLine이
false를 반환할 때) 네트워크 요청을 발생시키면 안 됩니다(MUST NOT).
request는 혼합 콘텐츠(mixed content)나 CORS 실패로 인해 차단된 경우 네트워크 요청을 발생시키면 안 됩니다(MUST NOT). 모든 CORS-사전요청(preflight request)은 반드시 별도의 네트워크 요청을 발생시켜야 합니다(MUST).
[FETCH] 표준에 따라 요청을 처리하는 사용자 에이전트에서 네트워크 요청은 HTTP-네트워크 fetch 알고리즘이 한 번 실행되는 것에 해당합니다.
어떤 fetch 알고리즘, 어떤 하위 애플리케이션 및 전송 프로토콜을 사용하든, 네트워크 요청의 처리는 다음의 단계로 이루어집니다:
단계 중 필수적인 것은 요청 및 응답 전송 단계뿐이며, 나머지 단계들은 모든 네트워크 요청에서 필요하지 않을 수도 있습니다. 예를 들어, DNS 결과를 사용자 에이전트에 로컬로 캐시하면 동일 도메인에 대한 이후 요청에서 DNS 해석 단계를 생략할 수 있습니다. 마찬가지로, HTTP 지속 연결(persistent connections)을 활용하면 동일 네트워크 파티션 키에 대해 여러 요청이 하나의 연결을 공유할 수 있습니다. 그러나 여러 단계가 발생하는 경우, 위에 명시된 순서로 진행됩니다.
네트워크 요청은 사용자 에이전트가 서버로부터 유효한 HTTP 응답을 받고, 그 응답이 4xx 또는 5xx 상태 코드를 포함하지 않을 때 성공한 것으로 간주됩니다.
네트워크 오류는 네트워크 요청이 실패하게 만든 오류 상태입니다.
각 네트워크 오류는 문자열로 된 유형(type)을 가집니다.
각 네트워크 오류는 단계(phase)를 가지며, 이는 해당 오류가 어느 단계에서 발생했는지를 나타냅니다:
dnsconnectionapplication여러 사전 정의된 네트워크 오류 유형은 6. 사전 정의된 네트워크 오류 유형에 정의되어 있습니다.
네트워크 오류 보고서는 Reporting API의 보고로, 네트워크 오류를 설명합니다.
네트워크 오류 보고서는 리포트
유형(report type)이
network-error입니다.
네트워크 오류 보고서는 절대 ReportingObserver에게 보이지 않습니다.
네트워크 오류 보고서가 ReportingObserver에 노출되지 않는 것은, 해당 보고서가 요청을 수신하는 서버의 소유자 또는 관리자에게만 노출되도록 의도되었기 때문입니다. 만약 ReportingObserver에 노출되면, 보고서가 요청자(originator)에게도 노출되며, 교차 오리진 요청의 경우 서버의 네트워크 구성 정보가 외부로 유출될 수 있습니다.
NEL 정책은 사용자 에이전트에게 특정 네트워크 요청을 오리진에 대해 보고를 수집할지, 그리고 보고서를 어디로 전송할지를 지시합니다. NEL 정책은 HTTP 응답 헤더(response headers)를 통해 사용자 에이전트로 전달됩니다.
각 NEL 정책은 수신 IP 주소를 가지며, 이는 사용자 에이전트가 이 NEL 정책을 받은 서버의 IP 주소입니다.
각 NEL 정책은 오리진을 가집니다.
각 NEL 정책은 하위 도메인(flag)을 가지며, 이 값은
include 또는 exclude입니다.
각 NEL 정책은 요청 헤더 리스트와 응답 헤더 리스트를 가지며, 각각 헤더 이름의 목록입니다.
각 NEL 정책은 리포팅 그룹을 가지며, 이는 본 정책에 대한 보고가 전송될 리포팅 엔드포인트 그룹의 이름입니다.
각 NEL 정책은 정책이 유효한 초 단위의 ttl 값을 가집니다.
각 NEL 정책은 사용자 에이전트가 정책을 수신한 시점을 나타내는 생성 시각(creation)을 가집니다.
NEL 정책은, 생성 시점부터 생성 시각에서 월 시계(wall clock)의 허용되지 않은 현재 시간까지 172800초(48시간)보다 크면 만료(stale)된 것으로 간주합니다.
NEL 정책은, 생성 시각부터 생성 시각에서 월 시계의 허용되지 않은 현재 시간까지 ttl (초 단위)보다 클 때 만료(expired)된 것으로 간주합니다.
많은 트래픽이 기대되는 오리진은 모든 네트워크 요청에 대해 NEL 보고서를 받아들일 여력이 없을 수 있습니다. 오리진은 각 사용자 에이전트가 제출하는 NEL 보고서 수를 제한하기 위해 샘플링 비율을 정의할 수 있습니다. 일반적으로 성공 요청이 실패 요청보다 훨씬 많으므로, 오리진은 각각에 대해 다른 샘플링 비율을 지정할 수 있습니다.
각 NEL 정책은 0.0~1.0 범위의 성공 샘플링 비율을 가집니다.
각 NEL 정책은 0.0~1.0 범위의 실패 샘플링 비율을 가집니다.
적합한 사용자 에이전트는 반드시(policy cache 제공, MUST) 정책 캐시를 제공해야 하며, 이는 (네트워크 파티션 키, 오리진) 튜플을 키로 하여 NEL 정책 세트를 유지하는 저장 메커니즘입니다.
이 저장 메커니즘은 불투명, 벤더별, 웹에 노출되지 않지만, 본 명세에 정의된 알고리즘에서 사용할 다음의 메서드를 반드시(MUST) 제공합니다:
서버는 자신이 제어하는 오리진에 대해
NEL
정책을
NEL
HTTP 응답 헤더를 통해 정의할 수 있습니다(MAY).
NEL 응답 헤더는 오리진의 NEL 정책을 사용자 에이전트에게
전달하는 데 사용됩니다.
ABNF(Augmented Backus-Naur Form) [RFC5234] 문법에서 NEL
헤더의 구문은 다음과 같습니다:
NEL = json-field-value
헤더 값은 json-field-value에서 정의된 대로 JSON 객체들의 배열로 해석됩니다. 배열의 각 객체는 오리진에 대한 NEL 정책을 정의합니다. 사용자 에이전트는 배열에서 첫 번째로 유효한 정책만 반드시(MUST) 처리하고, 배열의 추가 정책은 무시합니다.
사용자 에이전트는 본 명세에 정의된 문법에 맞지 않는 알 수 없거나 잘못된 필드나 값은 반드시(MUST) 무시해야 합니다. 유효한 NEL
헤더는 최소한 이 명세에서 정의된 모든 "REQUIRED" 필드를 포함한 객체를 하나 이상 가져야 합니다(MUST).
사용자 에이전트는 오류 보고의 하이재킹을 방지하기 위해 meta 요소를 통한 NEL 헤더를 반드시(MUST) 무시해야 합니다. NEL 정책은 반드시 NEL 응답 헤더로 전달되어야 합니다.
meta 요소에 대한 제한은 [CSP] 명세와 일치하며, 동일한 이유로 보고
등록이 오직 HTTP 헤더 필드에서만 가능하게 합니다.
report_to 멤버는 이 엔드포인트 그룹에 NEL 정책의 보고가 전송될
대상을 지정합니다.
report_to 멤버는 NEL 정책 등록 시 필수(REQUIRED)이며,
이전 등록 제거 목적일 경우 선택적(OPTIONAL)입니다. – max_age 참고.
만약 존재한다면, 값은 반드시(MUST) 문자열이어야 하며, 다른 타입이면 파싱 오류가 발생합니다.
필수(REQUIRED) max_age 멤버는 이 NEL 정책의 유효기간을 초
단위의 0 이상의 정수로 지정합니다. 값은 반드시(MUST) 0 이상의 정수여야 하며, 다른 타입이면 파싱 오류가 발생합니다.
선택(OPTIONAL) include_subdomains 멤버는
이 오리진의 모든 하위 도메인(깊이 제한 없음)에 해당 NEL 정책을 활성화할지 여부를 결정하는 불리언 값입니다.
객체에 include_subdomains란 멤버가 없거나 값이 true가 아니면,
NEL 정책이 하위 도메인에 대해 활성화되지 않습니다.
하위 도메인에 대한 NEL 보고서를 보장하려면, 애플리케이션은 리포팅 엔드포인트 그룹도 include_subdomains를
활성화하여
설정해야 합니다. 리포팅 정책에 이 항목이 없고, 해당 하위 도메인에 대한 별도 리포팅 정책이 없다면,
NEL 정책에 하위 도메인이 포함되어 있더라도 보고서는 전달되지 않습니다.
선택(OPTIONAL) success_fraction 멤버는
이 오리진에 대한 성공 네트워크 요청 보고에 적용할
샘플링 비율을 정의합니다.
값이 있을 경우 반드시(MUST) 0.0~1.0 사이의 수여야 하며, 다른 값이면 파싱 오류입니다.
이 멤버가 없으면 사용자 에이전트는 이 오리진의 성공 네트워크 요청에 대해 NEL 보고서를 수집하지 않습니다.
선택(OPTIONAL) failure_fraction 멤버는
이 오리진에 대한 실패
네트워크 요청 보고에 적용할 샘플링 비율을 정의합니다.
값이 있을 경우 반드시(MUST) 0.0~1.0 사이의 수여야 하며, 다른 값이면 파싱 오류입니다.
이 멤버가 없으면 사용자 에이전트는 이 오리진의 모든 실패 네트워크 요청에 대해 NEL 보고서를 수집합니다.
선택(OPTIONAL) request_headers 멤버는 이 오리진에 대한 요청 헤더의 이름 및 값을 네트워크 오류
보고서에 포함할 목록을 정의합니다.
값이 존재한다면 반드시(MUST) 문자열의 리스트여야 합니다.
선택(OPTIONAL) response_headers 멤버는 이 오리진에 대한 응답 헤더의
이름 및 값을 네트워크 오류
보고서에 포함할 리스트를 정의합니다.
값이 존재하면 반드시(MUST) 문자열의 리스트여야 합니다.
네트워크 요청 (request)과 그에 상응하는 응답(response)이 주어지면, 이 알고리즘은 request의 NEL 정책을 추출하여, request의 오리진에 대해 정책 캐시를 갱신합니다.
Potentially Trustworthy가 아닌 경우
NEL이라는 응답 헤더가 존재하지 않는 경우
NEL이라는 응답 헤더의 값으로 설정합니다.
max_age란 멤버가 없거나 멤버의 값이 숫자가 아니면 이 단계를 중단합니다.
max_age 값이 0이면,
정책 캐시에서 origin이
origin인 모든 NEL 정책을 삭제하고 이후 단계는 건너뜁니다.
report_to 멤버가 없거나 그 값이 문자열이 아니면 이 단계를 중단합니다.
success_fraction
멤버가 있고, 값이 0.0~1.0 범위가 아니면 이 단계를 중단합니다.
failure_fraction
멤버가 있고, 값이 0.0~1.0 범위가 아니면 이 단계를 중단합니다.
request_headers 멤버가 있고, 값이 리스트가 아니거나 리스트의 요소
중 문자열이 아니면 이 단계를 중단합니다.
response_headers
멤버가 있고, 값이 리스트가 아니거나 리스트의 요소 중 문자열이 아니면 이 단계를 중단합니다.
policy를 새 NEL 정책으로 생성하고, 다음 속성으로 설정합니다:
[FETCH]에서 좀 더 명확히 연결 예정.
include는 item에 include_subdomains
멤버가 있고 그 값이 true일 때, 아니면 exclude
request_headers 값
response_headers 값
report_to 값max_age 값
success_fraction 값(존재 시), 없으면
0.0
failure_fraction 값(존재 시), 없으면
1.0
네트워크 요청 (request)이 주어지면, 이 알고리즘은 해당 네트워크 요청에 대해 어떤 NEL 정책을 정책 캐시에서 사용해 보고서를 생성할지 결정합니다.
include라면,
그것을 반환합니다.
정책 없음(no policy)을 반환합니다.
네트워크 요청 (request)과 NEL 정책 (policy)이 주어졌을 때, 이 알고리즘은 정책에 따라 요청에서 헤더 값을 추출합니다.
응답 (response)과 NEL 정책 (policy)이 주어졌을 때, 이 알고리즘은 정책에 따라 응답에서 헤더 값을 추출합니다.
네트워크 요청 (request)과 그에 대응하는 응답 (response)이 주어졌을 때, 본 알고리즘은 일치하는 NEL 정책의 지시에 따라 request에 대한 보고서를 생성하고, 보고서와 NEL 정책을 반환합니다. 일치하는 정책이 없을 경우 null을 반환합니다.
Potentially Trustworthy가 아니면
null을 반환합니다.
정책 없음(no policy)이면 null을 반환합니다.
phase 속성이 dns가 아니라면, 아래 프로퍼티를 report
body에 추가합니다:
phase 속성이 dns 또는 connection이 아니라면, 아래
프로퍼티를 report body에 추가합니다:
referrermethodrequest_headersresponse_headersstatus_code0
include이며, report
body의 phase가 dns가 아니라면
null을 반환합니다.
이 단계는 하위 도메인 NEL 정책이 정책 오리진의 하위 도메인에 대해 DNS 해석 단계에서만 보고서 생성을 허용함을 보장합니다. 자세한 내용은 9. 프라이버시 고려사항 참조.
phase 속성이 dns가 아니고, server_ip 값이 비어있지
않고 policy의 수신 IP 주소와 다르다면:
phase를 dns로 변경합니다.
type을 dns.address_changed로 변경합니다.
request_headers, response_headers,
status_code, elapsed_time 프로퍼티를 초기화합니다.
이 단계는 서버의 IP 주소와 정책의 IP 주소가 일치하지 않을 경우 NEL 보고서를 "다운그레이드"합니다. 이는 프라이버시 보호를 위한 조치로, NEL 보고서가 보고서에 명시된 서비스 소유자에게만 전송되도록 보장합니다. 만약 두 IP 주소가 일치하지 않는다면, 사용자 에이전트는 NEL 정책이 origin의 도메인 이름 소유자에 의해 전달된 것임만 확인할 수 있고, 정책이 이 서버의 도메인 이름 해석 결과와 연결된 서버 소유자에 의해 전달된 것임은 확인할 수 없습니다. 따라서 보고서는 DNS 해석 정보만 포함하도록 다운그레이드됩니다. 자세한 내용은 9. 프라이버시 고려사항과 7.5 여러 IP 주소를 가진 오리진을 참조하세요.
ECMAScript 객체(report body, 보통 네트워크 오류 보고서 생성에서 반환되고 상위 명세에 의해 보강됨)와, 일치하는 NEL 정책 (policy), 네트워크 요청(request)이 주어지면, 이 알고리즘은 보고서를 큐잉해 전송합니다.
url을 request의 URL로 설정합니다.
url의 fragment를 비웁니다.
report body의 phase가 dns 또는 connection이면:
네트워크 보고서 생성을 다음 파라미터로 실행합니다:
network-error이 섹션에는 여러 미리 정의된 네트워크 오류 유형이 있습니다.
사용자 에이전트는 이 목록에 커스텀 네트워크 오류
유형을 확장할 수 있습니다
(MAY) — 예를 들어 새로운 프로토콜 대응, 또는 기존 오류의 더 세부적인 설명에 대응하기 위함입니다. 이 경우 사용자 에이전트는 오류 리포트의
단순하고 일관된 처리를 위해 SHOULD 점(.)으로 구분된
패턴([group].[optional-subgroup].[error-name])을 유형명에 따라야 합니다. 예를 들어 수집기는 카테고리별 또는 한 개 이상의 하위 그룹별
집계를 지원할 수 있습니다.
이 절의 모든 네트워크 오류는 DNS 해석 중에 발생하며, 단계(phase)가 dns입니다.
dns.unreachabledns.name_not_resolveddns.faileddns.address_changed
이 절의 모든 네트워크 오류는 보안 연결
설정 중에 발생하며, 단계가 connection입니다.
tcp.timed_outtcp.closedtcp.resettcp.refusedtcp.abortedtcp.address_invalidtcp.address_unreachabletcp.failedtls.version_or_cipher_mismatchtls.bad_client_auth_certtls.cert.name_invalidtls.cert.date_invalidtls.cert.authority_invalidtls.cert.invalidtls.cert.revokedtls.cert.pinned_key_not_in_cert_chaintls.protocol.errortls.failed
이 절의 모든 네트워크 오류는 요청
및 응답 전송 중에 발생하며,
단계가 application입니다.
http.errorhttp.protocol.errorhttp.response.invalidhttp.response.redirect_loophttp.failedabandonedunknown> GET / HTTP/1.1
> Host: example.com
< HTTP/1.1 200 OK
< ...
< Report-To: {"group": "network-errors", "max_age": 2592000,
"endpoints": [{"url": "https://example.com/upload-reports"}]}
< NEL: {"report_to": "network-errors", "max_age": 2592000}
이 NEL 헤더는 NEL 정책을 정의하여, 사용자 에이전트가 example.com의 네트워크 오류를
network-errors라는 이름의 엔드포인트 그룹으로 보고하도록 지정합니다. 이 정책은 2592000초(30일) 동안 적용됩니다.
위 등록은 응답이 신뢰할 수 있는 오리진(potentially trustworthy origin)에서 전달된 경우에만 성공합니다.
> GET / HTTP/1.1
> Host: example.com
< HTTP/1.1 200 OK
< ...
< NEL: {"max_age": 0}
이 NEL 헤더는 사용자 에이전트에게 example.com에 대한 기존 NEL 정책을 모두 제거하도록
지시합니다.
이 섹션은 등록된 NEL 정책이 있는 오리진에서 네트워크 오류가 발생할 때, 사용자 에이전트가 큐잉할 수 있는 네트워크 오류 보고서의 예제를
포함합니다. 보고 업로드 시 생성되는 전체 전송 페이로드 예시를 보여주며, payload의 body 필드는 네트워크 오류 리포트 body입니다.
{
"age": 0,
"type": "network-error",
"url": "https://www.example.com/",
"body": {
"sampling_fraction": 0.5,
"referrer": "http://example.com/",
"server_ip": "2001:DB8:0:0:0:0:0:42",
"protocol": "h2",
"method": "GET",
"request_headers": {},
"response_headers": {},
"status_code": 200,
"elapsed_time": 823,
"phase": "application",
"type": "http.protocol.error"
}
}
이 보고서는 사용자 에이전트가 example.com에서 www.example.com으로 이동을 시도했음을 나타냅니다. 성공적으로
2001:DB8::42로 해석되었으나, 사용자 에이전트가 HTTP/2(h2) 프로토콜로부터 200
응답을 수신하는 도중 프로토콜 오류가 발생해 내비게이션이 중단되었습니다. 823밀리초 후 사용자 에이전트가 탐색을 중단하였고, 네트워크 오류 발생 즉시 이 보고서를
보냈습니다(리포트 age: 0).
{
"age": 0,
"type": "network-error",
"url": "https://widget.com/thing.js",
"body": {
"sampling_fraction": 1.0,
"referrer": "https://www.example.com/",
"server_ip": "",
"protocol": "",
"method": "GET",
"request_headers": {},
"response_headers": {},
"status_code": 0,
"elapsed_time": 143,
"phase": "dns",
"type": "dns.name_not_resolved"
}
}
위 보고서는 사용자 에이전트가 https://www.example.com/에서 https://widget.com/thing.js를 가져오려고
시도했으나, widget.com의 DNS 이름을 해석할 수 없어 143밀리초 후 요청이 중단되었음을 나타냅니다. 이전 widget.com
요청에서 유효한 NEL 정책을 받았기 때문에 이 요청에 대해 네트워크 오류 보고서가 생성되며, 오류 발생 직후 업로드되었습니다(리포트 age: 0).
> GET / HTTP/1.1
> Host: example.com
< HTTP/1.1 200 OK
< ...
< Report-To: {"group": "network-errors", "max_age": 2592000,
"endpoints": [{"url": "https://example.com/upload-reports"}]}
< NEL: {"report_to": "network-errors", "max_age": 2592000, "include_subdomains": true}
이 NEL 헤더를 사용하면 example.com 소유자가 자신의 DNS 서버가 잘못 구성된 상황을 감지할 수 있습니다. 예를 들어
new-subdomain.example.com을 IP 주소로 매핑하는 리소스 레코드를 추가하지 않아 누락된 경우 등입니다. 만약 사용자 에이전트가
new-subdomain.example.com으로 요청을 시도한다면 다음과 같은 보고서가 생성될 수 있습니다:
{
"age": 0,
"type": "network-error",
"url": "https://new-subdomain.example.com/",
"body": {
"sampling_fraction": 1.0,
"server_ip": "",
"protocol": "http/1.1",
"method": "GET",
"request_headers": {},
"response_headers": {},
"status_code": 0,
"elapsed_time": 48,
"phase": "dns",
"type": "dns.name_not_resolved"
}
}
> GET / HTTP/1.1
> Host: example.com
< HTTP/1.1 200 OK
< ...
< Report-To: {"group": "network-errors", "max_age": 2592000,
"endpoints": [{"url": "https://example.com/upload-reports"}]}
< NEL: {"report_to": "network-errors", "max_age": 2592000, "success_fraction": 1.0,
"request_headers": ["If-None-Match"], "response_headers": ["ETag"]}
< ETag: 01234abcd
이 예시에서 example.com 소유자는 서버에 호스팅된 리소스의 각 버전을 식별하기 위해 ETag 응답 헤더를 사용합니다. 사용자 에이전트는 If-None-Match 요청 헤더로 클라이언트 측에
캐시되어 있는 리소스 버전을 서버에 알릴 수 있으며, 서버는 클라이언트의 사본이 최신 상태라면 실제 리소스 전송을 건너뛸 수 있습니다.
이 도메인에 대한 NEL 헤더에 request_headers와 response_headers 필드를 포함하면, 브라우저는
해당 요청에 대해 생성되는 모든 NEL 보고서에 요청 헤더와 If-None-Match 응답 헤더 사본을 포함하며, 사이트 소유자가 캐시 정책의
효과를 추적할 수 있게 합니다.
ETag
이에 따라 다음과 같은 시나리오를 생각해 볼 수 있습니다:
사용자 에이전트가 request를 example.com에 보내고,
response를 성공적으로 수신합니다. 응답에는 리소스 버전을 나타내는 헤더가 포함되어 있습니다. 사용자 에이전트는
다음과 같은 NEL 보고서를 생성합니다:
ETag
{
"age": 0,
"type": "network-error",
"url": "https://example.com/",
"body": {
"sampling_fraction": 1.0,
"server_ip": "192.0.2.1",
"protocol": "http/1.1",
"method": "GET",
"request_headers": {},
"response_headers": {
"ETag": ["01234abcd"]
},
"status_code": 200,
"elapsed_time": 1392,
"phase": "application",
"type": "ok"
}
}
조금 뒤, 사용자 에이전트가 다시 example.com으로 request를 보냅니다. 에이전트는 원본 리소스의 캐시 사본을 여전히 가지고 있고, 요청 헤더에
버전을 포함합니다. 서버는 버전이 여전히 최신임을 확인하고, If-None-Match304 응답으로 캐시 사본이 유효함을 알립니다. 사용자 에이전트는 다음과 같은 보고서를
생성합니다:
{
"age": 0,
"type": "network-error",
"url": "https://example.com/",
"body": {
"sampling_fraction": 1.0,
"server_ip": "192.0.2.1",
"protocol": "http/1.1",
"method": "GET",
"request_headers": {
"If-None-Match": ["01234abcd"]
},
"response_headers": {
"ETag": ["01234abcd"]
},
"status_code": 304,
"elapsed_time": 45,
"phase": "application",
"type": "ok"
}
}
그 이후, 사용자 에이전트가 다시 한번 example.com에 request를 보냅니다. 에이전트는 같은 사본을 가지고 있고, 이전과 같이 요청 헤더에
버전을 포함합니다. 하지만 이번에는 서버가 새 버전 리소스를 감지하여 생성 후 새로운 버전이 인코딩된 If-None-Match 응답 헤더로 보냅니다. 에이전트는 이와 같은
보고서를 생성합니다:
ETag
{
"age": 0,
"type": "network-error",
"url": "https://example.com/",
"body": {
"sampling_fraction": 1.0,
"server_ip": "192.0.2.1",
"protocol": "http/1.1",
"method": "GET",
"request_headers": {
"If-None-Match": ["01234abcd"]
},
"response_headers": {
"ETag": ["56789ef01"]
},
"status_code": 200,
"elapsed_time": 935,
"phase": "application",
"type": "ok"
}
}
오리진의 도메인 이름이 여러 IP 주소로 해석되는 경우, NEL은 때때로 오류 보고서를 "다운그레이드"하여 오류 원인에 대한 정보를 덜 제공할 수 있습니다. 이는 오리진 소유자가 요청을 처리하는 서버의 소유자와 동일하다는 것을 에이전트가 검증할 수 없기 때문입니다.
예를 들어, example.com이 서로 다른 IP 주소를 가진 세 서버에 의해 운영된다고 가정합니다. 서비스 소유자가 DNS를 example.com에 대해
192.0.2.1, 192.0.2.2, 192.0.2.3으로 해석되게 구성하고, 사용자 에이전트가 세 IP 주소에
트래픽을 분산하도록 합니다. 서비스 소유자는 다음과 같은 NEL 정책을 제공합니다:
> GET / HTTP/1.1
> Host: example.com
< HTTP/1.1 200 OK
< ...
< Report-To: {"group": "network-errors", "max_age": 2592000,
"endpoints": [{"url": "https://example.com/upload-reports"}]}
< NEL: {"report_to": "network-errors", "max_age": 2592000,
"success_fraction": 1.0, "failure_fraction": 1.0}
위와 같은 경우, 다음과 같은 시나리오를 생각해 볼 수 있습니다:
사용자 에이전트가 192.0.2.1로 request를 보내고, response를 성공적으로 수신합니다.
해당 응답에는 위 NEL 정책이 포함되어 있으며, 정책의 수신 IP 주소가 192.0.2.1로 설정됩니다. 수신 IP 주소가 서버 IP 주소와 일치하므로 올바르게 NEL 보고서가 생성됩니다:
{
"age": 0,
"type": "network-error",
"url": "https://example.com/",
"body": {
"sampling_fraction": 1.0,
"server_ip": "192.0.2.1",
"protocol": "http/1.1",
"method": "GET",
"request_headers": {},
"response_headers": {},
"status_code": 200,
"elapsed_time": 57,
"phase": "application",
"type": "ok"
}
}
사용자 에이전트가 192.0.2.2로 request를 보내고, 또 다른 response를 성공적으로 수신합니다. 이 응답에도 NEL 정책이 포함되어 있고, 수신 IP
주소가 192.0.2.2로 갱신됩니다. 역시 서버 IP와 일치하므로 다음과 같은 보고서가 생성됩니다:
{
"age": 0,
"type": "network-error",
"url": "https://example.com/",
"body": {
"sampling_fraction": 1.0,
"server_ip": "192.0.2.2",
"protocol": "http/1.1",
"method": "GET",
"request_headers": {},
"response_headers": {},
"status_code": 200,
"elapsed_time": 34,
"phase": "application",
"type": "ok"
}
}
이후 사용자 에이전트가 192.0.2.3으로 request를 보냈으나 서버에 연결할 수 없습니다. 정책의 수신 IP 주소(192.0.2.2)가 해당 요청의 IP와 일치하지
않으므로, tcp.timed_out 대신 dns.address_changed 보고서로 다운그레이드됩니다:
{
"age": 0,
"type": "network-error",
"url": "https://example.com/",
"body": {
"sampling_fraction": 1.0,
"server_ip": "192.0.2.3",
"protocol": "http/1.1",
"method": "GET",
"request_headers": {},
"response_headers": {},
"status_code": 0,
"elapsed_time": 0,
"phase": "dns",
"type": "dns.address_changed"
}
}
다시 192.0.2.1로 시도해도 마찬가지로 정책의 수신 IP 주소가 마지막으로
192.0.2.2로 기록되어 있으므로, 다운그레이드 보고서가 생성됩니다:
{
"age": 0,
"type": "network-error",
"url": "https://example.com/",
"body": {
"sampling_fraction": 1.0,
"server_ip": "192.0.2.1",
"protocol": "http/1.1",
"method": "GET",
"request_headers": {},
"response_headers": {},
"status_code": 0,
"elapsed_time": 0,
"phase": "dns",
"type": "dns.address_changed"
}
}
일반적인 애플리케이션은 수십 개의 리소스를 필요로 하며, 이러한 리소스들은 주로 HTML, CSS, JavaScript 등을 통해 불러오기(fetched)됩니다. 리소스를 요청하는
애플리케이션은 대부분 이러한 fetch 실패를(예: onerror 콜백 등으로) 감지할 수 있지만, 실패의 자세한 네트워크 오류(DNS 실패, TCP 오류, TLS
프로토콜 위반 등) 원인에 대한 보고는 알 수 없습니다.
이 점을 해결하기 위해, 애플리케이션은 하위 리소스를 불러오는 1차 호스트들에 대해 사용자 에이전트에 적절한 NEL 정책을 등록할 수 있습니다. 그런 다음, 해당 정책이 있고 등록된 origin의 리소스에서 네트워크 오류가 발생하면, 사용자 에이전트가 자세한 네트워크 오류 리포트를 보고하여 애플리케이션 개발자가 원인을 조사할 수 있게 됩니다.
리소스가 3차 제공자에 의해 삽입된 경우, 리소스 제공자는 실패를 계측하거나 관찰할 수 없는 경우가 많습니다. 예를 들어 example.com이 사이트에
widget.com/thing.js 리소스를 삽입하였고, example.com을 방문한 사용자가 네트워크 오류로 해당 리소스 로드에 실패했을
때, widget.com 호스트는 이러한 실패를 모를 뿐 아니라 감지할 수도 없습니다.
이런 상황을 해결하기 위해 widget.com은 자신의 호스트에 대해 NEL 정책을 등록할 수 있습니다. 그런 다음 정책이 존재하고, 해당 origin에서 네트워크 오류가 발생하면(1차 또는 3차 오리진에서 요청되었는지와 무관하게), 사용자 에이전트는
네트워크 오류를 보고하여 제공자가 문제를 조사할 수 있도록 합니다.
NEL은 사용자의 네트워크 구성에 대한 새로운 정보를 노출할 수 있는 네트워크 오류 보고서를 제공합니다. 예를 들어, 공격자는 NEL 보고를 악용해 사용자의 네트워크 구성을 탐색하거나 내부 네트워크의 서버를 스캔할 수 있습니다. 또한 HSTS, HPKP, 고정 CSP 정책과 유사하게, 저장된 NEL 정책을 각 사용자별로 맞춤화한 리포트 URI를 통해 식별자로 써서(HTTP 쿠키 조합 혹은 대체) "슈퍼쿠키(supercookie)"로 사용할 수 있습니다.
이러한 위험 완화를 위해 NEL 등록 및 네트워크 오류 보고서 전달 모두 신뢰할 수 있는 오리진에만 제한됩니다. 이는 임시 HTTP MITM 공격자가 NEL을 영속적 추적기로 악용하는 것을 어렵게 만듭니다.
또한, NEL 정책 캐시는 네트워크 파티션 키 단위로 분할되기 때문에, 한 임베딩 컨텍스트에 저장된 NEL 정책이 다른 컨텍스트(예: 다른 최상위 사이트에 임베딩)에서 사용되지 않습니다.
NEL은 기존의 서버 모니터링을 보완하도록 설계되었습니다. NEL 보고서는 오직 요청 대상 서비스 소유자에게만 전송되어야 합니다. DNS 해석 단계 오류에서는, NEL 정책을 해당 도메인 네임스페이스 트리 소유자에게서 받은 경우에만 보고가 생성됩니다. 보안 연결 설정 또는 요청 및 응답 전송 단계 오류의 경우, NEL 정책을 해당 서버 소유자에게서 받은 경우에만 보고가 생성됩니다.
위 정책의 수신 IP 주소 및 서브도메인 플래그 처리 이유도 이것과 관련됩니다. 정책의 수신 IP 주소가 서버의 IP와 일치하는지 검사함으로써, NEL은 정책의 신뢰 경계를 오리진 뿐 아니라 실제 통신하는 서버까지 확대합니다. 이는 공격자가 소유한 서버에서 장기 NEL 정책을 배포하고, 이후 자신의 네임서버를 바꿔 오리진을 자신이 소유하지 않은 서버로 해석하게 해 사용자 에이전트가 해당 서버 정보를 공격자에게 보고하는 것을 방지합니다. (즉, DNS 리바인딩 공격 예방)
마찬가지로, 서브도메인 NEL 정책은 제한되어 있으며, 정책 오리진의 하위 도메인에 대해서도 DNS 해석 단계(phase)에서만 보고서를 생성할 수 있습니다. 이 단계에서는 소유자 확인을 위한 서버가 없으며, 정책이 request의 origin의 상위도메인에서 내려왔는지만으로 소유권을 확인합니다. 이를 통해 특정 도메인 소유자가 7.3 DNS 오구성 오류를 감지할 수 있으나, 악의적 DNS 엔트리를 통한 정보 수집은 차단됩니다.
정보 유출을 방지하기 위해, NEL은 request에 대해 서버가 요청 처리 시 접근할 수 없는 정보는 포함하지 않습니다. DNS 해석 오류의 경우 NEL 보고서는 오로지 DNS에서 확인 가능한 정보만 포함합니다. 이는 서버가 NEL을 통해
사용자의 추가 정보를 수집하는 것을 차단합니다. 다만, NEL 보고서에는 server_ip 필드에 웹사이트의 공개 IP가 포함되며, 예컨대 서비스가 로드밸런서 뒤에 있거나
투명 프록시(MitM)가 있는 경우 서비스 소유자가 반드시 이를 아는 것은 아닙니다.
예를 들어, NEL 보고서는 특정 DNS 리졸버(resolver)를 통해 request의 도메인 이름을 해석했는지에 관한 정보는 포함하지 않습니다.
위와 같은 제한 외에도, 사용자 에이전트는 반드시(MUST) 다음을 해야 합니다:
NEL을 배포하는 개발자는 지정 수집기(collector)로 전송되는 NEL 보고서의 프라이버시 영향을 반드시(should) 고려해야 합니다. 예를 들어, 보고서에 민감한 데이터가 포함된 URL(예: "Capability URLs")이 실릴 수 있으며, 이에 대해 특별한 조치가 필요할 수 있습니다([CAPABILITY-URLS] 참고). 이런 경우 이를 제3자에게 전송하지 않도록 자체 NEL 수집기를 운영해야 할 수도 있습니다.
permanent message header field registry는 다음 등록사항들로 갱신되어야 합니다([RFC3864] 참고):
NELNEL response header 참고)request)
request)
response)
request)
report)
url)
url)
url)
이 문서는 [CSP] 및 [RFC6797] 명세의 텍스트를 해당 명세의 라이선스에 따라 재사용합니다. 또한, 본 작업에 유용한 피드백과 기여를 해주신 Julia Tuttle, Chris Bentzel, Todd Reifsteck, Aaron Heady, Mark Nottingham께 진심으로 감사드립니다.
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in: