URL 및 해싱

이 문서는 Update API (v4): fullHashes.find 메서드에 적용됩니다.

개요

세이프 브라우징 목록은 가변 길이의 SHA256 해시로 구성됩니다(목록 콘텐츠 참고). 로컬 또는 서버의 세이프 브라우징 목록과 비교하여 URL을 확인하려면 클라이언트는 먼저 URL의 해시 접두사를 계산해야 합니다.

URL의 해시 프리픽스를 계산하려면 다음 안내를 따르세요.

  1. URL을 표준화합니다(표준화 참조).
  2. URL의 서픽스/접두사 표현식을 만듭니다(서픽스/접두사 표현식 참조).
  3. 각 접미사/접두사 표현식의 전체 길이 해시를 계산합니다(해시 계산 참조).
  4. 각 전체 길이 해시의 해시 프리픽스를 계산합니다(해시 프리픽스 계산 참조).

이 단계는 세이프 브라우징 서버가 세이프 브라우징 목록을 유지관리하는 데 사용하는 프로세스를 반영합니다.

표준화

먼저 클라이언트가 URL을 파싱하고 RFC 2396에 따라 URL을 유효하게 만들었다고 가정합니다. URL에 국제화된 도메인 이름 (IDN)이 사용되는 경우 클라이언트는 URL을 ASCII Punycode 표현으로 변환해야 합니다. URL은 경로 구성요소를 포함해야 합니다. 즉, 후행 슬래시 ('http://google.com/')가 있어야 합니다.

먼저 URL에서 탭 (0x09), CR (0x0d), LF (0x0a) 문자를 삭제합니다. 이러한 문자의 이스케이프 시퀀스 (예: '%0a')를 삭제하지 마세요.

둘째, URL이 프래그먼트로 끝나면 프래그먼트를 제거합니다. 예를 들어 'http://google.com/#frag'을 'http://google.com/'으로 줄입니다.

셋째, 더 이상 퍼센트 이스케이프 처리되지 않을 때까지 URL을 반복적으로 퍼센트 이스케이프 취소합니다.

호스트 이름을 정규화하려면 다음 단계를 따르세요.

URL에서 호스트 이름을 추출한 후 다음을 수행합니다.

  1. 모든 선행 및 후행 점은 삭제합니다.
  2. 연속된 점을 단일 점으로 바꿉니다.
  3. 호스트 이름을 IP 주소로 파싱할 수 있으면 점으로 구분된 십진수 4개로 정규화합니다. 클라이언트는 8진수, 16진수, 4개 미만의 구성요소를 포함한 모든 법적 IP 주소 인코딩을 처리해야 합니다.
  4. 전체 문자열을 소문자로 표기합니다.

경로를 정규화하려면 다음 단계를 따르세요.

  1. '/../'를 '/'로 바꾸고 '/../'를 앞의 경로 구성요소와 함께 삭제하여 경로의 '/../' 및 '/./' 시퀀스를 해결합니다.
  2. 연속된 슬래시 실행을 단일 슬래시 문자로 바꿉니다.

이러한 경로 표준화를 쿼리 매개변수에 적용하지 마세요.

URL에서 <= ASCII 32, 127 이상, '#' 또는 '%'인 모든 문자를 퍼센트 이스케이프 처리합니다. 이스케이프 처리 시 대문자 16진수 문자를 사용해야 합니다.

다음은 표준화 구현을 검증하는 데 도움이 되는 테스트입니다.

