가격 확인 복호화

광고 소재가 입찰에서 낙찰되면 Google은 광고 소재를 정의하는 HTML 스니펫 또는 VAST URL에 WINNING_PRICE 매크로가 포함되어 있는 경우 낙찰 가격을 알려줄 수 있습니다. Google은 암호화된 형식으로 낙찰 가격을 반환합니다. 다음 주제에서는 애플리케이션이 낙찰가 정보를 복호화하는 방법을 설명합니다.

WINNING_PRICE 매크로는 예를 들어 광고의 일부로 렌더링되는 보이지 않는 픽셀 요청과 함께 광고 소재에 포함될 수 있습니다.

<div>
  <script language='JavaScript1.1' src='https://example.com?creativeID=5837243'/>
  <img src='https://example.com/t.gif?price=%%WINNING_PRICE%%' width='1' height='1'/>
</div>

WINNING_PRICE 매크로는 동영상 광고 소재의 VAST URL에도 포함될 수 있습니다 (VAST의 노출 URL에는 포함될 수 없음).

https://example.com/vast/v?price=%%WINNING_PRICE%%

시나리오

  1. 애플리케이션에서 Google에 반환하는 HTML 스니펫 또는 VAST URL에 WINNING_PRICE 매크로가 포함되어 있습니다.
  2. Google은 패딩되지 않은 웹 보안 base64 인코딩 (RFC 3548)을 적용하여 이 매크로를 낙찰 가격으로 대체합니다.
  3. 스니펫은 선택한 형식으로 확인 메시지를 전달합니다. 예를 들어 광고의 일부로 렌더링된 보이지 않는 픽셀 요청의 URL로 확인이 전달될 수 있습니다.
  4. 서버에서 웹 보안 애플리케이션 base64가 낙찰가 정보를 디코딩하고 결과를 복호화합니다.

종속 항목

SHA-1 HMAC를 지원하는 Openssl과 같은 암호화 라이브러리가 필요합니다.

샘플 코드

샘플 코드는 Java 및 C++로 제공되며 privatedatacommunicationprotocol 프로젝트에서 다운로드할 수 있습니다.

  • 자바 샘플 코드는 Apache Commons 프로젝트의 base64 디코더를 사용합니다. 참조 구현은 필요한 부분을 포함하고 있고 독립 실행형이므로 Apache Commons 코드를 다운로드할 필요가 없습니다.

  • C++ 샘플 코드는 OpenSSL base64 BIO 메서드를 사용합니다. 이 코드는 웹 보안 base64로 인코딩된 문자열 (RFC 3548)을 사용하여 디코딩합니다. 일반적으로 웹 보안 base64 문자열은 '=' 패딩을 '.'로 대체하지만(따옴표는 가독성을 위해 추가된 것이며 프로토콜에 포함되지 않음) 매크로 대체로 암호화된 가격을 패딩할 수 없습니다. OpenSSL은 패딩되지 않은 문자열에 문제가 있으므로 참조 구현에서는 패딩을 추가합니다.

인코딩

낙찰된 가격 암호화 및 복호화에는 두 개의 공유 보안 비밀 키가 필요합니다. 무결성 키와 암호화 키(각각 i_keye_key라고 함) 두 키 모두 계정 설정 시 웹 보안 base64 문자열로 제공되며 Authorized Buyers 페이지의 입찰자 설정 > RTB 설정 > 암호화 키에서 찾을 수 있습니다.

무결성 및 암호화 키의 예:

skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o=  // Encryption key (e_key)
arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo=  // Integrity key (i_key)

키는 웹에 안전하게 디코딩된 후 애플리케이션에서 base64로 디코딩되어야 합니다.

e_key = WebSafeBase64Decode('skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o=')
i_key = WebSafeBase64Decode('arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo=')

암호화 스키마

가격은 크기 오버헤드를 최소화하면서 적절한 보안을 보장하도록 설계된 커스텀 암호화 체계로 암호화됩니다. 암호화 체계는 키 HMAC 알고리즘을 사용하여 고유한 노출 이벤트 ID를 기반으로 보안 비밀 패드를 생성합니다.

