유저 에이전트 클라이언트 힌트

커뮤니티 그룹 초안 보고서,

이번 버전:
https://wicg.github.io/ua-client-hints/
편집자:
(Google LLC)
(Google LLC)
이전 편집자:
(Google LLC)
참여하기:
이슈 등록 (오픈 이슈)

소개

이 문서는 개발자가 필요할 때 에이전트 기반 콘텐츠 협상을 수행할 수 있도록 하는 클라이언트 힌트 집합을 정의하며, 기존 User-Agent 헤더가 노출했던 역사적 문제와 수동 지문 채취 위험을 회피할 수 있도록 설계되었습니다.

이 문서의 상태

이 명세는 웹 플랫폼 인큐베이터 커뮤니티 그룹에서 발행했습니다. W3C 표준이 아니며 W3C 표준 트랙에도 포함되어 있지 않습니다. W3C 커뮤니티 기여자 라이선스 계약 (CLA)에 따라 제한적 옵트아웃 및 기타 조건이 적용됩니다. W3C 커뮤니티 및 비즈니스 그룹에 대해 더 알아보세요.

1. 소개

이 섹션은 규범적이지 않습니다.

현재, 사용자 에이전트는 일반적으로 각 요청에 User-Agent HTTP 요청 헤더 필드를 서버에 함께 전송하여 자신을 식별합니다( [rfc9110] 5.5.3절에 정의됨). 이상적으로는, 이 헤더가 서버에게 콘텐츠 협상 능력을 제공해, 해당 사용자 에이전트에 가장 적합한 리소스를 전송함으로써 대역폭과 사용자 경험 모두를 최적화할 수 있어야 합니다. 하지만 실제로 이 헤더 값은 기본값으로 보기엔 너무 많은 정보를 사용자 기기에 대해 노출하고, 동시에 잘못된 서버 측 휴리스틱을 우회하기 위해 진짜 사용자 에이전트를 의도적으로 숨기기도 합니다.

예를 들어, 최신 버전의 iOS용 Chrome은 아래와 같이 자신을 식별합니다:

User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X)
            AppleWebKit/605.1.15 (KHTML, like Gecko)
            CriOS/69.0.3497.105 Mobile/15E148 Safari/605.1

최신 버전의 Edge는 아래와 같이 자신을 식별합니다:

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
            AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.2704.79
            Safari/537.36 Edge/18.014

이 문자열들에는 꽤 많은 정보(그리고 거짓 정보도 다수)가 포함되어 있습니다. 버전 번호, 플랫폼 정보, 모델 정보 등이 모든 요청과 함께 전송되며, 다양한 지문 채취 기법의 기반이 됩니다. 개별 벤더들은 자신의 사용자 에이전트 문자열을 변경하려 시도했고, 개발자들로부터 여러 가지 피드백 범주를 받아왔습니다. 이는 기존 방식의 발전을 막는 요인이 되었습니다:

  1. 브랜드 및 버전 정보(예: "Chrome 69")는 사이트가 감지 불가능한 특정 릴리즈의 알려진 버그를 우회할 수 있게 해줍니다. 예를 들어, 콘텐츠 보안 정책의 구현은 벤더마다 크게 달랐으며, 어떤 브라우저가 파싱과 실행을 담당하는지 모르면 어떤 정책을 HTTP 응답에 포함해야 할지 알기 어렵습니다.

  2. 개발자들은 종종 사용자 에이전트와 플랫폼에 따라 어떤 콘텐츠를 보낼지 협상합니다. 예를 들어, 일부 애플리케이션 프레임워크는 iOS와 Android에서 각각의 미적·디자인 패턴에 맞게 스타일을 다르게 적용합니다.

  3. 1번과 유사하게, OS 버전과 아키텍처는 특정 버그의 원인이 될 수 있으며, 웹사이트 코드에서 이를 우회하거나, 다운로드할 적절한 실행파일(32/64비트, ARM/Intel 등)을 선택하는 데 활용됩니다.

  4. 숙련된 개발자들은 모델/제조정보를 활용하여 사이트를 기기 성능에 맞게 최적화하고(예: [FacebookYearClass]), 성능 버그 및 회귀가 특정 모델/제조사에 국한될 때 이를 정확히 파악합니다.

이 문서는 User-Agent 문자열에서 엔트로피를 더 적극적으로 제거할 수 있는 메커니즘을 제안합니다. 서버가 클라이언트에 대한 특정 정보가 정말 필요할 때만 해당 정보를 받을 수 있도록 옵트인할 수 있습니다. 이 문서는 클라이언트의 브랜드/버전 정보, OS 브랜드/주요 버전, 그리고 기기 정보 등을 제공하는 새로운 클라이언트 힌트([RFC8942])들을 소개합니다. 이 데이터는 항상 모든 곳에 브로드캐스트 되는 대신, 사용자 에이전트가 더 세분화된 데이터 요청에 대해 적절하게 응답함으로써 수동 지문 채취 노출 영역을 줄일 수 있습니다( 최고 모범 사례 1[FINGERPRINTING-GUIDANCE] 참고).

1.1. 예시

이 섹션은 규범적이지 않습니다.

사용자가 최신 버전의 "Examplary Browser"로 https://example.com/에 처음 진입합니다. 이때 사용자 에이전트는 HTTP 요청에 아래와 같은 헤더를 포함해 전송합니다:

Sec-CH-UA: "Examplary Browser"; v="73", ";Not?A.Brand"; v="27"
Sec-CH-UA-Mobile: ?0
Sec-CH-UA-Platform: "Windows"

서버가 사용자의 기본 플랫폼 버전에 맞는 콘텐츠를 렌더링하고자 할 때, 초기 응답에 Accept-CH 헤더( [RFC8942] 2.2.1절)로 정보를 더 요청합니다:

Accept-CH: Sec-CH-UA-Platform-Version

이에 대해 사용자 에이전트는 다음 요청에 플랫폼 버전 정보를 포함합니다:

Sec-CH-UA: "Examplary Browser"; v="73", ";Not?A.Brand"; v="27"
Sec-CH-UA-Mobile: ?0
Sec-CH-UA-Platform: "Windows"
Sec-CH-UA-Platform-Version: "14.0.0"

1.2. 활용 사례

이 섹션은 규범적이지 않습니다.

이 섹션은 User-Agent 문자열의 현재 활용 방식과, 유사한 기능을 User-Agent Client Hints(UA-CH)로 어떻게 구현할 수 있는지 문서화합니다.

1.2.1. 차등 제공

1.2.1.1. 브라우저 기능 기반
이 활용 사례는 polyfill.io 같은 서비스가 최신 브라우저 사용자에게 불필요한 폴리필을 제공하지 않고, 맞춤 폴리필만 제공할 수 있게 지원합니다.

비슷하게, 사용자의 브라우저가 최신 ES 기능을 지원한다면 자바스크립트 전환(transpilation)을 피할 수 있고, 이는 코드의 비대화와 비효율을 막아줍니다. 또한, 이미지를 제공할 때 일부 브라우저는 Accept 요청 헤더를 업데이트하지 않거나 MIME 타입만으로는 동일 포맷의 다양한 변종을 구분하기 힘든 경우(WebP 등)가 있습니다. 이런 경우에는 브라우저와 버전 정보를 아는 것이 올바른 이미지를 제공하는 데 중요합니다.

이 활용 사례가 동작하려면, 서버는 브라우저와 의미 있는 버전을 알고, 이를 사용 가능한 기능 목록과 매핑해야 합니다. 이를 통해 어떤 폴리필이나 코드 변종을 제공해야 하는지 알 수 있습니다.

UA-CH를 사용하는 서비스는 모든 요청에 기본적으로 전송되는 Sec-CH-UA 헤더를 검사하고, 이에 따라 응답을 수정해야 합니다.

