가격 확인 복호화

광고 소재가 입찰에서 낙찰되면 광고 소재에 ${AUCTION_PRICE} 매크로가 포함된 경우 Google에서 낙찰가를 알려줄 수 있습니다.

매크로가 펼쳐지면 낙찰가를 암호화된 형식으로 반환합니다. 광고의 일부로 렌더링된 보이지 않는 픽셀 요청을 사용하여 광고 소재에 포함할 수 있습니다.

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

${AUCTION_PRICE} 매크로는 동영상 광고 소재의 VAST URL에 포함할 수 있지만 VAST의 노출 URL에는 포함할 수 없습니다.

https://example.com/vast/v?price=${AUCTION_PRICE}

시나리오

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

종속 항목

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

샘플 코드

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

  • Java 샘플 코드는 Apache 공통 프로젝트의 base64 디코더를 사용합니다. 참조 구현에 필요한 부분이 포함되어 있으므로 Apache commons 코드를 다운로드할 필요가 없습니다.

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

인코딩

낙찰가 암호화 및 복호화에는 비공개이지만 공유된 키 2개가 필요합니다. 무결성 키와 암호화 키(각각 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바이트 - 계정 통화의 마이크로 단위)
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 웹에 적합한 base64로 인코딩된 38자
의사코드
// 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를 사용할 수 있습니다. 자세한 내용은 암호화를 참고하세요.