Canonicalize("http://host/%25%32%35") = "http://host/%25";
Canonicalize("http://host/%25%32%35%25%32%35") = "http://host/%25%25";
Canonicalize("http://host/%2525252525252525") = "http://host/%25";
Canonicalize("http://host/asdf%25%32%35asd") = "http://host/asdf%25asd";
Canonicalize("http://host/%%%25%32%35asd%%") = "http://host/%25%25%25asd%25%25";
Canonicalize("http://www.google.com/") = "http://www.google.com/";
Canonicalize("http://%31%36%38%2e%31%38%38%2e%39%39%2e%32%36/%2E%73%65%63%75%72%65/%77%77%77%2E%65%62%61%79%2E%63%6F%6D/") = "http://168.188.99.26/.secure/www.ebay.com/";
Canonicalize("http://195.127.0.11/uploads/%20%20%20%20/.verify/.eBaysecure=updateuserdataxplimnbqmn-xplmvalidateinfoswqpcmlx=hgplmcx/") = "http://195.127.0.11/uploads/%20%20%20%20/.verify/.eBaysecure=updateuserdataxplimnbqmn-xplmvalidateinfoswqpcmlx=hgplmcx/";
Canonicalize("http://host%23.com/%257Ea%2521b%2540c%2523d%2524e%25f%255E00%252611%252A22%252833%252944_55%252B") = "http://host%23.com/~a!b@c%23d$e%25f^00&11*22(33)44_55+";
Canonicalize("http://3279880203/blah") = "http://195.127.0.11/blah";
Canonicalize("http://www.google.com/blah/..") = "http://www.google.com/";
Canonicalize("www.google.com/") = "http://www.google.com/";
Canonicalize("www.google.com") = "http://www.google.com/";
Canonicalize("http://www.evil.com/blah#frag") = "http://www.evil.com/blah";
Canonicalize("http://www.GOOgle.com/") = "http://www.google.com/";
Canonicalize("http://www.google.com.../") = "http://www.google.com/";
Canonicalize("http://www.google.com/foo\tbar\rbaz\n2") ="http://www.google.com/foobarbaz2";
Canonicalize("http://www.google.com/q?") = "http://www.google.com/q?";
Canonicalize("http://www.google.com/q?r?") = "http://www.google.com/q?r?";
Canonicalize("http://www.google.com/q?r?s") = "http://www.google.com/q?r?s";
Canonicalize("http://evil.com/foo#bar#baz") = "http://evil.com/foo";
Canonicalize("http://evil.com/foo;") = "http://evil.com/foo;";
Canonicalize("http://evil.com/foo?bar;") = "http://evil.com/foo?bar;";
Canonicalize("http://\x01\x80.com/") = "http://%01%80.com/";
Canonicalize("http://notrailingslash.com") = "http://notrailingslash.com/";
Canonicalize("http://www.gotaport.com:1234/") = "http://www.gotaport.com/";
Canonicalize("  http://www.google.com/  ") = "http://www.google.com/";
Canonicalize("http:// leadingspace.com/") = "http://%20leadingspace.com/";
Canonicalize("http://%20leadingspace.com/") = "http://%20leadingspace.com/";
Canonicalize("%20leadingspace.com/") = "http://%20leadingspace.com/";
Canonicalize("https://www.securesite.com/") = "https://www.securesite.com/";
Canonicalize("http://host.com/ab%23cd") = "http://host.com/ab%23cd";
Canonicalize("http://host.com//twoslashes?more//slashes") = "http://host.com/twoslashes?more//slashes";

서픽스/프리픽스 표현식

URL이 표준화되면 다음 단계는 접미사/접두사 표현식을 만드는 것입니다. 각 서픽스/프리픽스 표현식은 이 예와 같이 호스트 서픽스 (또는 전체 호스트)와 경로 프리픽스 (또는 전체 경로)로 구성됩니다.

서픽스/프리픽스 표현식동등한 정규 표현식
a.b/mypath/
http\:\/\/.*\.a\.b\/mypath\/.*
c.d/full/path.html?myparam=a
http\:\/\/.*.c\.d\/full\/path\.html?myparam=a

클라이언트는 최대 30개의 가능한 호스트 서픽스 및 경로 프리픽스 조합을 구성합니다. 이러한 조합에서는 URL의 호스트 및 경로 구성요소만 사용합니다. 스키마, 사용자 이름, 비밀번호 및 포트가 삭제됩니다. URL에 쿼리 매개변수가 포함된 경우에는 하나 이상의 조합에 전체 경로와 쿼리 매개변수가 포함됩니다.

호스트의 경우 클라이언트는 최대 5개의 다른 문자열을 시도합니다. 각 유형은 다음과 같습니다.

  • URL의 정확한 호스트 이름입니다.
  • 마지막 5개 구성요소부터 시작하여 연속적으로 선행 구성요소를 삭제하여 최대 4개의 호스트 이름을 형성합니다. 최상위 도메인은 건너뛸 수 있습니다. 호스트가 IP 주소인 경우에는 이러한 추가 호스트 이름을 확인하면 안 됩니다.