1.2.1.2. 브라우저 버그 우회
일부 브라우저 버전에는 잘 알려진 버그가 존재하며, 해당 버그를 우회하기 위해 콘텐츠에서 별도 처리가 필요합니다. 이런 버그가 발생하면 브라우저가 충돌하거나 콘텐츠가 깨지는 등 문제가 생기며, 해당 버그는 기능 탐지로 감지할 수 없습니다. 따라서 영향을 받는 브라우저 버전에 대해서는 콘텐츠에서 해당 문제를 완전히 회피해야 합니다.

이 활용 사례에서는 서버가 브라우저와 의미 있는 버전을 파악하고, 해당 브라우저 버전에 영향을 주는 버그를 인지하며, 그 버전에 대해 우회 처리를 적용해야 합니다.

UA-CH를 사용하는 서비스는 모든 요청에 기본적으로 전송되는 Sec-CH-UA 헤더를 검사하여 응답을 수정해야 합니다.

1.2.2. 시장 점유율 분석

브라우저의 시장 점유율은 매우 중요할 수 있습니다. 브라우저 사용 현황을 파악하면 개발자들이 특정 브라우저에서 테스트를 늘려 사용자 호환성 문제를 줄일 수 있습니다. 또한, 시장 점유율은 브라우저 벤더의 비즈니스 목표에도 직접적인 영향을 주며, 브라우저의 향후 개발을 보장합니다.

시장 점유율 분석을 위해서는 서버가 아래 중 하나 이상을 파악해야 합니다: 사용자 에이전트 이름과 그 의미 있는 버전, 운영체제 및 버전, 기기 모델. 이를 등록하고 상대적 시장 점유율을 산출할 수 있습니다.

UA-CH로 시장 점유율 분석을 하고자 하는 사이트는 모든 요청에 기본적으로 전송되는 Sec-CH-UA 헤더를 기록해야 하며, 추가적인 UA 클라이언트 힌트가 활용 사례에 따라 요청될 수 있습니다(예: 모바일 기기 모델 분석).

설계상, 브랜드 리스트 각각을 개별적으로 보면 덜 인기 있는 브라우저의 실제 브랜드와 인기 있는 브라우저의 임의 GREASE를 구분하기 어렵습니다. 덜 인기 있는 브라우저는 호환성을 위해 여러 인기 브랜드 이름을 포함할 수 있으므로, 이 방식으로 시장 점유율을 집계하면 인기 있는 브라우저의 사용자로 분류되어 덜 인기 있는 브라우저가 가시성을 얻지 못할 수 있습니다.

따라서 분석 목적으로는 브랜드 리스트 전체를 하나의 단위로 취급해, 구분하려는 (브라우저, 버전) 쌍이 보내는 브랜드 리스트와 비교하는 것이 좋습니다. 이 방법을 쓰려면, 새로운 브라우저 버전이 출시되거나 새로운 브라우저가 인기 있을 때 브랜드 리스트 목록을 정기적으로 업데이트해야 하며, 그렇지 않으면 모두 미확인 브라우저로 분류됩니다. 하지만 이런 경우 사이트가 깨지지는 않으므로, 미확인 브라우저에 대해 폐쇄적으로 대응해도 괜찮습니다.

이러한 브랜드 리스트 목록은 중앙 집중식으로 관리되어 많은 사이트에서 사용할 수 있습니다(예: caniuse와 MDN에서 브라우저 기능 지원을 관리하고 많은 웹마스터가 활용하는 것처럼).

명세에서는 브라우저가 버전별로 고정된 브랜드 리스트를 전송하도록 권장합니다. 이렇게 하면 사용량 집계가 간단해지고 캐싱에도 도움이 되며, 브랜드 리스트와 (브라우저, 버전) 쌍의 간단한 매핑으로 구현할 수 있습니다.

1.2.3. 콘텐츠 적응

콘텐츠 적응이란 사용자의 필요에 맞게 콘텐츠를 맞춤화하는 것입니다. UA 문자열 외에도 콘텐츠 적응에는 다양한 차원이 있습니다: 뷰포트 크기, 기기 메모리, 사용자 선호도 등. 이 하위 섹션에서는 현재 User-Agent 문자열에 포함된 정보에 기반한 콘텐츠 적응 요구를 다룹니다.
1.2.3.1. 브라우저 기반 적응
일부 사이트는 서로 다른 브라우저에 약간씩 다른 콘텐츠를 제공합니다. 그 이유는 다양합니다. 일부는 기능 지원 차이로 인해 다양한 경험을 제공하기 위함이고(정당한 이유), 일부는 해당 브라우저에서 테스트되지 않았음을 경고하기 위함입니다(덜 정당한 이유). 일부는 특정 브라우저 사용자를 사이트 접근에서 차단하려는 경우도 있습니다(명백히 잘못된 이유).

브라우저로서 우리는 첫 번째 목적은 지원하고, 후자는 지양하고자 합니다.

1.2.3.2. 모바일 특화 사이트
많은 사이트 운영자는 모바일과 데스크탑 사이트 간에 다른 콘텐츠를 제공합니다. 반응형 웹 디자인 덕분에 단일 코드베이스로 다양한 폼팩터 지원이 가능하지만, 여전히 모바일 특화 버전을 제공하는 것이 더 나은 경우가 있습니다.

이런 경우에는 모바일 기기 사용자에게 모바일 특화 사이트를 제공하는 것이 도움이 됩니다. 이를 위해 서버는 HTML 제공 시점에 사용자가 모바일 기기인지 아닌지를 파악해야 합니다.

UA-CH를 활용하여 모바일 특화 사이트를 제공하려는 사이트는 모든 요청에 기본적으로 전송되는 Sec-CH-UA-Mobile 헤더를 사용할 수 있습니다.

1.2.3.3. 저사양 기기
일부 사이트는 CPU 집약적 작업, 대용량 영상·이미지 등을 처리할 수 없는 저사양 기기에 대해 다른 콘텐츠를 제공합니다. 이런 콘텐츠 적응은 주로 현재 User-Agent 문자열에 포함된 기기 모델 정보를 사용하며, 서버 측 데이터베이스로 기기 모델을 메모리, CPU 성능 등으로 분류해 콘텐츠를 나눕니다.

분류 기준이 메모리인 경우 Device-Memory Client Hint를 사용할 수 있습니다. 그 외에는 UA-CH를 통해 Sec-CH-UA-Model 힌트로 기기 모델을 얻을 수 있습니다.

이 두 힌트 모두 기본값으로 전송되지 않으므로, 추가 작업이 필요합니다.

최상위 오리진은 Accept-CH: Device-Memory, Sec-CH-UA-Model 헤더를 응답에 포함해 해당 힌트 수신에 옵트인해야 합니다. 모든 탐색 요청에서 적응이 꼭 필요하다면, 브라우저가 해당 힌트를 지원하지만 없는 경우 리다이렉트가 필요합니다. 또는 Critical-CH를 사용해 클라이언트가 추가 요청/응답 라운드트립을 처리하게 할 수도 있습니다.

이런 적응이 필요한 서드파티 오리진은 위임이 필요합니다. 최상위 오리진은 Accept-CH로 옵트인하고, Permissions-Policy 헤더로 해당 힌트를 서드파티에 위임해야 합니다.

1.2.3.4. OS 특화 스타일
일부 사이트는 사용자의 OS에 맞춘 인터페이스를 제공하기를 원할 수 있습니다. 점진적 개선 방식(예: 스크립트로 버튼 스타일을 다르게 적용)이 더 바람직할 수 있지만, 플랫폼 및 플랫폼 버전에 따라 인라인 스타일을 서버에서 맞춤 제공하는 경우도 있습니다.

이 경우는 위 "저사양 기기" 사례와 유사하며, Sec-CH-UA-PlatformSec-CH-UA-Platform-Version 힌트를 활용하면 됩니다.

1.2.3.5. OS 통합
비슷하게, 일부 사이트는 OS별 링크(예: Android 인텐트 링크)를 제공하기를 원할 수 있습니다. 역시 점진적 개선 방식으로 스크립트로 링크를 수정하는 것이 더 바람직하지만, 일부 사이트는 서버 측 적응을 선호할 수 있습니다.