암호화된 가격은 길이가 28바이트로 고정되어 있습니다. 16바이트 초기화 벡터, 8바이트 암호문, 4바이트 무결성 서명으로 구성됩니다. 암호화된 가격은 RFC 3548에 따라 웹 보안 base64로 인코딩되며 패딩 문자가 생략됩니다. 따라서 28바이트로 암호화된 가격은 지불된 낙찰 가격과 관계없이 38자의 웹 보안 base-64 문자열로 인코딩됩니다.

암호화된 가격의 예:

YWJjMTIzZGVmNDU2Z2hpN7fhCuPemCce_6msaw  // 100 CPI micros
YWJjMTIzZGVmNDU2Z2hpN7fhCuPemCAWJRxOgA  // 1900 CPI micros
YWJjMTIzZGVmNDU2Z2hpN7fhCuPemC32prpWWw  // 2700 CPI micros

암호화된 형식은 다음과 같습니다.

{initialization_vector (16 bytes)}{encrypted_price (8 bytes)}
{integrity (4 bytes)}

가격은 <price xor HMAC(encryption_key, initialization_vector)>로 암호화되므로 복호화 시 HMAC(encryption_key,initialization_vector)을 계산하고 암호화된 가격으로 xor를 계산하여 암호화를 취소합니다. 무결성 단계에서는 <HMAC(integrity_key, price||initialization_vector)>의 4바이트를 사용합니다. 여기서 ||는 연결입니다.

입력
iv 초기화 벡터 (16바이트 - 노출에 고유함)
e_key 암호화 키 (32바이트 - 계정 설정 시 제공)
i_key 무결성 키 (32바이트 - 계정 설정 시 제공)
price (8바이트 - 계정 통화의 마이크로(micro))
Notation
hmac(k, d) k 키를 사용하는 d 데이터의 SHA-1 HMAC
a || b a 문자열을 b 문자열로 연결합니다.
의사코드
pad = hmac(e_key, iv)  // first 8 bytes
enc_price = pad <xor> price
signature = hmac(i_key, price || iv)  // first 4 bytes

final_message = WebSafeBase64Encode( iv || enc_price || signature )

복호화 스키마

복호화 코드는 암호화 키를 사용하여 가격을 복호화하고 무결성 키로 무결성 비트를 확인해야 합니다. 키는 설정 중에 제공됩니다. 구현을 구성하는 방법에 관한 세부정보에는 제한사항이 없습니다. 대부분의 경우 샘플 코드를 필요에 따라 조정할 수 있어야 합니다.

입력
e_key 암호화 키, 32바이트 - 계정 설정 시 제공
i_key 무결성 키, 32바이트 - 계정 설정 시 제공
final_message 38자(영문 기준)의 웹 보안 base64 인코딩
의사코드
// Base64 padding characters are omitted.
// Add any required base64 padding (= or ==).
final_message_valid_base64 = AddBase64Padding(final_message)

// Web-safe decode, then base64 decode.
enc_price = WebSafeBase64Decode(final_message_valid_base64)

// Message is decoded but remains encrypted.
(iv, p, sig) = enc_price // Split up according to fixed lengths.
price_pad = hmac(e_key, iv)
price = p <xor> price_pad

conf_sig = hmac(i_key, price || iv)
success = (conf_sig == sig)

부실 응답 공격 감지

부실 응답 또는 재생 공격을 감지하려면 시간대 차이를 고려한 후 시스템 시간과 크게 다른 타임스탬프가 있는 응답을 필터링하는 것이 좋습니다.

초기화 벡터의 처음 8바이트에 타임스탬프가 포함됩니다. 다음 C++ 함수로 읽을 수 있습니다.

void GetTime(const char* iv, struct timeval* tv) {
    uint32 val;
    memcpy(&val, iv, sizeof(val));
    tv->tv_sec = htonl(val);
    memcpy(&val, iv+sizeof(val), sizeof(val));
    tv->tv_usec = htonl(val)
}

타임스탬프는 다음 C++ 코드를 사용하여 사람이 읽을 수 있는 형식으로 변환할 수 있습니다.

struct tm tm;
localtime_r(&tv->tv_sec, &tm);

printf("%04d-%02d-%02d|%02d:%02d:%02d.%06ld",
       tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
       tm.tm_hour, tm.tm_min, tm.tm_sec,
       tv_.tv_usec);

Java 라이브러리

암호화 알고리즘을 구현하여 낙찰가를 인코딩하고 디코딩하는 대신 DoubleClickCrypto.java를 사용할 수 있습니다. 자세한 내용은 암호화를 참고하세요.