경로의 경우 클라이언트는 최대 6개의 다른 문자열을 시도합니다. 다음과 같습니다.

  • 쿼리 매개변수를 포함한 URL의 정확한 경로입니다.
  • 쿼리 매개변수가 없는 URL의 정확한 경로입니다.
  • 루트 (/)에서 시작하여 후행 슬래시를 포함한 경로 구성요소를 이어서 추가하여 형성된 4개의 경로입니다.

다음 예시는 확인 동작을 보여줍니다.

URL http://a.b.c/1/2.html?param=1의 경우 클라이언트는 다음과 같은 가능한 문자열을 시도합니다.

a.b.c/1/2.html?param=1
a.b.c/1/2.html
a.b.c/
a.b.c/1/
b.c/1/2.html?param=1
b.c/1/2.html
b.c/
b.c/1/

URL http://a.b.c.d.e.f.g/1.html의 경우 클라이언트는 다음과 같은 가능한 문자열을 시도합니다.

a.b.c.d.e.f.g/1.html
a.b.c.d.e.f.g/
(Note: skip b.c.d.e.f.g, since we'll take only the last five hostname components, and the full hostname)
c.d.e.f.g/1.html
c.d.e.f.g/
d.e.f.g/1.html
d.e.f.g/
e.f.g/1.html
e.f.g/
f.g/1.html
f.g/

URL http://1.2.3.4/1/의 경우 클라이언트는 다음과 같은 가능한 문자열을 시도합니다.

1.2.3.4/1/
1.2.3.4/

해시 계산

서픽스/접두사 표현식 집합이 생성되면 다음 단계는 각 표현식의 전체 길이 SHA256 해시를 계산하는 것입니다. 해시 계산을 검증하는 데 사용할 수 있는 단위 테스트 (유사 C 형식)가 아래에 나와 있습니다.

FIPS-180-2의 예시:

Unit Test (in pseudo-C)

// Example B1 from FIPS-180-2
string input1 = "abc";
string output1 = TruncatedSha256Prefix(input1, 32);
int expected1[] = { 0xba, 0x78, 0x16, 0xbf };
assert(output1.size() == 4);  // 4 bytes == 32 bits
for (int i = 0; i < output1.size(); i++) assert(output1[i] == expected1[i]);

// Example B2 from FIPS-180-2
string input2 = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
string output2 = TruncatedSha256Prefix(input2, 48);
int expected2[] = { 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06 };
assert(output2.size() == 6);
for (int i = 0; i < output2.size(); i++) assert(output2[i] == expected2[i]);

// Example B3 from FIPS-180-2
string input3(1000000, 'a');  // 'a' repeated a million times
string output3 = TruncatedSha256Prefix(input3, 96);
int expected3[] = { 0xcd, 0xc7, 0x6e, 0x5c, 0x99, 0x14, 0xfb, 0x92,
                    0x81, 0xa1, 0xc7, 0xe2 };
assert(output3.size() == 12);
for (int i = 0; i < output3.size(); i++) assert(output3[i] == expected3[i]);

해시 프리픽스 계산

마지막으로 클라이언트는 각 전체 길이 SHA256 해시의 해시 접두사를 계산해야 합니다. 세이프 브라우징의 경우 해시 프리픽스는 SHA256 해시의 최상위 4~32바이트로 구성됩니다.

FIPS-180-2의 예시:

  • FIPS-180-2의 예시 B1
    • 입력은 'abc'입니다.
    • SHA256 다이제스트는 ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad입니다.
    • 32비트 해시 접두사는 ba7816bf입니다.
  • FIPS-180-2의 예시 B2
    • 입력은 'abcdbcdecdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'입니다.
    • SHA256 다이제스트는 248d6a61 d20638b8 e5c02693 0c3e6039 a33ce459 64ff2167 f6ecedd4 19db06c1입니다.
    • 48비트 해시 접두사는 248d6a61 d206입니다.