이 경우에도 "OS 특화 스타일"과 같이 Sec-CH-UA-PlatformSec-CH-UA-Platform-Version 힌트를 사용하면 됩니다.

1.2.3.6. 브라우저 및 OS 특화 실험
일부 서버는 다중 변종 실험을 특정 브라우저, 특정 플랫폼, 또는 그 버전에 제한하고자 할 수 있습니다. 브라우저와 버전 한정 실험은 Sec-CH-UA 값을 사용하고, 플랫폼과 버전까지 필요하면 Sec-CH-UA-Platform 힌트(기본값)를 쓰고, Sec-CH-UA-Platform-Version 힌트는 별도 요청하거나 클라이언트 측 스크립트로 실험을 제어해야 합니다.

1.2.4. 사용자 로그인 알림

특히 보안 민감 사이트는 새로운 기기에서 로그인이 발생할 때 사용자를 알리기를 선호합니다. 사용자는 해당 로그인을 인지하고 자신이 직접 하거나 대리한 로그인이 아니라면 조치를 취할 수 있습니다.

이런 알림이 의미 있으려면, 사이트는 브라우저의 상업 브랜드를 인식하여 사용자에게 전달해야 합니다. 메시지에는 플랫폼 및 버전도 포함되어 사용자가 어떤 기기인지 알 수 있게 합니다.

이런 메시지는 서버 측 적응이 필요 없으므로, userAgentData.getHighEntropyValues() 메서드로 필요한 정보를 얻는 것이 더 좋습니다.

1.2.5. 적합한 바이너리 실행파일 다운로드

일부 사이트는 네이티브 애플리케이션의 바이너리 실행파일 다운로드를 지원하며, 사용자에게 올바른 바이너리를 제안해야 합니다. 적합한 바이너리는 운영체제, 버전, 비트수, CPU 아키텍처 등 여러 요소에 따라 달라집니다.

이 활용 사례를 해결하려면 다운로드 사이트가 Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA-Arch, Sec-CH-UA-Bitness 힌트(혹은 API를 통한 쿼리)를 옵트인하여 사용자에게 기본값으로 맞는 바이너리를 제공합니다.

1.2.6. 전환 모델링

일부 머신러닝 모델은 User-Agent 문자열의 다양한 정보를 활용해 사용자에 대해 여러 가지를 추정합니다. 유사 모델링도 가능하지만, 필요한 정보를 수집하려면 명시적으로 옵트인을 해야 합니다.

1.2.7. 취약점 필터링

일부 환경에서는 프록시 서버가 구식 기기에서 접근하는 사용자를 차단하여 보안 취약점을 방지합니다. Sec-CH-UA를 통해 브라우저 및 버전 정보를 얻을 수 있지만, 브라우저와 OS의 전체 버전 정보가 추가 분석에 유용합니다.

이런 프록시는 리다이렉트 단계를 추가하거나, 브라우저 및 플랫폼 버전 힌트 수신에 옵트인하는 Client Hint 신뢰성 메커니즘 중 하나를 사용해야 계속 해당 힌트에 접근할 수 있습니다.

1.2.8. 로그 및 디버깅

많은 서비스가 User-Agent 문자열을 기록하며, 과거 트래픽 분석이나 에러 디버깅에 활용합니다. 이런 서비스는 기록 목적으로 Sec-CH-UA의 저엔트로피 값을 사용하거나, 고엔트로피 힌트 수신을 옵트인해야 합니다. 후자는 단순 포렌식 목적으로는 적합하지 않습니다. 반면, 특정 이슈 발생 시에는 추가 정보를 받거나 userAgentData.getHighEntropyValues() API를 사용할 수 있습니다.

1.2.9. 지문 채취

사용자 지문 채취란 여러 출처에서 다양한 사용자 정보를 수집·교차해 고유한 사용자 식별자를 생성하는 행위로, 이는 사용자가 브라우저의 상태를 초기화해도(예: 쿠키 삭제) 이후 다시 식별할 수 있게 합니다.

이 경우에는 오리진이 가능한 많은 엔트로피를 수집하려 하므로, 모든 힌트를 수집할 가능성이 높습니다.

1.2.9.1. 스팸 필터링 및 봇 감지
이는 사용자에게 불리하지 않은 지문 채취 사례로, UA-CH에서는 다양한 힌트의 능동적 수집으로 가능합니다.

향후에는 브라우저가 사용자를 대신해 수집 엔트로피를 제한(예: Privacy Budget 제안)함으로써 스팸 필터링 및 봇 감지에 대한 대체 방법이나 API가 나오길 기대합니다.

1.2.9.2. 지속적인 사용자 추적
이 사례는 명시적으로 더 어렵게 하려는 지문 채취입니다. "스팸 필터링" 사례와 마찬가지로 모든 힌트의 능동 수집은 가능하지만, Privacy Budget 등의 제안은 이를 방지하며, 지속적 사용자 추적을 위한 대체 메커니즘은 제공하지 않습니다.
1.2.9.3. 알려진 봇 및 크롤러 차단
현재 User-Agent 문자열은 알려진 봇 및 크롤러를 차단하는 단순한 수단으로 자주 사용됩니다. "일반" 트래픽이 기본적으로 더 적은 엔트로피를 노출하면 봇이 군중 속에 숨기 쉬워질 수 있다는 우려가 있습니다. 하지만 이 때문에 군중을 더 쉽게 식별 가능하게 만드는 것은 정당하지 않습니다.

스팸 필터링 사례와 마찬가지로, 향후 User-Agent 문자열 매칭을 대체할 방법이 나오길 기대합니다.

2. 인프라

이 명세는 Client Hints Infrastructure, HTTP Client Hints, Infra Standard, Permissions Policy에 의존합니다. [CLIENT-HINTS-INFRASTRUCTURE] [RFC8942] [INFRA] [permissions-policy-1]

이 명세에서 사용하는 일부 용어는 HTTP를 위한 구조화된 필드 값에서 정의됩니다. [rfc9651]

3. 유저 에이전트 힌트

다음 섹션에서는 서버가 유저 에이전트에 대한 세부 정보를 노출하는 여러 HTTP 요청 헤더 필드를 정의합니다. 서버는 [RFC8942]에서 정의한 Client Hints 인프라를 통해 해당 정보를 받을 수 있도록 옵트인할 수 있습니다. 아래 정의는 각 유저 에이전트가 자신에 대해 여러 속성을 정의했다고 가정합니다:

유저 에이전트는 이러한 문자열을 짧고 명확하게 유지해야 하지만, 서버는 각 값에 대해 임의의 값을 허용해야 합니다. 왜냐하면 모든 값은 유저 에이전트가 임의로 구성하기 때문입니다.

유저 에이전트는 고엔트로피 platform architecture 값을 다음과 같이 매핑해야 합니다:

기타 CPU 아키텍처는 상황에 따라 위 값 중 하나로 매핑하거나 빈 문자열로 매핑할 수 있습니다.

유저 에이전트platform architecture 또는 platform bitness에 대해, 다음 두 조건 모두 해당하는 플랫폼이 아닌 경우 빈 문자열 또는 허구의 값을 반환해야 합니다:

유저 에이전트mobileness가 거짓이면 model에 대해 빈 문자열을 반환해야 합니다. 유저 에이전트mobileness가 참이어도 해당 플랫폼에서 일반적으로 모델이 노출되지 않으면 model에 대해 빈 문자열을 반환해야 합니다.

유저 에이전트sf-string 타입 힌트에 대해 빈 문자열, sf-boolean 타입 힌트에 대해 false, 또는 기타 허구의 값을 반환할 수 있습니다. 이는 프라이버시, 호환성, 기타 이유로 full version, platform architecture, platform bitness, wow64-ness 또는 model 힌트 요청 시 적용할 수 있습니다.

3.1. 'Sec-CH-UA' 헤더 필드

Sec-CH-UA 요청 헤더 필드는 서버에 유저 에이전트브랜딩주요 버전 정보를 제공합니다. 이는 구조화 헤더로, 값은 반드시 리스트여야 하며 [rfc9651] 참고. 리스트의 각 항목은 문자열이어야 합니다. 각 항목의 값에는 "v" 파라미터가 포함되어 유저 에이전트 버전을 나타내야 합니다.

헤더의 ABNF:

Sec-CH-UA = sf-list

요청에 대해 Sec-CH-UA 값을 반환하려면 다음 단계를 수행합니다:

  1. brands를 "significant version"으로 create brands를 실행한 결과로 설정합니다.

  2. listbrands와 "significant version"을 사용하여 create a brand-version list를 실행한 결과로 설정합니다.

  3. listserializing a list를 실행한 출력을 반환합니다.

참고: 대부분의 Client Hint와 달리, 저엔트로피 힌트 테이블에 포함되어 있으므로, Sec-CH-UA 헤더는 서버가 Accept-CH 헤더로 옵트인하지 않아도 기본적으로 전송됩니다(단, 정책-제어 Client Hint 기능으로 제어는 가능). 저엔트로피로 간주되는 이유는 유저 에이전트의 브랜드 정보와 주요 버전 번호만 포함되기 때문입니다(둘 다 다른 헤더 구조나 기능 테스트 등으로 쉽게 추측 가능 [Janc2014] 참고).

참고: Sec-CH-UA 는 brands 리스트 내 각 브랜드의 주요 버전을 노출합니다. full version이 필요한 경우 Sec-CH-UA-Full-Version-List를 참조하세요.

3.2. 'Sec-CH-UA-Arch' 헤더 필드

Sec-CH-UA-Arch 요청 헤더 필드는 서버에 특정 유저 에이전트가 실행되는 플랫폼의 아키텍처 정보를 제공합니다. 구조화 헤더로, 값은 반드시 문자열이어야 합니다 [rfc9651].

헤더의 ABNF:

Sec-CH-UA-Arch = sf-string

3.3. 'Sec-CH-UA-Bitness' 헤더 필드

Sec-CH-UA-Bitness 요청 헤더 필드는 서버에 특정 platform bitness 정보를 제공합니다. 해당 유저 에이전트가 실행되는 플랫폼의 아키텍처에 대한 정보입니다. 구조화 헤더로, 값은 반드시 문자열이어야 합니다 [rfc9651].

헤더의 ABNF:

Sec-CH-UA-Bitness = sf-string

3.4. 'Sec-CH-UA-Form-Factors' 헤더 필드

Sec-CH-UA-Form-Factors 요청 헤더 필드는 서버에 유저 에이전트form-factors 정보를 제공합니다. 구조화 헤더로, 값은 반드시 리스트이어야 하며 [rfc9651] 참고. 추가 지문 채취 엔트로피를 제공하지 않기 위해, 헤더 값은 반드시 사전순으로 제공되어야 하며 값은 대소문자 구분입니다.

헤더는 장치의 폼팩터를 다음과 같은 일반적인 값 중 하나 이상으로 기술해야 합니다: "Desktop", "Automotive", "Mobile", "Tablet", "XR", "EInk", "Watch". 적용 가능한 모든 폼팩터 값을 포함해야 합니다.

참고:

유저 에이전트의 폼팩터는 사용자가 유저 에이전트와 상호작용하는 방식을 설명합니다. 허용된 값의 의미는 다음과 같습니다:

새로운 폼팩터가 의미 있게 다르게 사용자와 상호작용할 때, 사이트가 해당 장치에서 사용자 경험을 변경하고 싶을 compelling한 사례가 있고, 기존 힌트로 해당 폼팩터를 신뢰성 있게 식별할 방법이 없다면 새로운 값을 명세에 제안·추가해야 합니다.

헤더의 ABNF:

Sec-CH-UA-Form-Factors = sf-list

3.5. 'Sec-CH-UA-Full-Version' 헤더 필드

Sec-CH-UA-Full-Version는 더 이상 권장되지 않으며, 앞으로 제거될 예정입니다. 개발자는 Sec-CH-UA-Full-Version-List를 대신 사용해야 합니다.

Sec-CH-UA-Full-Version 요청 헤더 필드는 서버에 유저 에이전트의 full version 정보를 제공합니다. 구조화 헤더로, 값은 반드시 문자열이어야 합니다 [rfc9651].

헤더의 ABNF:

Sec-CH-UA-Full-Version = sf-string

3.6. 'Sec-CH-UA-Full-Version-List' 헤더 필드

Sec-CH-UA-Full-Version-List 요청 헤더 필드는 서버에 brands 리스트 내 각 브랜드의 full version 정보를 제공합니다. 구조화 헤더로, 값은 반드시 리스트이어야 합니다 [rfc9651].

헤더의 ABNF:

Sec-CH-UA-Full-Version-List = sf-list

요청에 대해 Sec-CH-UA-Full-Version-List 값을 반환하려면 다음 단계를 수행합니다:

  1. brands를 "full version"으로 create brands를 실행한 결과로 설정합니다.

  2. listbrands와 "full version"을 사용하여 create a brand-version list를 실행한 결과로 설정합니다.

  3. listserializing a list를 실행한 출력을 반환합니다.

3.7. 'Sec-CH-UA-Mobile' 헤더 필드

Sec-CH-UA-Mobile 요청 헤더 필드는 서버에 유저 에이전트가 "모바일" 사용자 경험을 선호하는지 여부를 제공합니다. 구조화 헤더로, 값은 반드시 불리언이어야 합니다 [rfc9651].

헤더의 ABNF:

Sec-CH-UA-Mobile = sf-boolean

참고:Sec-CH-UA와 같이 저엔트로피 힌트 테이블에 포함되어 있으므로, Sec-CH-UA-Mobile 헤더는 서버가 Accept-CH 헤더로 옵트인하지 않아도 기본적으로 전송됩니다(단, 정책-제어 Client Hint 기능으로 제어는 가능). 저엔트로피로 간주되는 이유는 사용자가 직접 제어할 수 있는 한 비트 정보이기 때문입니다.

3.8. 'Sec-CH-UA-Model' 헤더 필드

Sec-CH-UA-Model 요청 헤더 필드는 서버에 특정 유저 에이전트가 실행되는 장치에 대한 정보를 제공합니다. 구조화 헤더로, 값은 반드시 문자열이어야 합니다 [rfc9651].

헤더의 ABNF:

Sec-CH-UA-Model = sf-string

3.9. 'Sec-CH-UA-Platform' 헤더 필드

Sec-CH-UA-Platform 요청 헤더 필드는 서버에 특정 유저 에이전트가 실행되는 플랫폼 정보를 제공합니다. 구조화 헤더로, 값은 반드시 문자열이어야 하며 [rfc9651] 참고. 값은 "Android", "Chrome OS", "Fuchsia", "iOS", "Linux", "macOS", "Windows", "Unknown" 중 하나와 일치하는 것이어야 합니다.

헤더의 ABNF:

Sec-CH-UA-Platform = sf-string

참고:Sec-CH-UA와 같이 저엔트로피 힌트 테이블에 포함되어 있으므로, Sec-CH-UA-Platform 헤더는 서버가 Accept-CH 헤더로 옵트인하지 않아도 기본적으로 전송됩니다(단, 정책-제어 Client Hint 기능으로 제어는 가능).

3.10. 'Sec-CH-UA-Platform-Version' 헤더 필드

Sec-CH-UA-Platform-Version 요청 헤더 필드는 서버에 특정 플랫폼 버전 정보를 제공합니다. 해당 유저 에이전트가 실행 중인 플랫폼 버전입니다. 구조화 헤더이며, 값은 반드시 문자열이어야 합니다[rfc9651]. 값은 플랫폼 버전 얻기platform brand와 함께 실행한 결과입니다.

플랫폼 버전 얻기를 위해, platform 문자열이 주어지면 다음 단계를 실행합니다:

  1. platform이 "Linux"인 경우:

    1. 빈 문자열을 반환합니다.

  2. platform이 "Android"인 경우:

    1. platformReturnedVersionString을 OS의 android.os.Build.VERSION.RELEASE 문자열 쿼리 결과로 설정합니다.

    2. 통합 플랫폼 버전 문자열 생성platformReturnedVersionString과 함께 실행한 결과를 반환합니다.

  3. platform이 "iOS"인 경우:

    1. platformReturnedVersionStringcurrentDevice로 반환된 UIDevice 객체의 systemVersion 읽기 결과로 설정합니다.

    2. 통합 플랫폼 버전 문자열 생성platformReturnedVersionString과 함께 실행한 결과를 반환합니다.

  4. platform이 "Windows"인 경우:

    1. 가능하다면(즉, Windows 10 이상) platformReturnedVersionStringWindows.Foundation.UniversalApiContract 정수 버전 쿼리 결과를 문자열로 변환한 값으로 설정합니다. 그렇지 않으면 platformReturnedVersionString레거시 Windows 버전 번호 얻기 결과로 설정합니다.

    2. 통합 플랫폼 버전 문자열 생성platformReturnedVersionString과 함께 실행한 결과를 반환합니다.

  5. platformVersionComponentList리스트로 설정합니다.

  6. platform이 "macOS"인 경우:

    1. macOSVersionNSProcessInfo 객체의 processInfo 정보 agent로 얻은 operatingSystemVersion 속성으로 설정합니다.

    2. Append macOSVersionmajorVersion, minorVersion, patchVersion 컴포넌트(순서대로)를 platformVersionComponentList에 추가합니다.

  7. 기타 platform 값인 경우:

    1. Append 1~3개의 버전 파트를 platform에서 동작하는 다른 브라우저와 상호운용성을 가장 잘 이끌 포맷으로 platformVersionComponentList에 추가합니다.

    2. platformVersionComponentList의 길이가 3 미만인 동안, "0"platformVersionComponentList에 추가합니다.

  8. platformVersionComponentList를 U+002E FULL STOP(.) 구분자로 연결한 결과를 반환합니다.

레거시 Windows 버전 번호 얻기를 위해, 다음 단계를 실행합니다:

  1. major를 Win32 GetVersionEx API로 반환된 OSVERSIONINFOdwMajorVersion 값으로 설정합니다.

  2. minor를 Win32 GetVersionEx API로 반환된 OSVERSIONINFOdwMinorVersion 값으로 설정합니다.

  3. major6이고 minor3(즉, Windows 8.1)이면 "0.3"을 반환합니다.

  4. major6이고 minor2(즉, Windows 8)이면 "0.2"를 반환합니다.

  5. major6이고 minor1(즉, Windows 7)이면 "0.1"을 반환합니다.

  6. 그 외에는 "0"을 반환합니다.

통합 플랫폼 버전 문자열 생성을 위해, input 문자열이 주어지면 다음 단계를 실행합니다:

  1. platformVersionComponentList리스트로, index를 0으로 설정합니다.

  2. platformVersionUnprocessedTokenListinput을 U+002E FULL STOP 문자(.)로 strictly splitting리스트로 설정합니다.

  3. index가 3 미만인 동안:

    1. indexplatformVersionUnprocessedTokenList 길이보다 작으면:

      1. platformVersionUnprocessedTokenList[index]가 unsigned integer이면 문자열로 변환해 platformVersionComponentList에 추가합니다.

      2. 그렇지 않으면 "0"platformVersionComponentList에 추가합니다.

    2. 그 외(index가 길이 이상이면):

      1. "0"platformVersionComponentList에 추가합니다.

    3. index를 1 증가시킵니다.

  4. platformVersionComponentList를 U+002E FULL STOP(.) 구분자로 연결한 결과를 반환합니다.

헤더의 ABNF:

Sec-CH-UA-Platform-Version = sf-string

3.11. 'Sec-CH-UA-WoW64' 헤더 필드

Sec-CH-UA-WoW64 요청 헤더 필드는 서버에 유저 에이전트 바이너리가 64비트 Windows에서 32비트 모드로 실행 중인지 여부를 제공합니다. 구조화 헤더이며, 값은 반드시 불리언이어야 합니다 [rfc9651].

헤더의 ABNF:

Sec-CH-UA-WoW64 = sf-boolean

참고: 다음 client hints token 집합으로 이 클라이언트 힌트들을 활성화할 수 있습니다: Sec-CH-UA, Sec-CH-UA-Arch, Sec-CH-UA-Bitness, Sec-CH-UA-Form-Factors, Sec-CH-UA-Full-Version, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA-WoW64

4. 인터페이스

dictionary NavigatorUABrandVersion {
  DOMString brand;
  DOMString version;
};

dictionary UADataValues {
  DOMString architecture;
  DOMString bitness;
  sequence<NavigatorUABrandVersion> brands;
  sequence<DOMString> formFactors;
  sequence<NavigatorUABrandVersion> fullVersionList;
  DOMString model;
  boolean mobile;
  DOMString platform;
  DOMString platformVersion;
  DOMString uaFullVersion; // deprecated in favor of fullVersionList
  boolean wow64;
};

dictionary UALowEntropyJSON {
  sequence<NavigatorUABrandVersion> brands;
  boolean mobile;
  DOMString platform;
};

[Exposed=(Window,Worker)]
interface NavigatorUAData {
  readonly attribute FrozenArray<NavigatorUABrandVersion> brands;
  readonly attribute boolean mobile;
  readonly attribute DOMString platform;
  Promise<UADataValues> getHighEntropyValues(sequence<DOMString> hints);
  UALowEntropyJSON toJSON();
};

interface mixin NavigatorUA {
  [SecureContext] readonly attribute NavigatorUAData userAgentData;
};

Navigator includes NavigatorUA;
WorkerNavigator includes NavigatorUA;

참고: 유저 에이전트 정보의 고엔트로피 부분은 Promise를 통해 조회되며, 유저 에이전트가 노출을 잠재적으로 시간 소요가 큰 확인(예: 사용자에게 권한 요청)을 거치게 할 수 있도록 합니다.

4.1. 처리 모델

4.1.1. WindowOrWorkerGlobalScope

유저 에이전트brands와 연결되어 있으며, 이는 리스트로, create brandssignificant version과 함께 실행해 생성합니다.

모든 WindowOrWorkerGlobalScope 객체는 brands frozen array와 연결되어 있으며, FrozenArray<NavigatorUABrandVersion>입니다. 초기 값은 create frozen array유저 에이전트brands에 대해 실행한 결과입니다.

또한, 모든 WindowOrWorkerGlobalScope 객체는 full version list frozen array와 연결되어 있으며, FrozenArray<NavigatorUABrandVersion>입니다. create frozen arraycreate brandsfull version을 함께 실행한 결과입니다.

4.1.2. 브랜드 생성

create brandsversion type과 함께 요청받으면, 다음 단계를 실행합니다:

  1. list리스트로 설정합니다.

  2. Assert version type이 "full version" 또는 "significant version"이어야 합니다.

  3. brand별로, 해당 유저 에이전트 또는 동등 클래스를 대표하는 각 brand에 대해:

    1. version문자열로, 다음에 따라 초기화:

      1. version type이 "full version"이면 versionfull version에 해당하는 문자열로 설정합니다.

      2. version type이 "significant version"이면 versionsignificant version에 해당하는 문자열로 설정합니다.

    2. dictNavigatorUABrandVersion 딕셔너리로 새로 생성하고, brandbrand로, versionversion으로 설정합니다.

    3. dictlist에 추가합니다.

  4. 유저 에이전트는 다음 단계를 실행해야 합니다:

    1. 추가item 하나를 list에 추가합니다. NavigatorUABrandVersion 딕셔너리로, brand임의 브랜드로, version임의 버전 생성version type과 함께 실행한 결과로 설정합니다.

    2. listitem 순서를 랜덤화합니다.

    참고: 이러한 임의 컴포넌트 생성 시 캐싱 변동을 최소화하려면, 빌드 시점에 결정하고 유저 에이전트의 significant version 라이프타임 전체에 걸쳐 동일하게 유지하는 방법도 있을 수 있습니다.

    참고: 이러한 랜덤화 방식의 타이밍 및 목적에 대해서는 § 7.2 GREASE 유사 UA 브랜드 목록을 참고하세요.

  5. list를 반환합니다.

동등 클래스는 서로 호환된다고 판단되는 브라우저 그룹을 의미합니다. 예를 들어, 동일 렌더링 엔진을 공유하면 동등 클래스가 될 수 있습니다.

4.1.3. 임의 브랜드 및 버전 값 생성

임의 브랜드 생성을 위해, 유저 에이전트는 다음 단계를 반드시 수행해야 합니다:

  1. arbitraryBrand문자열로, ASCII 알파벳과 0x20(SP)로 구성합니다. arbitraryBrand는 하나 이상의 0x20(SP) 바이트를 반드시 포함하며, 20 ASCII 바이트 이하로 해야 하며, 시작과 끝이 0x20(SP)로 시작하거나 끝나면 안 됩니다.

  2. arbitraryBrandListASCII 공백으로 arbitraryBrand 분할 결과로 설정합니다.

  3. greaseyStack스택으로 설정합니다.

  4. greaseyChars를 다음 ASCII 바이트 리스트 « 0x20(SP), 0x28(왼쪽 괄호), 0x29(오른쪽 괄호), 0x2D(-), 0x2E(.), 0x2F(/), 0x3A(:), 0x3B(;), 0x3D(=), 0x3F(?), 0x5F(_) »로 설정합니다.

  5. index를 0으로 설정합니다.

  6. indexarbitraryBrandList 크기 - 1 미만인 동안:

    1. greaseyStack에 greaseyChars에서 무작위로 선택한 item을 push합니다.

    2. index를 1 증가시킵니다.

  7. greaseyBrandList리스트로 설정하고, index를 0으로 설정합니다.

  8. greaseyStack비어있지 않으면:

    1. arbitraryBrandList[index]greaseyBrandList에 추가합니다.

    2. itemgreaseyStack에서 pop합니다.

    3. itemgreaseyBrandList에 추가합니다.

    4. index를 1 증가시킵니다.

  9. arbitraryBrandList[index]greaseyBrandList에 추가합니다.

  10. 선두 및 후미 ASCII 공백 제거greaseyBrandList를 연결(구분자 없음)한 결과를 반환합니다.

이 알고리즘은 greaseyChars로 시작하거나 끝나지 않는 임의 브랜드를 생성해야 하며, 구현 경험상 이는 웹 호환성에 중요합니다.

추가적으로, 구조화 헤더가 0x22(") 및 0x5C(\) 이스케이프를 허용하지만, 이 문자들은 방화벽과 호환성 이슈를 유발할 수 있습니다.

임의 버전 생성을 위해 version type이 주어지면, 다음 단계를 수행합니다:

  1. version type이 "full version" 또는 "significant version"임을 assert합니다.

  2. arbitrary version문자열로, 다음에 따라 초기화:

    1. version type이 "full version"이면 arbitrary versionfull version의 포맷과 일치하지만 값은 다르게 설정합니다.

    2. version type이 "significant version"이면 arbitrary versionsignificant version의 포맷과 일치하지만 값은 다르게 설정합니다.

  3. arbitrary version을 반환합니다.

참고: 유저 에이전트는 버전 체크가 제대로 작동하도록 임의로 낮은 버전을 전송할 수 있으며, 시간이 지남에 따라 버전을 변경해야 합니다.

4.1.4. 브랜드-버전 목록 생성

브랜드-버전 목록 생성을 위해 brandsversion type이 주어지면, 다음 단계를 수행합니다:

  1. list리스트로, 초기값은 빈 리스트입니다.

  2. version type이 "full version" 또는 "significant version"임을 assert합니다.

  3. brands의 각 brand에 대해:

    1. version을 문자열로, 다음에 따라 초기화:

      1. version type이 "full version"이면 versionfull version에 해당하는 문자열로 설정합니다.

      2. version type이 "significant version"이면 versionsignificant version에 해당하는 문자열로 설정합니다.

    2. parameter딕셔너리로, 초기값은 빈 딕셔너리입니다.

    3. parameter["param_key"]를 "v"로 설정합니다.

    4. parameter["param_value"]를 version으로 설정합니다.

    5. pairbrandbrandparameter로 구성된 튜플로 설정합니다.

    6. pairlist에 추가합니다.

  4. list를 반환합니다.

4.1.5. Getter

brands 속성의 get 시 this관련 글로벌 객체brands frozen array를 반환해야 합니다.

mobile 속성의 get 시 유저 에이전트mobileness를 반환해야 합니다.

platform 속성의 get 시 유저 에이전트platform brand를 반환해야 합니다.

4.1.6. getHighEntropyValues 메서드

getHighEntropyValues(hints) 메서드는 반드시 다음 단계를 실행해야 합니다:

  1. p현재 realm에서 생성된 새로운 promise로 설정합니다.

  2. uaData를 새 UADataValues로 설정합니다.

  3. uaData["brands"] 를 this관련 글로벌 객체brands frozen array로 설정합니다.

  4. uaData["mobile"] 를 유저 에이전트mobileness로 설정합니다.

  5. uaData["platform"] 를 유저 에이전트platform brand로 설정합니다.

  6. this관련 글로벌 객체연결된 Documentch-ua-high-entropy-values 기능을 사용할 수 없다면, puaData로 resolve 합니다.

  7. 그 외에는 다음 단계를 병렬로 실행합니다:

    1. hints에 "architecture"가 포함되면, uaData["architecture"] 를 유저 에이전트platform architecture로 설정합니다.

    2. hints에 "bitness"가 포함되면, uaData["bitness"] 를 유저 에이전트platform bitness로 설정합니다.

    3. hints에 "formFactors"가 포함되면, uaData["formFactors"] 를 유저 에이전트form-factors로 설정합니다.

    4. hints에 "fullVersionList"가 포함되면, uaData["fullVersionList"] 를 this관련 글로벌 객체full version list frozen array로 설정합니다.

    5. hints에 "model"이 포함되면, uaData["model"] 를 유저 에이전트model로 설정합니다.

    6. hints에 "platformVersion"이 포함되면, uaData["platformVersion"] 를 platform version 얻기platform brand와 함께 실행한 결과로 설정합니다.

    7. hints에 "uaFullVersion"이 포함되면, uaData["uaFullVersion"]

    8. hints에 "wow64"가 포함되면, uaData["wow64"] 를 유저 에이전트의 wow64-ness로 설정합니다.

    9. permission task source에서 resolve puaData로 큐에 넣습니다.

  8. p를 반환합니다.

4.1.7. toJSON 메서드

toJSON() 메서드는 반드시 다음 단계를 실행해야 합니다:

  1. uaLowEntropyData를 새 UALowEntropyJSON으로 설정합니다.

  2. uaLowEntropyData["brands"] 를 this관련 글로벌 객체brands frozen array로 설정합니다.

  3. uaLowEntropyData["mobile"] 를 유저 에이전트mobileness로 설정합니다.

  4. uaLowEntropyData["platform"] 를 유저 에이전트platform brand로 설정합니다.

  5. uaLowEntropyData를 반환합니다.

5. Permissions-Policy 통합

이 명세는 문자열 "ch-ua-high-entropy-values"로 식별되는 정책 제어 기능을 정의합니다. 기본 허용 목록'*'입니다. 이 값은 특정 문서가 getHighEntropyValues() API를 통해 고엔트로피 client hint 값을 반환할 수 있는지 결정합니다.

참고: 특정 문서가 "ch-ua-high-entropy-values" 기능을 사용할 수 없으면, getHighEntropyValues() API는 편의를 위해 계속 저엔트로피 값을 반환합니다.

6. 보안 및 개인정보 보호 고려사항

6.1. 보안 전송

Client Hint는 비보안 엔드포인트로 전달되지 않습니다([RFC8942] 2.2.1절의 보안 전송 요구사항 참고). 즉, 유저 에이전트 정보는 평문 채널로 유출되지 않아 네트워크 공격자가 특정 에이전트의 행동 프로파일을 구축할 기회를 줄입니다.

6.2. 위임

Client Hint는 Permissions Policy ([permissions-policy-1])를 통해 최상위 페이지에서 위임됩니다. 이는 유저 에이전트 정보가 하위 리소스 요청에 함께 전달될 가능성을 줄이며, 수동 지문 채취 위험을 줄입니다.

이 위임은 append client hints to request의 일부로 정의됩니다.

6.3. 지문 채취

User Agent Client Hints의 주요 목표는 User-Agent 헤더 필드를 통해 웹에 기본적으로 노출되는 엔트로피 양을 줄이는 것이며, 이는 수동 지문 채취에 사용될 수 있습니다.

유저 에이전트는 어떤 힌트를 제공할지 결정할 수 있으며, 비편집된 User-Agent 헤더 필드에 포함될 정보 이상의 추가 정보를 제공하지 않는다는 기대가 있습니다. 유저 에이전트는 제공을 원하지 않는 값에 대해 빈 문자열을 주거나 힌트 반환 자체를 거부할 수 있습니다.

하지만 일부 또는 모든 힌트가 1차 또는 위임된 3차 당사자에 의해 능동 지문 채취에 사용될 수 있습니다. § 6.4 접근 제한에서 언급한 바와 같이, 유저 에이전트는 사용자 지문 채취를 적극적으로 수행하는 당사자에 대한 접근 정책을 검토해야 합니다.

유저 에이전트는 개별 사용자 또는 소수 사용자에게만 고유한 GREASE 유사 브랜드 목록을 통해 지문 채취 벡터가 생기지 않도록 주의해야 합니다. 오히려, 임의 브랜드를 많은 사용자(예: 해당 major version 전체 사용자)에게 공유하는 전략을 채택해야 합니다.

6.4. 접근 제한

위에서 정의한 Client Hint 정보는 유저 에이전트 및 실행 기기에 대한 많은 정보를 공개합니다. 유저 에이전트는 이 정보 접근을 허가하기 전 반드시 판단을 해야 하며, 앞서 언급한 보안 전송 및 위임 요구사항 이상으로 추가 제한을 적용할 수 있습니다. 예를 들어, 유저 에이전트는 다운로드 의도가 있는 요청에만 platform architecture 또는 platform bitness를 공개해 서버가 적합한 바이너리를 제공하도록 할 수 있습니다. 또한 사용자가 서버에 공개할 값에 대해 직접 제어하거나 명시적 사용자 상호작용(권한 프롬프트 또는 설정 인터페이스)을 통해 접근을 제한할 수 있습니다.

7. 구현 고려사항

7.1. 'User-Agent' 헤더

유저 에이전트는 정보 구체성을 줄여 이 문서의 Client Hint 모델을 우선함으로써 User-Agent 헤더 사용을 점진적으로 폐기해야 합니다. 헤더는 단기간 내 완전히 제거하기는 불가능할 것이며, 기존 사이트의 콘텐츠 협상 코드가 여전히 필요로 하기 때문입니다(최근 브라우저의 어려움 예시는 [Rossi2015] 참고).

권장될 수 있는 한 가지 접근법은 각 유저 에이전트User-Agent 헤더 값을 고정하고, "like Gecko" 및 "AppleWebKit/537.36" 등의 오래된 선언을 계속 유지하여 하위 호환성을 확보하는 것입니다. 버전 번호를 먼저 고정하고, 이후 플랫폼 및 모델 정보를 더 일반적으로 변경하여 헤더가 제공하는 지문을 줄일 수 있습니다.

7.2. GREASE 유사 UA 브랜드 목록

역사는 유저 에이전트가 사이트의 sniffing 스크립트를 우회하고 UA 기반 허용/차단 리스트로 사용자가 차단되지 않게 하려고 브랜드 정보를 거짓으로 보내는 유인이 크다는 점을 보여줍니다.

기대치 재설정은 브랜드 리스트 남용을 단기적으로 억제할 수 있지만, 장기적으로는 효과가 없을 수 있습니다. 네트워크 프로토콜 세계는 GREASE 개념을 도입했습니다([I-D.ietf-tls-grease]). 이 개념을 차용해 문제를 해결할 수 있습니다.

유저 에이전트brands가 한 개 이상일 경우 브랜드 리스트의 표준화된 처리를 장려할 수 있습니다. 랜덤하게 추가된 의도적으로 잘못된, 콤마로 구분된 항목들이 포함되면, 특정 값이 특정 위치에 고정되는 현상을 줄일 수 있습니다.

예시를 살펴봅시다:

유저 에이전트brands에 하나 이상의 값을 반드시 포함해야 하며, 그 중 하나는 임의 값이어야 합니다.

brands의 값 순서는 시간이 지남에 따라 바뀌어야 하며, 헤더 수신자가 특정 값이 특정 위치에 있다고 가정하는 것을 방지해야 합니다.

GREASE 전략 선택 시, 유저 에이전트는 캐싱 변동성과 분석 활용 사례를 고려해 동일 유저 에이전트 버전 간 변동성을 최소화해야 합니다.

참고: 캐싱 및 분석 변동성을 최소화하려면 UA 셋의 GREASE 부분을 빌드 시점에 결정하여 해당 유저 에이전트의 significant version 라이프타임 전체에 동일하게 유지하는 방법도 있습니다.

7.3. 'Sec-CH-' 접두어

유저랜드 JavaScript 코드가 UA-CH 헤더에 영향을 주거나 수정하지 못하게 하면 다양한 보안상의 이점이 있습니다. 동시에, 사용 사례 중에서도 사용자가 직접 헤더를 수정해야 할 정당한 사례는 없는 것 같습니다.

따라서 TAG와의 논의에 따라, JavaScript(예: fetch 또는 Service Workers)를 통한 쓰기 접근을 금지하고, 브라우저 제어 Client Hint로 구분하여 CORS preflight 없이 요청에 포함·문서화할 수 있도록 하는 것이 합리적입니다.

따라서 이 명세에서 정의한 요청 헤더는 Sec-CH- 접두어를 포함합니다.

8. IANA 고려사항

이 문서는 Sec-CH-UA, Sec-CH-UA-Arch, Sec-CH-UA-Bitness, Sec-CH-UA-Form-Factors, Sec-CH-UA-Full-Version, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA-WoW64, 그리고 Sec-CH-UA-Full-Version-List HTTP 요청 헤더 필드를 정의하여 영구 메시지 헤더 필드 레지스트리에 등록할 예정입니다 ([RFC3864]).

또한 User-Agent 헤더 필드의 사용 중단(deprecate)을 의도합니다.

8.1. 'Sec-CH-UA' 헤더 필드

헤더 필드 이름: Sec-CH-UA

적용 프로토콜: http

상태: 표준

작성자/변경 관리자: IETF

명세 문서: 본 명세서 (§ 3.1 'Sec-CH-UA' 헤더 필드)

8.2. 'Sec-CH-UA-Arch' 헤더 필드

헤더 필드 이름: Sec-CH-UA-Arch

적용 프로토콜: http

상태: 표준

작성자/변경 관리자: IETF

명세 문서: 본 명세서 (§ 3.2 'Sec-CH-UA-Arch' 헤더 필드)

8.3. 'Sec-CH-UA-Bitness' 헤더 필드

헤더 필드 이름: Sec-CH-UA-Bitness

적용 프로토콜: http

상태: 표준

작성자/변경 관리자: IETF

명세 문서: 본 명세서 (§ 3.3 'Sec-CH-UA-Bitness' 헤더 필드)

8.4. 'Sec-CH-UA-Form-Factors' 헤더 필드

헤더 필드 이름: Sec-CH-UA-Form-Factors

적용 프로토콜: http

상태: 표준

작성자/변경 관리자: IETF

명세 문서: 본 명세서 (§ 3.4 'Sec-CH-UA-Form-Factors' 헤더 필드)

8.5. 'Sec-CH-UA-Full-Version' 헤더 필드

헤더 필드 이름: Sec-CH-UA-Full-Version

적용 프로토콜: http

상태: 사용 중단됨(deprecated)

작성자/변경 관리자: IETF

명세 문서: 본 명세서 (§ 3.5 'Sec-CH-UA-Full-Version' 헤더 필드)

8.6. 'Sec-CH-UA-Full-Version-List' 헤더 필드

헤더 필드 이름: Sec-CH-UA-Full-Version-List

적용 프로토콜: http

상태: 표준

작성자/변경 관리자: IETF

명세 문서: 본 명세서 (§ 3.6 'Sec-CH-UA-Full-Version-List' 헤더 필드)

8.7. 'Sec-CH-UA-Mobile' 헤더 필드

헤더 필드 이름: Sec-CH-UA-Mobile

적용 프로토콜: http

상태: 표준

작성자/변경 관리자: IETF

명세 문서: 본 명세서 (§ 3.7 'Sec-CH-UA-Mobile' 헤더 필드)

8.8. 'Sec-CH-UA-Model' 헤더 필드

헤더 필드 이름: Sec-CH-UA-Model

적용 프로토콜: http

상태: 표준

작성자/변경 관리자: IETF

명세 문서: 본 명세서 (§ 3.8 'Sec-CH-UA-Model' 헤더 필드)

8.9. 'Sec-CH-UA-Platform' 헤더 필드

헤더 필드 이름: Sec-CH-UA-Platform

적용 프로토콜: http

상태: 표준

작성자/변경 관리자: IETF

명세 문서: 본 명세서 (§ 3.9 'Sec-CH-UA-Platform' 헤더 필드)

8.10. 'Sec-CH-UA-Platform-Version' 헤더 필드

헤더 필드 이름: Sec-CH-UA-Platform-Version

적용 프로토콜: http

상태: 표준

작성자/변경 관리자: IETF

명세 문서: 본 명세서 (§ 3.10 'Sec-CH-UA-Platform-Version' 헤더 필드)

8.11. 'Sec-CH-UA-WoW64' 헤더 필드

헤더 필드 이름: Sec-CH-UA-WoW64

적용 프로토콜: http

상태: 표준

작성자/변경 관리자: IETF

명세 문서: 본 명세서 (§ 3.11 'Sec-CH-UA-WoW64' 헤더 필드)

8.12. 'User-Agent' 헤더 필드

헤더 필드 이름: User-Agent

적용 프로토콜: http

상태: 사용 중단됨(deprecated)

작성자/변경 관리자: IETF

명세 문서: 본 명세서 (§ 7.1 'User-Agent' 헤더), 그리고 [rfc9110] 5.5.3절

9. 감사의 글

다음 분들께 본 명세서에 대한 소중한 피드백과 기여에 감사드립니다: Aaron Tagliaboschi, Ali Beyad, ArkUmbra, Dustin Mitchell, Erik Anderson, jasonwee, Luke Williams, Mike West, Martin Thomson, 그리고 Toru Kobayashi.

정합성

문서 관례

정합성 요구사항은 설명적 단언과 RFC 2119 용어의 조합으로 표현됩니다. 규범적 부분에서 “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, “OPTIONAL”이라는 주요 단어들은 RFC 2119에서 설명된 대로 해석되어야 합니다. 다만, 가독성을 위해 이 단어들은 이 명세서에서 모두 대문자로 표기되지 않습니다.

이 명세서의 모든 텍스트는 명시적으로 비규범적, 예시, 참고로 표시된 부분을 제외하고는 규범적입니다. [RFC2119]

이 명세서의 예시는 “for example”이라는 문구로 도입하거나, class="example"로 규범적 텍스트와 구분해 아래처럼 제시됩니다:

이것은 정보성 예시의 예입니다.

정보성 참고는 “Note”라는 단어로 시작하며, class="note"로 규범적 텍스트와 구분해 아래처럼 표시됩니다:

참고, 이것은 정보성 참고입니다.

색인

이 명세서에서 정의된 용어

참조로 정의된 용어

참고 문헌

규범 참고 문헌

[CLIENT-HINTS-INFRASTRUCTURE]
Client Hints Infrastructure. Draft Community Group Report. URL: https://wicg.github.io/client-hints-infrastructure/
[COMPAT]
Mike Taylor. Compatibility Standard. Living Standard. URL: https://compat.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[PERMISSIONS-POLICY-1]
Ian Clelland. Permissions Policy. URL: https://w3c.github.io/webappsec-permissions-policy/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[RFC8942]
I. Grigorik; Y. Weiss. HTTP Client Hints. February 2021. Experimental. URL: https://www.rfc-editor.org/rfc/rfc8942
[RFC9651]
M. Nottingham; P-H. Kamp. Structured Field Values for HTTP. September 2024. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc9651
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

정보 참고 문헌

[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 4. URL: https://drafts.csswg.org/css-values-4/
[FacebookYearClass]
Chris Marra; Daniel Weaver. Year class: A classification system for Android. URL: https://engineering.fb.com/android/year-class-a-classification-system-for-android/
[FINGERPRINTING-GUIDANCE]
Nick Doty; Tom Ritter. Mitigating Browser Fingerprinting in Web Specifications. URL: https://w3c.github.io/fingerprinting-guidance/
[I-D.ietf-tls-grease]
David Benjamin. Applying GREASE to TLS Extensibility. ID. URL: https://tools.ietf.org/html/draft-ietf-tls-grease
[Janc2014]
Artur Janc; Michal Zalweski. Technical analysis of client identification mechanisms. URL: https://dev.chromium.org/Home/chromium-security/client-identification-mechanisms#TOC-Browser-level-fingerprints
[RFC3864]
G. Klyne; M. Nottingham; J. Mogul. Registration Procedures for Message Header Fields. September 2004. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc3864
[RFC9110]
R. Fielding, Ed.; M. Nottingham, Ed.; J. Reschke, Ed.. HTTP Semantics. June 2022. Internet Standard. URL: https://httpwg.org/specs/rfc9110.html
[Rossi2015]
The Microsoft Edge Rendering Engine that makes the Web just work. URL: https://channel9.msdn.com/Events/WebPlatformSummit/2015/The-Microsoft-Edge-Rendering-Engine-that-makes-the-Web-just-work#time=9m45s

IDL 색인

dictionary NavigatorUABrandVersion {
  DOMString brand;
  DOMString version;
};

dictionary UADataValues {
  DOMString architecture;
  DOMString bitness;
  sequence<NavigatorUABrandVersion> brands;
  sequence<DOMString> formFactors;
  sequence<NavigatorUABrandVersion> fullVersionList;
  DOMString model;
  boolean mobile;
  DOMString platform;
  DOMString platformVersion;
  DOMString uaFullVersion; // deprecated in favor of fullVersionList
  boolean wow64;
};

dictionary UALowEntropyJSON {
  sequence<NavigatorUABrandVersion> brands;
  boolean mobile;
  DOMString platform;
};

[Exposed=(Window,Worker)]
interface NavigatorUAData {
  readonly attribute FrozenArray<NavigatorUABrandVersion> brands;
  readonly attribute boolean mobile;
  readonly attribute DOMString platform;
  Promise<UADataValues> getHighEntropyValues(sequence<DOMString> hints);
  UALowEntropyJSON toJSON();
};

interface mixin NavigatorUA {
  [SecureContext] readonly attribute NavigatorUAData userAgentData;
};

Navigator includes NavigatorUA;
WorkerNavigator includes NavigatorUA;