안전한 결제 확인

W3C 후보 권고안 초안,

이 문서에 대한 추가 정보
이 버전:
https://www.w3.org/TR/2025/CRD-secure-payment-confirmation-20251209/
최신 공개 버전:
https://www.w3.org/TR/secure-payment-confirmation/
편집자 초안:
https://w3c.github.io/secure-payment-confirmation/
이전 버전:
히스토리:
https://www.w3.org/standards/history/secure-payment-confirmation/
구현 보고서:
https://wpt.fyi/results/secure-payment-confirmation
피드백:
GitHub
사양 내 인라인
편집자:
(Google)
이전 편집자:
(Google)
공헌자:
Ian Jacobs (W3C)
Nick Burris (Google)
테스트 스위트:
https://wpt.fyi/results/secure-payment-confirmation/

초록

안전한 결제 확인(SPC)은 결제 거래 중 간소화된 인증을 지원하는 웹 API입니다. 이는 다양한 인증 프로토콜 내에서 사용될 수 있도록 설계되었으며, 여러 가맹점에서 인증을 확장할 수 있고, 사용자가 거래 내역을 확인했음을 암호화 증거로 생성할 수 있습니다.

이 문서의 상태

이 섹션은 본 문서가 발행된 시점의 상태를 설명합니다. 최신 W3C 출판물 목록과 이 기술 보고서의 가장 최근 버전은 W3C 표준 및 드래프트 색인에서 확인할 수 있습니다.

본 문서는 웹 결제 워킹 그룹에서 권고안 경로(Recommendation track)를 이용해 후보 권고안 초안(Candidate Recommendation Draft)으로 발행되었습니다.

이전 후보 권고 스냅샷 이후 변경사항은 GitHub 변경 로그를 참조하세요.

후보 권고안으로 발행된다고 해서 W3C 및 회원이 이를 지지한다는 의미는 아닙니다. 후보 권고안 초안은 워킹 그룹이 다음 후보 권고 스냅샷에 포함할 계획인 이전 후보 권고안의 변경사항을 통합합니다.

이는 초안 문서로 언제든지 갱신, 대체 또는 폐기될 수 있습니다. 진행 중인 작업이므로 다른 용도로 인용하는 것은 부적절합니다.

이 사양이 제안 권고안(Proposed Recommendation)으로 진행되기 위해서는 사용자 에이전트에서 두 가지 독립적이고 상호운용 가능한 구현을 보여야 합니다. 관련 구현 보고서를 참조하세요.

이 후보 권고안은 2023년 8월 1일 이전에 제안 권고안으로 진전될 것으로 예상되지 않습니다.

웹 결제 워킹 그룹은 이슈 목록을 관리합니다.

후보 권고안으로 발행된다고 해서 W3C 및 회원이 이를 지지한다는 의미는 아닙니다. 후보 권고안 초안은 워킹 그룹이 다음 후보 권고 스냅샷에 포함할 계획인 이전 후보 권고안의 변경사항을 통합합니다.

이 문서는 W3C 특허 정책을 준수하는 그룹에 의해 제작되었습니다. W3C는 그룹의 결과물과 관련하여 공개된 특허 공개 목록을 유지하고 있습니다. 그 페이지에는 특허 공개 방법도 포함되어 있습니다. 어떤 개인이 본질적인 청구를 포함한다고 생각되는 특허에 대한 실제 정보를 알고 있으면 W3C 특허 정책 6절에 따라 정보를 공개해야 합니다.

이 문서는 2025년 8월 18일 W3C 프로세스 문서의 적용을 받습니다.

1. 소개

이 절과 하위 절은 비규범적입니다.

이 규격은 웹에서 결제 흐름 중 강력한 인증 방법 사용을 가능하게 하는 API를 정의합니다. 이는 [webauthn-3] 와 동일한 인증 장점과 사용자 프라이버시 초점을 제공하는 것을 목표로 하며, 결제 처리를 위한 요구사항을 충족하도록 개선되었습니다.

[webauthn-3] 와 유사하게, 이 규격은 사용자와 관련된 두 가지 프로세스를 정의합니다. 첫 번째는 § 3 등록 (이전 "등록(enrollment)"), 여기서 사용자와 릴라잉 파티 사이의 관계가 생성됩니다. 두 번째는 § 4 인증 - 안전한 결제 확인 결제 방식에서 사용자가 릴라잉 파티 (중개 결제 서비스 제공자를 통해 가능)로부터 챌린지에 응답하여 특정 결제에 동의합니다.

이 규격의 목표 중 하나는 결제 과정 중 인증 마찰을 줄이는 것이며, 그 한 요소는 사용자가 한 번 등록으로 가능한 한 많은 인증을 수행할 수 있도록 하는 것입니다. 즉, 릴라잉 파티의 동의 하에, 이상적으로 사용자는 "한 번 등록"으로 최초로 등록한 가맹점 오리진뿐만 아니라 모든 가맹점 오리진(및 결제 서비스 제공자를 통해)에서 인증할 수 있어야 합니다.

이를 위해, 안전한 결제 확인의 중요한 기능은 상점(또는 다른 단체)이 릴라잉 파티를 대신하여 인증 절차를 시작할 수 있다는 것입니다. 릴라잉 파티는 자격 증명 생성 시, 해당 동작을 허용하도록 명시적으로 동의해야 합니다.

기능적으로, 이 규격은 PaymentRequest API를 위한 새로운 결제 방식을 정의하고, 결제 특화 데이터 구조, 장치 바인딩 및 결제 맥락에서 API 호출을 허용하기 위한 가정 완화를 위해 WebAuthn 확장을 추가하여 [webauthn-3] 를 확장합니다.

1.1. 사용 사례

[webauthn-3] 가 웹을 위한 일반 인증 기능을 제공하지만, 아래와 같은 사용 사례를 통해 이 규격에서 정의된 결제 특화 확장 기능의 가치를 설명합니다.

온라인 거래에 대해 암호 기반 인증의 일반적 사용 사례는 이미 충분히 확립되어 있다고 가정합니다.

1.1.1. 거래 확인의 암호화 증거

많은 온라인 결제 시스템에서 결제 수단을 발급하는 기관(예: 은행)은 인증을 통해 사기 방지하려고 합니다. [webauthn-3] 및 이 규격을 사용하면 인증자를 통해 상점의 오리진, 거래 금액 및 통화 등 결제 특화 정보를 암호화 서명할 수 있습니다. 은행은 릴라잉 파티로서, 결제 승인 결정 시 해당 결제 특화 정보의 서명을 검증할 수 있습니다.

은행이 일반 [webauthn-3] 를 사용할 경우, 검증할 결제 특화 정보는 WebAuthn의 challenge 에 저장되어야 합니다. 이는 다음과 같은 문제를 야기합니다:

  1. challenge 필드의 오용(이 필드는 재사용 공격 방지 용도입니다).

  2. 명세가 없어 각 은행이 결제 특화 정보를 챌린지에 어떠한 형식과 인코딩으로 담을지 직접 설계해야 하므로, 배포가 복잡해지고 파편화가 증가합니다.

  3. 규제에 따라 사용자에게 결제 특화 정보가 표시되고 사용자 동의 증거가 요구될 수 있습니다. 일반 [webauthn-3] 에서는 이러한 정보 표시를 위한 UX가 명세되어 있지 않고, challenge 필드의 정보에 대한 UX가 제공되지 않습니다.

이러한 제한을 해소하기 위해 다음과 같은 안전한 결제 확인(SPC) 행동 양식이 도입됩니다:

  1. challenge 필드는 일반 [webauthn-3] 처럼 오직 재사용 공격 방지 용도로만 사용됩니다.

  2. SPC는 결제 특화 정보의 형식을 명세합니다. 이를 통해 범용 검증 코드와 테스트 스위트 개발이 가능합니다.

  3. SPC는 사용자 에이전트가 결제 특화 정보를 사용자에게 악의적인 웹사이트(또는 신뢰된 웹사이트 내 악의적으로 삽입된 자바스크립트 코드)가 우회할 수 없는 방식으로 표시했음을 보장합니다.

    • 결제 특화 정보는 CollectedClientData 딕셔너리에 포함되며, 자바스크립트에서 변조할 수 없습니다.

    참고: 결제 생태계에서 은행 등 이해당사자들은 오늘날 브라우저의 TLS, iframe 등 Web 기능을 이용해 결제에 충분한 신뢰를 두고 있습니다. 현재 명세는 웹 결제의 보안성 및 사용성을 향상시키려 설계되었습니다.

1.1.2. 가맹점의 인증 제어

가맹점들은 결제 진행 중 사용자 이탈을 방지하고 인증 마찰을 줄이고자 합니다. 릴라잉 파티 (예: 은행)가 [webauthn-3] 로 사용자를 인증하려는 경우, 보통 iframe을 통해 인증합니다. 하지만 가맹점들은 인증 사용자 경험을 직접 관리하면서도 릴라잉 파티 가 인증 결과를 검증할 수 있기를 원합니다.

이러한 제한을 해소하기 위해 다음과 같은 안전한 결제 확인(SPC) 행동 양식이 도입됩니다:

이 기능의 추가적 장점은 릴라잉 파티가 인증 프론트엔드 경험을 직접 구축하지 않아도 된다는 것입니다. 대신 결제 서비스 제공자가 상점들을 위해 이를 구축할 가능성이 높아집니다.

참고: 인증 사용자 경험을 직접 제공하고 싶은 릴라잉 파티는 SPC를 iframe에서 사용할 수 있습니다.

1.1.3. 장치 바인딩의 암호화 증거

결제 업계에서는 장치 소유 신호가 2차 요소로 중요한 역할을 합니다. WebAuthn은 하나의 자격증명이 여러 장치에서 동기화될 수 있는 패스키를 지원합니다(Web Authentication § 1.2.1 다중 장치 자격증명 사용 소비자). 동기화는 로그인 사용 사례에서 사용자 경험을 개선하지만, 일부 규제 환경에서는 동기화된 패스키만으로는 장치 소유 요건을 충족하지 못한다는 우려가 있습니다.

이런 우려를 해소하기 위해 사용자 에이전트가 생성하는 보조 공개/개인 키 쌍의 비밀키가 특정 장치에서만 존재(및 사용)하도록 만듭니다. 이러한 키와 SPC 내 사용을 브라우저 바인드 키 라고 합니다.

1.2. API 사용 예시 시나리오

이 절에서는 안전한 결제 확인을 위한 시나리오와 해당 API 사용 예시 코드를 설명합니다. 이것은 예시 흐름일 뿐이며 API 사용 범위를 제한하지 않습니다.

1.2.1. 결제 시 등록

이 흐름은 사용자가 어떤 상점에서 결제하는 과정 중 발급 은행이 새로운 자격증명을 생성해 저장하는 첫 등록 흐름입니다.

  1. 사용자가 merchant.example에 접속하여 상품을 선택하고 결제 과정을 진행합니다. 결제 수단 정보를 입력한 뒤 결제를 희망(예: "Pay" 버튼 누름)합니다.

  2. 상점은 발급 은행과 밴드 밖(out-of-band)(예: 다른 프로토콜 사용)으로 통신합니다. 발급 은행은 사용자 확인을 요청하고, 상점이 iframe에서 열 수 있도록 은행 제어의 URL을 제공합니다.

  3. 상점은 bank.example에 iframe을 열고 allow 속성을 "publickey-credentials-create"로 설정합니다.

  4. iframe 내에서 발급 은행은 기존 방식(예: SMS OTP 등)으로 사용자를 확인합니다. 확인 후, 은행은 향후 결제에 SPC 인증 등록을 권유합니다.

  5. 사용자가 동의(예: 은행 UX의 "Register" 버튼 클릭)하면, 은행은 iframe 내에서 코드를 실행합니다(예시 아래 참고).

  6. 사용자는 WebAuthn 등록 과정을 거칩니다. 새 자격증명이 생성되고, 발급 은행 서버 DB에 사용자 및 결제 수단과 연계해 저장됩니다.

  7. 확인이 완료되면 은행 iframe이 닫히고, 상점은 사용자의 결제 과정을 끝마칩니다.

이 방식으로 사용자를 등록하는 샘플 코드는 다음과 같습니다:

if (!window.PublicKeyCredential) { /* 클라이언트가 지원 불가. 에러 처리 필요. */ }

const publicKey = {
  // 챌린지는 은행 서버가 생성 후 iframe으로 전달해야 합니다.
  challenge: new Uint8Array([21,31,105 /* 서버에서 생성된 무작위 바이트 29개 */]),

  // 릴라잉 파티:
  rp: {
    name: "Fancy Bank",
  },

  // 사용자:
  user: {
    // WebAuthn 일부. SPC에서는 필수 아님
    // 그러나 은행 서버에서 이 정보를 사용자 식별 목적으로 활용 가능
    // 동일 사용자에 대해 불일치 값 사용 시, 여러 자격증명 생성으로 UX 마찰(선택 화면)이 생길 수 있음
    id: Uint8Array.from(window.atob("MIIBkzCCATigAwIBAjCCAZMwggE4oAMCAQIwggGTMII="), c=>c.charCodeAt(0)),
    name: "jane.doe@email.example",
    displayName: "Jane Doe",
  },

  // 이 예시에서 릴라잉 파티는 ES256 또는 RS256 자격증명 둘 다 허용하지만 ES256을 선호
  pubKeyCredParams: [
    {
      type: "public-key",
      alg: -7 // "ES256"
    },
    {
      type: "public-key",
      alg: -257 // "RS256"
    }
  ],

  authenticatorSelection: {
    userVerification: "required",
    residentKey: "required",
    authenticatorAttachment: "platform",
  },

  timeout: 360000,  // 6분

  // SPC 자격증명임을 나타냅니다. 현재 필수로써
  // 브라우저가 이 자격증명이 SPC와 관련됨을 인지할 수 있습니다.
  // 교차 오리진 iframe 내 자격증명 생성도 이로써 가능, 본 예시에서 필수
  //
  // 향후 명세 버전에서는 이 확장 필요성을 제거할 수 있음.
  extensions: {
    "payment": {
      isPayment: true,
      // 허용 알고리즘 목록(선택 사항). 비어있거나 미포함시 pubKeyCredParams(ES256, RS256)가 기본,
      // 여기선 ES256, RS256 허용하며 RS256이 선호됨
      browserBoundPubKeyCredParams: [
        {
          type: "public-key",
          alg: -257 // "RS256"
        },
        {
          type: "public-key",
          alg: -7 // "ES256"
        }
      ]
    }
  }
};

// 참고: 아래 호출 시 인증기에서 UI를 표시합니다.
navigator.credentials.create({ publicKey })
  .then(function (newCredentialInfo) {
    // 신규 자격증명 정보를 서버에 검증 및 등록 용도로 전송
  }).catch(function (err) {
    // 인증기가 없거나 사용자가 동의 거부. 적절히 처리 필요.
  });

1.2.2. 가맹점 사이트에서 인증

이미 등록된 자격증명을 가진 사용자가 거래를 수행하고, 발급 은행과 가맹점이 안전한 결제 확인(SPC)를 사용하고자 할 때의 흐름입니다.

  1. 사용자가 merchant.example에 방문하여 상품을 선택한 뒤 결제 과정을 진행합니다. 결제 수단 정보를 입력하고, 결제 의사를 표시합니다(예: "결제" 버튼 클릭).

  2. 가맹점은 결제 수단의 발급 은행과 밴드 밖(out-of-band)으로 통신합니다(예: 다른 프로토콜 사용). 발급 은행은 사용자 확인을 요청하며, 동시에 API 사용에 필요한 정보를 제공하여 SPC 사용을 승인했음을 상점에 알립니다. 이 정보에는 챌린지와 해당 사용자 및 결제 수단과 연계된 자격증명 ID 목록이 포함됩니다.

  3. 가맹점은 아래 예시 코드를 실행합니다.

  4. 사용자는 SPC UX에 표시된 결제 특화 정보에 동의하고, 이후 WebAuthn 인증 절차를 수행합니다. 서명된 암호문(브라우저 바인드 키 출력 포함)은 가맹점에 반환됩니다(AuthenticationExtensionsPaymentOutputs 포함).

  5. 가맹점은 서명된 암호문을 밴드 밖으로 발급 은행에 전달합니다. 발급 은행은 암호문을 검증하여 사용자의 유효성과 표시된 결제 특화 정보, 사용자의 거래 동의 여부를 확인합니다. 발급 은행이 거래를 승인하고, 가맹점은 결제 과정을 완료합니다. 발급 은행은 브라우저 바인드 키의 공개키, browserBoundPublicKey를 저장합니다.

사용자 인증을 위한 샘플 코드는 아래와 같습니다. 이 예시 코드는 promise 처리를 쉽게하기 위해 await/async 접근을 가정합니다.

/* securePaymentConfirmationAvailability는 브라우저가 SPC를 지원하는지 표시하며, */
/* 사용자가 이 기기에서 바로 사용할 수 있는 자격증명을 가진다는 뜻은 아닙니다. */
const spcAvailable =
  PaymentRequest &&
  PaymentRequest.securePaymentConfirmationAvailability &&
  (await PaymentRequest.securePaymentConfirmationAvailability()) === 'available';
if (!spcAvailable) {
  /* 브라우저가 SPC를 지원하지 않을 경우, 기존 방식으로 결제 흐름을 대체해야 합니다. */
}

const request = new PaymentRequest([{
  supportedMethods: "secure-payment-confirmation",
  data: {
    // 은행에서 받은 자격증명 ID 목록
    credentialIds,

    rpId: "fancybank.example",

    // 챌린지 또한 은행에서 제공됨
    challenge: new Uint8Array([21,31,105 /* 은행에서 생성된 임의 바이트 29개 */]),

    instrument: {
      displayName: "FancyBank Platinum Card",
      details: "****1234 | 01/29",
      icon: "https://fancybank.example/card-art.png",
    },

    payeeName: "Merchant Shop",
    payeeOrigin: "https://merchant.example",

    paymentEntitiesLogos: [
      {
        url: "https://fancybank.example/logo.png",
        label: "Fancy Bank",
      },
      {
        url: "https://securenetwork.example/logo.png",
        label: "Secure Network",
      },
    ],

    // 요청자의 로컬 환경 지정
    locale: ["en"],

    timeout: 360000,  // 6분

    // 허용 알고리즘 목록(선택 사항, 기본값 ES256/RS256). 
    // 이 예시에서는 ES256, RS256 모두 허용하며 ES256 우선. 
    // 브라우저 바인드 키가 이미 존재할 경우 새로 만들지 않으므로 목록은 키가 없을 때만 사용됨.
    browserBoundPubKeyCredParams: [
      {
        type: "public-key",
        alg: -7 // "ES256"
      },
      {
        type: "public-key",
        alg: -257 // "RS256"
      }
    ]
  }], {
    total: {
      label: "총액",
      amount: {
        currency: "USD",
        value: "5.00",
      },
    },
  });

try {
  const response = await request.show();
  await response.complete('success');

  // response.data는 PublicKeyCredential로, clientDataJSON에 거래 검증용 정보 포함(은행에서 사용).

  /* response.data를 발급 은행 검증 용도로 전송 */
} catch (err) {
  /* SPC를 사용할 수 없으면 기존 결제 흐름으로 대체해야 함 */
}

2. 용어

SPC 자격증명

이 규격에서 정의된 동작에 사용될 수 있는 WebAuthn 자격증명입니다.

이 규격은 릴라잉 파티가 SPC 자격증명을 다른 인증 흐름(예: 로그인)에 어떻게(혹은 하지 않고) 사용할지 제한하려는 목적이 아닙니다.

참고: 현 규격 버전에서는 릴라잉 파티가 자격증명이 1자 파티 또는 3자 파티 맥락에서 사용될 수 있도록 명확히 동의(opt-in)해야 함을 요구합니다. 장기적으로는 모든 WebAuthn 자격증명이 1자 파티 맥락(즉, 릴라잉 파티 도메인)에서 SPC에 사용할 수 있고, 3자 파티 사용만 opt-in이 필요하도록 발전하는 것이 목적입니다.

3자 파티 SPC 자격증명

SPC 자격증명릴라잉 파티가 자격증명 생성 시 명확히 동의(opt-in)하여, 릴라잉 파티 이외의 주체가 Secure Payment Confirmation 인증에 이를 사용할 수 있도록 허용한 자격증명입니다.

SPC 자격증명이 3자 파티 허용 여부를 조용히 판별하는 단계

아직 정의되지 않은 프로세스이며, 사용자 에이전트가 릴라잉 파티 식별자자격증명 ID 만으로, 사용자 조작 없이 해당 ID의 자격증명이 3자 파티 SPC 자격증명인지 판별하는 단계입니다.

참고: WebAuthn 이슈 1667 참고.

현재 기기에서 자격증명 사용 가능 여부를 조용히 판별하는 단계

아직 정의되지 않은 프로세스이며, 사용자 에이전트가 릴라잉 파티 식별자자격증명 ID만으로, 사용자 조작 없이 해당 자격증명이 현 기기에서 사용 가능한지 판별하는 단계입니다(즉, WebAuthn Get 호출에 성공적으로 사용할 수 있는지 여부).

사용자 에이전트는 해당 자격증명이 사용할 수 있을 확률이 있어야만 거래 확인 UX를 사용자에게 조건부로 표시할 수 있습니다.

참고: 이 특성은 SPC 자격증명탐색가능이어야 구현 가능하므로, 본 규격에서 요구사항으로 코딩되어 있습니다.

참고: 이 특성은 WebAuthn Conditional UI Proposal에 요구되는 것과 매우 유사합니다. 둘 다 동일한 기반 API로 지원될 가능성이 높습니다.

브라우저 바인드 키

거래 상세 정보를 WebAuthn 자격증명과 추가로 서명하며, 사용자 에이전트가 특정 기기와 바인드하는 공개/개인 키 쌍입니다.

키 쌍

키 쌍은 구조체로, 공개키개인키로 구성되며, 구현 종속 타입입니다.

참고: 공개키개인키는 IANA COSE 알고리즘 레지스트리에서 참조하는 암호 알고리즘별로 사용되는 매개변수입니다 [IANA-COSE-ALGS-REG].

참고: 공개키는 등록 시 CollectedClientAdditionalPaymentRegistrationData.browserBoundPublicKey 와 결제 인증 시 CollectedClientAdditionalPaymentData.browserBoundPublicKey에 반환됩니다.

참고: 개인키BrowserBoundSignature.signature에 포함되는 암호 서명 생성에 사용됩니다. 사용자 에이전트는 개인키를 외부로 내보내지 않으며, 기기의 보안 요소에 저장할 수도 있습니다.

3. 등록

사용자를 안전한 결제 확인에 등록하려면, 릴라잉 파티가 navigator.credentials.create() 를 호출하고 payment WebAuthn 확장 을 지정해야 합니다.

테스트

참고: 이 규격에서는 브라우저가 Conditional UI 없이도 SPC 자격증명 ID를 캐시할 수 있도록 확장을 정의합니다. 향후 WebAuthn에 해당 기능이 추가된다면 SPC에서 확장 필요성을 제거할 수 있습니다. SPC 자격증명(확장 포함)은 본질적으로 완전한 WebAuthn 자격증명입니다.

참고: 등록 시, Web Authentication은 namedisplayName 을 모두 요구하지만, user 멤버 정의에 따라, 구현에서는 이후 인증 절차에서 둘 중 어떤 것도 표시할 필요가 없습니다. 두 항목 중 2023년 10월 기준 name 이 더 꾸준히 표시되고 있습니다. 개발자들은 구현 변화를 모니터링해야 합니다.

4. 인증 - 안전한 결제 확인 결제 방식

안전한 결제 확인을 통해 결제를 인증하려면, 본 규격은 다음을 정의합니다:

  1. 결제 핸들러, 안전한 결제 확인 결제 핸들러, 결제 인증 요청을 처리합니다.

  2. 이 결제 핸들러의 표준화된 결제 방식 식별자, "secure-payment-confirmation"입니다.

안전한 결제 확인 결제 핸들러는 사용자와 거래를 확인한 후 인증 절차를 실행하여 사용자를 인증하고 인증 절차를 나타내는 서명된 blob을 생성합니다.

상위 수준에서, 안전한 결제 확인용 인증은 [webauthn-3]과 유사하지만 하나의 중요한 개념적 변화가 있습니다. 안전한 결제 확인은 제3자(예: 상점)가 릴라잉 파티를 대신해 인증 절차를 유발할 수 있도록 하며, 릴라잉 파티로부터 얻은 자격증명을 별도의 경로를 통해 전달할 수 있습니다. § 1.1.2 가맹점의 인증 제어 참고.

참고: 초기에 SPC 실험을 빠르게 지원하기 위해 이 API는 기존 Payment Request 및 Payment Handler API 구현 위에 설계되었습니다. 현재는 Payment Request와 독립적인 SPC 디자인을 탐색하기로 합의되었습니다. 구체적 일정은 없지만, SPC가 Payment Request 기반에서 분리될 것으로 예상합니다. 개발자에게는 기능 탐지, 호출, 기타 API 관련 요소가 개선될 것입니다.

4.1. [payment-method-id]에서 등록

표준화된 결제 방식 레지스트리에 다음을 추가하세요: [payment-method-id]:

"secure-payment-confirmation"

안전한 결제 확인 규격.

4.2. 결제 요청 생성자 수정

PaymentRequest 객체 생성자 단계에, 4.3 단계 뒤에 새 단계를 추가합니다:

  1. 결제 방식 처리: [하위 단계 1-3 생략]

    1. seenPMIs에 "secure-payment-confirmation"이 포함돼 있고 seenPMIs의 크기가 1보다 크면 RangeError를 throw합니다.

4.3. 사용자 활성화 요구 조건 수정

PaymentRequest.show() 메서드 단계에서 2, 3단계를 다음과 같이 수정합니다:

  1. 관련 글로벌 객체에서 request일시적 활성화(transient activation)가 아니면, 사용자 에이전트는 다음을 수행할 수 있습니다:

    1. 거부된 promise"SecurityError" DOMException과 함께 반환.

  2. 그렇지 않으면, 사용자 활성화 소진관련 글로벌 객체에 대해 수행합니다.

참고: 사용자 에이전트가 사용자 활성화를 요구하지 않을 수 있으므로, 리디렉트 인증 흐름 등 활성화가 없는 상황도 지원합니다. 보안 고려는 § 11.4 사용자 활성화 요구 조건 미비 참고.

4.4. SecurePaymentConfirmationRequest 딕셔너리

dictionary SecurePaymentConfirmationRequest {
    required BufferSource challenge;
    required USVString rpId;
    required sequence<BufferSource> credentialIds;
    required PaymentCredentialInstrument instrument;
    unsigned long timeout;
    USVString payeeName;
    USVString payeeOrigin;
    sequence<> paymentEntitiesLogos;
    AuthenticationExtensionsClientInputs extensions;
    sequence<PublicKeyCredentialParameters> browserBoundPubKeyCredParams;
    sequence<USVString> locale;
    boolean showOptOut;
};

SecurePaymentConfirmationRequest 딕셔너리는 다음 멤버를 포함합니다:

challenge

릴라잉 파티가 서버에서 재사용 공격 방지용으로 생성한 난수 챌린지입니다.

rpId

자격증명의 릴라잉 파티 식별자입니다.

credentialIds

해당 결제 수단에 대한 자격증명 식별자 목록입니다.

instrument

등록 시 표시되는 결제 수단 이름과 아이콘 설명이며 거래 상세 정보와 함께 서명됩니다.

timeout

거래 상세 정보 서명 요청이 만료되기까지의 밀리초 수. 최대 1시간. 기본값 및 허용값 범위는 사용자 에이전트가 정의하며, Web Authentication은 추가 타임아웃 가이드를 제공합니다.

payeeName

이 SPC 호출의 수취인(예: 상점)의 표시 이름입니다. 선택사항이며, payeeOrigin과 함께, 또는 대신 제공할 수 있습니다.

payeeOrigin

이 SPC 호출의 수취인(예: 상점)의 origin입니다. 선택사항이며, payeeName과 함께, 또는 대신 제공할 수 있습니다.

paymentEntitiesLogos

결제를 중개하는 개체의 로고 선택 목록이며, 표시 우선순위가 높은 순서대로 나열됩니다. 사용자 에이전트가 일부 또는 전체 로고를 표시할 필요는 없으며, § 4.8 결제 가능 여부 확인 단계 참고.

참고: 이 규격은 로고의 표시 방법을 명확히 규정하지 않으며, 사용자 에이전트가 렌더링을 제어할 수 있도록 하고 다양한 조건을 고려해 정보를 적절히 표시할 수 있음. 리스트 내 로고 순서는 중요하며, 개발자가 새로운 순서를 실험해 원하는 결과를 얻을 수 있음.

extensions

지정된 자격증명에 사용해야 할 WebAuthn 확장입니다. 호출자가 결제 확장을 명시할 필요 없으며, 자동 추가됩니다.

browserBoundPubKeyCredParams

브라우저 바인드 키에 적용할 암호 알고리즘 유형을 제약하는 자격증명 허용 목록.

참고: 이 멤버는 브라우저 바인드 키가 새로 생성되어야 할 때만 사용됩니다.

locale

우선순위가 높은 순서대로 나열한 언어 태그 선택 목록입니다([BCP47] 참고). 이는 웹사이트의 지역(locale) 선호를 식별하며, 언어 우선순위 목록 [RFC4647] 형태입니다. 사용자 에이전트가 언어 협상(language negotiation) 및 지역 기반 포맷을 호출자와 함께 수행하는 데 사용 가능합니다.

showOptOut

거래 확인 UX에서 사용자가 거부할 기회를 받을지 여부입니다. 선택사항, 기본값은 false.

4.5. 결제 방식 추가 데이터 타입

이 결제 방식의 추가 데이터 타입SecurePaymentConfirmationRequest 입니다.

4.6. 안전한 결제 확인 사용 가능 여부 확인

PaymentRequest 에 정적 API가 추가되어 개발자가 안전한 결제 확인 사용 가능 여부를 간단히 확인할 수 있습니다. 이 메서드 securePaymentConfirmationAvailability()는 열거형(enum) 멤버를 반환하며, 안전한 결제 확인이 사용 가능 또는 불가능(내부 사유 설명 포함)함을 나타냅니다. 열거형 출력 사용은 개발자가 사용자 인증 옵션 안내 및 메시징에 도움됩니다.

enum SecurePaymentConfirmationAvailability {
  "available",
  "unavailable-unknown-reason",
  "unavailable-feature-not-enabled",
  "unavailable-no-permission-policy",
  "unavailable-no-user-verifying-platform-authenticator",
};

partial interface PaymentRequest {
    static Promise<SecurePaymentConfirmationAvailability> securePaymentConfirmationAvailability();
};

SecurePaymentConfirmationAvailability 열거형에는 다음 멤버가 포함됩니다:

available

사용자 에이전트가 현재 프레임에서 안전한 결제 확인 API를 사용할 수 있음으로 판단함을 나타냅니다.

참고: 이 결과는 특정 SPC 자격증명의 현재/향후 사용 가능 여부를 의미하지 않습니다.

unavailable-unknown-reason

호출 프레임에서 안전한 결제 확인 API를 사용할 수 없는 원인을 알 수 없음을 나타냅니다. 사용자 에이전트는 사용자 프라이버시 보호를 위해 항상 이 결과를 반환할 수 있습니다.

unavailable-feature-not-enabled

기능이 활성화되지 않아 호출 프레임에서 안전한 결제 확인 API를 사용할 수 없음을 나타냅니다.

unavailable-no-permission-policy

호출 프레임에서 "payment" 권한 정책이 없어 안전한 결제 확인 API를 사용할 수 없음을 나타냅니다.

unavailable-no-user-verifying-platform-authenticator

사용자 인증이 가능한 플랫폼 인증기가 없어 해당 프레임에서 안전한 결제 확인 API를 사용할 수 없음을 나타냅니다.

참고: isUserVerifyingPlatformAuthenticatorAvailable 호출로도 이 정보를 획득할 수 있으나, 개발자 편의상 본 API에도 포함시켰습니다.

securePaymentConfirmationAvailability() 메서드를 Document document에서 호출하면, 사용자 에이전트는 아래 단계를 수행합니다. 사용자 프라이버시 보호 또는 사용자 에이전트 특유의 사유로 단계 완료 불가시, 언제든지 "unavailable-unknown-reason"를 반환할 수 있습니다.

  1. 사용자 에이전트가 안전한 결제 확인을 지원하지 않거나, 지원하지만 기능이 비활성화된 경우 "unavailable-feature-not-enabled" 반환.

  2. "payment" 권한 정책이 document에 활성화 되어 있지 않으면 "unavailable-no-permission-policy" 반환.

  3. 플랫폼 인증기가 없으면 "unavailable-no-user-verifying-platform-authenticator" 반환.

  4. 기타 이유로 해당 프레임에서 안전한 결제 확인 기능이 동작하지 않으면 "unavailable-unknown-reason" 반환.

  5. "available" 반환.

개발자는 SPC 흐름 시작 여부 판단 시 다음과 같이 확인할 수 있습니다:

const spcAvailable =
    PaymentRequest &&
    PaymentRequest.securePaymentConfirmationAvailability &&
    await PaymentRequest.securePaymentConfirmationAvailability() === 'available';

참고: SPC 기능 탐지는 이미 생성된 PaymentRequest 객체에서 canMakePayment 를 호출하기보다, 정적 메서드 securePaymentConfirmationAvailability 사용을 권장합니다.

참고: 본 API의 프라이버시 고려는 § 12.5 securePaymentConfirmationAvailability를 통한 지문채취 참고.

4.7. 결제 방식 데이터 검증 단계

이 결제 방식에 대한 결제 방식 데이터 검증 단계는 입력 PaymentRequest requestSecurePaymentConfirmationRequest data에 대해 다음과 같습니다:

테스트
  1. data["credentialIds"] 가 비어 있으면 RangeError를 throw합니다.

  2. data["credentialIds"]의 각 id에 대해:

    1. id가 비어 있으면 RangeError를 throw합니다.

  3. data["challenge"] 가 null이거나 비어 있으면 TypeError를 throw합니다.

  4. data["instrument"]["displayName"] 이 비어 있으면 TypeError를 throw합니다.

  5. data["instrument"]["icon"] 이 비어 있으면 TypeError를 throw합니다.

  6. data["instrument"]["icon"]에 URL 파서를 실행합니다. 실패 반환이면 TypeError를 throw합니다.

  7. data["instrument"]["details"] 가 존재하면서 비어 있으면 TypeError를 throw합니다.

  8. data["rpId"] 이 유효한 도메인이 아니면, TypeError를 throw합니다.

  9. data["payeeName"] 와 data["payeeOrigin"] 중 둘 다 생략된 경우, TypeError를 throw합니다.

  10. data["payeeName"] 또는 data["payeeOrigin"] 중 하나라도 존재하면서 비어 있으면, TypeError를 throw합니다.

  11. data["payeeOrigin"] 이 존재 시:

    1. parsedURL = URL 파서 결과 data["payeeOrigin"].

    2. parsedURL이 실패면 TypeError를 throw합니다.

    3. parsedURLscheme가 "https"가 아니면 TypeError를 throw합니다.

  12. data["paymentEntitiesLogos"] 가 존재하면서 비어 있지 않으면:

    1. logo = data["paymentEntitiesLogos"]의 각 항목에 대해:

      1. logo["url"] 이 비어 있으면 TypeError를 throw합니다.

      2. logo["url"]에 URL 파서를 실행. 실패 반환이면 TypeError를 throw합니다.

      3. logo["label"] 이 비어 있으면 TypeError를 throw합니다.

  13. data["locale"] 존재하고 비어 있지 않으면

    1. tag = data["locale"]의 각 항목에 대해:

      1. tag이 올바른 [BCP47] 언어 태그가 아니면 TypeError를 throw합니다.

    참고: locale 은 특정 입력 멤버와 관련된 언어나 방향 메타데이터와는 구별되며, 특정 문자열 값에 대한 주장이라기보다 호출자가 요청한 지역화 경험을 나타냅니다. 자세한 논의는 § 14 국제화 고려사항 참고.

4.8. 결제 가능 여부 확인 단계

이 결제 방식에 대한 결제 가능 여부 확인 단계는 입력 SecurePaymentConfirmationRequest data에 대해 다음과 같습니다:

테스트
  1. data["payeeOrigin"] 이 존재할 경우:

    1. parsedURL = URL 파서 결과 data["payeeOrigin"].

    2. parsedURL이 실패가 아니라는 것을 assert합니다.

    3. parsedURLscheme이 "https"임을 assert합니다.

    참고: 이 사전 조건은 결제 방식 데이터 검증 단계에서 이미 확인되었습니다.

    1. data["payeeOrigin"] 을 origin의 직렬화값으로 설정합니다.

  2. 아이콘 이미지 리소스를 가져오기(fetch), «["src" → data["instrument"]["icon"]]» 를 image로 전달합니다. 실패 시:

    1. data["instrument"]["iconMustBeShown"] 이 true이면 false 반환.

    2. 그 외에는 data["instrument"]["icon"] 를 빈 문자열로 설정.

      참고: 이 결과로 RP가 해당 아이콘이 표시되지 않았음을 알 수 있으며, instrument 값의 icon이 빈 문자열입니다.

    참고: 이미지 리소스는 자격증명 매칭 여부와 관계없이 항상 가져와야 하며, 자격증명 존재 탐지(프라이버시 공격) 방지를 위해서입니다.

  3. 사용자 에이전트가 data["paymentEntitiesLogos"]의 항목을 리스트 끝에서 시작해 처음까지 제거할 수 있습니다.

    참고: 사용자 에이전트가 표시할 로고 수를 제어할 수 있으며, 리스트의 로고는 호출자에게 표시 우선순위 내림차순임을 보장합니다.

  4. logo = data["paymentEntitiesLogos"]의 각 항목에 대해:

    1. 이미지 리소스 가져오기(fetch)logo에 대해 수행하고, «["src" → logo["url"]]»를 image로 전달하고 결과를 디코드합니다.

    2. fetch 또는 decode 실패 시, logo["url"]를 빈 문자열로 설정합니다.

      참고: RP가 해당 로고가 표시되지 않았음을 알 수 있으며, paymentEntitiesLogos 값의 해당 logo의 url이 빈 문자열입니다.

    참고: 로고 리소스도 자격증명 매칭 여부와 관계없이 항상 가져와야 하며, 자격증명 존재 탐지(프라이버시 공격) 방지 목적입니다.

  5. id = data["credentialIds"]의 각 항목에 대해:

    1. 현재 기기에서 자격증명 사용 가능 여부 조용하게 판별하는 단계를 실행하여 data["rpId"] 및 id를 전달합니다. 결과가 false이면 iddata["credentialIds"]에서 제거합니다.

    2. data["rpId"] 가 관련 설정 객체의 origin이 아니면, SPC 자격증명이 3자 파티 허용 여부를 조용히 판별하는 단계를 실행하고, data["rpId"]와 id를 전달합니다. 결과가 false이면 iddata["credentialIds"]에서 제거합니다.

  6. true 반환.

4.9. 거래 확인 UX 표시

PaymentRequest.show() 가 호출되고 안전한 결제 확인 결제 핸들러 가 선택된 경우(해당 알고리즘 19~24단계), 사용자 에이전트는 반드시 사용자에게 진행 선택 또는 방법을 결정할 수 있는 사용자 인터페이스를 표시해야 합니다.

4.9.1. 사용자에게 제공되는 정보

사용자 에이전트의 구현 자유도를 제한하지 않기 위해 이 규격은 특정 UI 표시를 요구하지 않습니다. 그러나 릴라잉 파티CollectedClientPaymentData 에 포함된 정보를 신뢰할 수 있도록, 사용자 에이전트는 아래 정보를 사용자에게 반드시 전달하고 인증에 대한 사용자의 동의를 반드시 수집해야 합니다:

사용자 에이전트는 locale 정보를 활용해 웹사이트와 일치하는 언어, 포맷의 지역화된 UX 표시를 할 수 있습니다.

showOptOuttrue인 경우, 사용자 에이전트는 지정 릴라잉 파티에 대한 과정 거부(opt-out) 선택 기회를 반드시 제공해야 합니다.

테스트

4.9.2. 거래 확인 UX 결과

사용자 에이전트는 사용자가 진행 여부와 방법을 아래 옵션 중 하나로 표시할 수 있도록 해야 합니다:

사용자가 SPC 자격증명으로 인증하며 결제 진행을 희망할 경우

PaymentRequest 에 대해 user accepts the payment request algorithm 실행.

사용자가 결제를 진행하려 하지만 SPC 자격증명을 사용할 수 없거나 사용을 원치 않을 경우

아래 단계 실행:

  1. data["credentialIds"] 를 빈 리스트로 설정.

    참고: 이로 인해 PaymentRequest.show() promise가 "NotAllowedError" DOMException 으로 reject됨(§ 4.10 결제 요청 응답 단계 참고).

  2. 해당 PaymentRequest 에 대해 user accepts the payment request algorithm 실행.

테스트
사용자가 결제 진행을 원치 않을 경우

해당 PaymentRequest 에 대해 user aborts the payment request algorithm 실행.

참고: 이로 인해 PaymentRequest.show() promise는 "AbortError" DOMException 으로 reject됨.

사용자가 지정 릴라잉 파티에 대한 과정에서 opt-out을 희망할 경우

PaymentRequest.show() 를 "OptOutError" DOMException으로 reject. (§ 12.6 사용자 거부 참고)

참고: 해당 옵션은 showOptOuttrue일 때만 반드시 사용자에게 노출되어야 함.

4.9.3. 테스트 자동화 지원

현재 거래 자동화 모드가 "none"이 아니면, 사용자 에이전트는 먼저 자동화 컨텍스트에 있는지 확인해야 하며(WebDriver 보안 고려 참고), 위 정보 전달·동의 수집을 건너뛰고 아래 각 자동화 모드 값에 따라 동작해야 합니다:

"autoAccept"

사용자가 거래 세부정보를 보고 수락한 것처럼 동작.

"autoChooseToAuthAnotherWay"

사용자가 거래 세부정보를 보고 수락, 동시에 SPC 자격증명 인증 사용을 원치 않은 것처럼 동작. data["credentialIds"] 가 비어 있다면 "autoAccept"와 동일.

"autoReject"

사용자가 거래 세부정보를 보고 거절한 것처럼 동작(즉, 거래 진행 원치 않음).

"autoOptOut"

사용자가 거래 세부정보를 보고 opt-out을 희망한 것처럼 동작.

4.10. 결제 요청 응답 단계

결제 요청 응답 단계는 해당 PaymentRequest requestSecurePaymentConfirmationRequest data에 대해 다음과 같습니다.

참고: 이 단계는 사용자가 거래 확인 UX를 수락한 경우에만 실행되며, user accepts the payment request algorithm 에 의해 호출됩니다.

  1. data["credentialIds"] 가 비어 있으면, "NotAllowedError" DOMException을 throw. 이는 사용자가 SPC 인증 사용이 불가능하거나 원치 않는 경우 인증 절차 프라이버시를 보장함.

    참고: 여기서 throw되면 PaymentRequest.show() promise가 "NotAllowedError" DOMException 으로 reject됨.

  2. topOrigin = 최상위 오리진(request관련 설정 객체 기준)으로 설정.

  3. payment = AuthenticationExtensionsPaymentInputs 딕셔너리(아래 필드 갖음):

    isPayment

    불리언 값 true.

    rpId

    data["rpId"].

    topOrigin

    topOrigin

    payeeName

    data["payeeName"] 이 존재하면, 없으면 생략.

    payeeOrigin

    data["payeeOrigin"] 이 존재하면, 없으면 생략.

    paymentEntitiesLogos

    data["paymentEntitiesLogos"] 가 존재·비어 있지 않으면, 없거나 비어 있으면 생략.

    현 규격은 PaymentEntityLogo 당 단일 URL만 지원하므로 구조 복사·서명만 해도 사용자에게 무엇이 표시됐는지 표시 가능. 차후 멀티 URL 지원 시 RP에 표시된 실제 URL 전달이 필요함.

    total

    request.[[details]]["total"].

    instrument

    data["instrument"].

    browserBoundPubKeyCredParams

    data["browserBoundPubKeyCredParams"].

  4. extensions = AuthenticationExtensionsClientInputs 딕셔너리(payment 멤버에 payment 설정, 다른 멤버는 data["extensions"]에서 복사).

  5. publicKeyOpts = PublicKeyCredentialRequestOptions 딕셔너리(아래 필드 갖음):

    challenge

    data["challenge"].

    timeout

    data["timeout"].

    rpId

    data["rpId"].

    userVerification

    required.

    extensions

    extensions

    참고: 이 알고리즘은 userVerification값을 "required"로 고정(Chrome 초안 구현이 지원하는 값). 향후 WG가 다른 값(예: "preferred"/"discouraged") 수요 있으면 구현자 제안 환영.

  6. id = data["credentialIds"]의 각 항목에 대해:

    1. descriptor = PublicKeyCredentialDescriptor 딕셔너리(아래 필드 갖음):

      type

      public-key.

      id

      id

      transports

      길이 1 시퀀스로, "internal" 만 포함.

    2. descriptorpublicKeyOpts["allowCredentials"]에 append.

  7. outputCredential = Request a Credential 알고리즘 실행, «["publicKey" → publicKeyOpts]» 전달.

    참고: Chrome 초안에서는 전체 data.credentialIds가 아닌 현재 기기와 맞는 것 하나만 넘기지만, 표준은 전체 리스트를 넘김.

    참고: 이 단계는 [webauthn-3]Get 동작을 trig함.

  8. outputCredential 반환.

5. WebAuthn 확장 - "payment"

클라이언트 등록 확장인증 확장은 각각 자격증명이 안전한 결제 확인용으로 생성 또는 사용됨을 나타냅니다.

등록 시, 이 확장은 브라우저가 안전한 결제 확인 자격증명 ID를 식별·캐시할 수 있도록 합니다. 인증 시, 이 확장은 제3자가 릴라잉 파티를 대신하여 인증 절차를 실행할 수 있도록 하며, 동시에 거래 정보를 서명된 암호문에 추가합니다.

특히, 웹사이트는 navigator.credentials.get() 를 이용하여 이 확장에 직접 접근하면 안 됩니다; 인증은 반드시 PaymentRequest 에서 "secure-payment-confirmation" 결제 방식과 함께 사용해야만 활성화됩니다.

참고: 과거에는 payment 확장이 교차(origin) iframe에서 자격증명 생성을 허용했으나, 이제 WebAuthn에서 기본 허용됨(WebAuthn PR #1801).

테스트

이 테스트는 명세 라인에 직접 대응하지는 않으며, 교차(origin) iframe 내부에서 인증을 트리거할 수 있음을 테스트합니다. 해당 동작은 금지 라인이 없음을 통해 명세됩니다.


확장 식별자

payment

동작 적용 대상

등록인증

클라이언트 확장 입력
partial dictionary AuthenticationExtensionsClientInputs {
  AuthenticationExtensionsPaymentInputs payment;
};

dictionary AuthenticationExtensionsPaymentInputs {
  boolean isPayment;
  sequence<PublicKeyCredentialParameters> browserBoundPubKeyCredParams;

  // Only used for authentication.
  USVString rpId;
  USVString topOrigin;
  USVString payeeName;
  USVString payeeOrigin;
  sequence<PaymentEntityLogo> paymentEntitiesLogos;
  PaymentCurrencyAmount total;
  PaymentCredentialInstrument instrument;
};
isPayment

확장이 활성화 중임을 나타냅니다.

rpId

인증 시에만 사용되며 등록 단계에는 사용하지 않고, 웹 개발자가 직접 지정하면 안 됩니다.

topOrigin

인증 시에만 사용되며 등록 단계에는 사용하지 않고, 웹 개발자가 직접 지정하면 안 됩니다.

payeeName

인증 시에만 사용되며 등록 단계에는 사용하지 않고, 웹 개발자가 직접 지정하면 안 됩니다.

payeeOrigin

인증 시에만 사용되며 등록 단계에는 사용하지 않고, 웹 개발자가 직접 지정하면 안 됩니다.

paymentEntitiesLogos

인증 시에만 사용되며 등록 단계에는 사용하지 않고, 웹 개발자가 직접 지정하면 안 됩니다.

total

인증 시에만 사용되며 등록 단계에는 사용하지 않고, 웹 개발자가 직접 지정하면 안 됩니다.

instrument

인증 시에만 사용되며 등록 단계에는 사용하지 않고, 웹 개발자가 직접 지정하면 안 됩니다.

browserBoundPubKeyCredParams

브라우저 바인드 키에 적용되는 암호 알고리즘 유형을 제한하는 자격증명 허용 리스트입니다. 인증 시에는 웹 개발자가 SecurePaymentConfirmationRequest.browserBoundPubKeyCredParams 를 설정해야 합니다.

참고: 이 멤버가 없는 경우, PublicKeyCredentialCreationOptions.pubKeyCredParams 가 기본값이 됩니다.

클라이언트 확장 처리(등록)

새 자격증명 생성 시:

  1. 3단계 후, 다음 단계를 삽입:

  2. 13단계 전에(CollectedClientData 생성 직전)

    테스트
    1. bbk_allowed_algorithms = browserBoundPubKeyCredParams.

    2. browserBoundPubKeyCredParams비어 있다면, bbk_allowed_algorithms = PublicKeyCredentialCreationOptions.pubKeyCredParams로 설정.

    3. bbk_and_algorithm, bbk_id = 키 쌍 생성(bbk_allowed_algorithms 사용).

    4. (bbk_and_algorithm, bbk_id)가 null이면 bbk_and_algorithm, bbk_public_key 관련 하위 단계를 건너뜁니다.

    5. bbk_public_key = 브라우저 바인드 공개키 얻기(bbk_and_algorithm 사용).

  3. 13단계에서는 CollectedClientData 대신, CollectedClientPaymentData 를 생성하며, 필드는:

    payment

    CollectedClientAdditionalPaymentRegistrationData 필드로 초기화:

    browserBoundPublicKey

    bbk_public_key - COSE_Key로 인코딩된 공개키.

    기타 필드

    모든 나머지 필드는 기존 13단계와 동일.

  4. 22단계 중, "어떤 인증기도 성공 신호" 경우, 3번째 단계에서 pubKeyCred 반환 직전:

    1. 키 쌍 바인딩(bbk_id, pubKeyCred.[[identifier]] 사용). 실패시 UnknownError 반환.

    2. payment_outputs = AuthenticationExtensionsPaymentOutputs 생성, 필드는:

      browserBoundSignature

      BrowserBoundSignature로 초기화(필드:)

      signature

      브라우저 바인드 서명 생성(bbk_and_algorithm, clientDataJson, create credential 14단계 사용)

    3. [[clientExtensionsResults]]["payment"] 에 payment_outputs 설정.

클라이언트 확장 처리(인증)

AuthenticationExtensionsPaymentInputs extension_inputs어트리뷰션(assertion) 실행 시:

  1. "secure-payment-confirmation" 결제 핸들러가 아니면 "NotAllowedError" DOMException 반환.

    테스트

    참고: 사이트가 SPC의 확장 권한도 트랜잭션 UX를 통하지 않고 접근하려는 것을 막습니다.

  2. [[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors) 중:

    1. 6.1단계(options.rpIdeffectiveDomain 비교) 건너뜀

    테스트

    참고: 이는 교차 도메인 인증 절차를 가능케 하며, § 1.1.2 가맹점의 인증 제어 참고.

    1. 10단계 전에(CollectedClientData 생성 직전)

      1. allowed_algorithms = SecurePaymentConfirmationRequest.browserBoundPubKeyCredParams.

      2. bbk_and_algorithm = 브라우저 바인드 키 조회 혹은 생성(credential_id, allowed_algorithms 사용).

    2. 10단계에서는 CollectedClientData 대신, CollectedClientPaymentData 생성(내용은 아래와 같음):

      1. type = "payment.get"

      2. payment = CollectedClientAdditionalPaymentData 생성(필드들:

        rpId

        extension_inputs["rpId"]

        topOrigin

        extension_inputs["topOrigin"]

        payeeName

        extension_inputs["payeeName"] 존재 시, 없으면 생략

        payeeOrigin

        extension_inputs["payeeOrigin"] 존재 시, 없으면 생략

        paymentEntitiesLogos

        extension_inputs["paymentEntitiesLogos"] 존재 시, 없으면 생략

        total

        extension_inputs["total"]

        instrument

        extension_inputs["instrument"]

        browserBoundPublicKey

        bbk_and_algorithm이 null이 아니면 브라우저 바인드 공개키 얻기(bbk_and_algorithm 사용). null이면 지정하지 않음.

      3. 나머지 필드는 기존 10단계와 동일.

      테스트
    3. "어떤 인증기도 성공 신호" 단계에서, 2단계(세팅 [[clientExtensionsResults]]):

      1. payment_outputs = AuthenticationExtensionsPaymentOutputs 생성(필드들):

        browserBoundSignature

        bbk_and_algorithm이 null이 아니면, BrowserBoundSignature 생성(필드들:)

        signature

        브라우저 바인드 서명 생성(bbk_and_algorithm, clientDataJson, create credential 14단계 사용)

      2. 맵에 [[clientExtensionsResults]]["payment"] 에 payment_outputs 설정.

      테스트
클라이언트 확장 출력
partial dictionary AuthenticationExtensionsClientOutputs {
  AuthenticationExtensionsPaymentOutputs payment;
};

dictionary AuthenticationExtensionsPaymentOutputs {
  BrowserBoundSignature browserBoundSignature;
};

dictionary BrowserBoundSignature {
  required ArrayBuffer signature;
};
browserBoundSignature

브라우저 바인드 서명 처리 출력.

signature

브라우저 바인드 서명 프로세스의 출력값.

인증기 확장 처리

없음

5.1. CollectedClientPaymentData 사전

dictionary CollectedClientPaymentData : CollectedClientData {
    required (CollectedClientAdditionalPaymentData or CollectedClientAdditionalPaymentRegistrationData) payment;
};

The CollectedClientPaymentData 사전은 CollectedClientData를 상속합니다. 다음의 추가 필드를 포함합니다:

payment

서명할 추가 결제 정보.

5.2. CollectedClientAdditionalPaymentData 사전

dictionary CollectedClientAdditionalPaymentData {
    required USVString rpId;
    required USVString topOrigin;
    USVString payeeName;
    USVString payeeOrigin;
    sequence<PaymentEntityLogo> paymentEntitiesLogos;
    required PaymentCurrencyAmount total;
    required PaymentCredentialInstrument instrument;
    USVString browserBoundPublicKey;
};

The CollectedClientAdditionalPaymentData 사전은 다음 필드를 포함합니다:

rpId

자격 증명(credential)을 생성한 Relying Party의 id입니다.

참고: 역사적 이유로 일부 구현체는 이 매개변수를 rp라는 이름으로 추가로 포함할 수 있습니다. rprpId가 둘 다 존재하는 경우 그 값은 동일해야 합니다.

topOrigin

거래 세부 정보를 서명하도록 요청한 최상위 컨텍스트의 출처(origin)입니다.

payeeName

사용자에게 표시된, (존재하는 경우) 수취인(payee)의 이름입니다.

payeeOrigin

사용자에게 표시된, (존재하는 경우) 수취인의 출처(origin)입니다.

paymentEntitiesLogos

거래 대화 상자에서 사용자에게 표시된 로고(있는 경우)입니다. 이 로고들은 거래를 중개하는 기관들을 나타내기 위한 것입니다.

total

PaymentCurrencyAmount[payment-request]total 필드에 해당합니다.

instrument

사용자에게 표시된 결제 수단(instrument) 정보입니다.

browserBoundPublicKey

브라우저 바운드 키(browser bound key) 공개키의 base64url 인코딩입니다. WebAuthn의 Base64url 인코딩을 참조하세요.

참고로 CollectedClientAdditionalPaymentData에는 paymentRequestOrigin 필드가 없습니다. 호출 프레임의 출처는 이미 CollectedClientData에 포함되어 있기 때문입니다.

5.3. CollectedClientAdditionalPaymentRegistrationData 사전

dictionary CollectedClientAdditionalPaymentRegistrationData {
    USVString browserBoundPublicKey;
};

The CollectedClientAdditionalPaymentRegistrationData 사전은 다음 필드를 포함합니다:

browserBoundPublicKey

브라우저 바운드 키 공개키의 base64url 인코딩입니다. WebAuthn의 Base64url 인코딩을 참조하세요.

6. 브라우저 바운드 키 저장소

이는 사용자 에이전트가 소유한 내부 구성요소로, SPC 자격증명(예: 패스키)과의 연관을 포함해 플랫폼 종속 암호화 키 쌍을 관리합니다. 이 절에서는 브라우저 바운드 키 저장소와 브라우저 바운드 키 쌍을 생성, 바인딩, 검색, 서명하는 절차를 설명합니다.

참고: 브라우저 바운드 키 저장소는 키 쌍 생성, 키 쌍 검색, 공개키 내보내기, 서명 생성을 수행해야 합니다. 많은 운영체제가 이러한 작업을 구현하는 API를 제공하고 가능할 경우 장치의 보안 요소에 개인키를 저장합니다. 예를 들어 Android KeyStore, Apple CryptoKit, 또는 Windows Cryptography API: Next Generation가 있습니다.

브라우저 바운드 키 저장소는 다음을 포함합니다:

browser_bound_map

Credential ID에서 키 페어 식별자로의 맵입니다.

keypair_map

바이트 배열(키 페어 식별자)에서 공개/개인 키 쌍과 알고리즘 식별자(COSEAlgorithmIdentifier)의 튜플로의 맵입니다.

참고: 사용자 에이전트가 keypair_map와 동등한 기능을 제공하는 암호화 API를 사용하고 있는 경우, 사용자 에이전트는 이 부분을 암호화 API에 위임할 수 있습니다.

6.1. 키 쌍 생성

키 쌍 생성 PublicKeyCredentialParameters, allowed_algorithms가 주어지면, 튜플 형태로 키 쌍과 해당 COSEAlgorithmIdentifier 및 키 쌍 식별자의 바이트 배열을 반환하는 절차는 다음과 같다:

참고: 허용된 알고리즘 또는 기본 허용 알고리즘에서 키 쌍을 생성한다.

  1. 만약 allowed_algorithms비어 있으면, 이 목록을 다음을 순서대로 포함하는 기본 목록으로 설정합니다:

  2. allowed_algorithms에서 PublicKeyCredential 타입이 아닌 모든 항목을 제거합니다.

  3. 사용자 에이전트가 지원하지 않는 allowed_algorithms의 모든 항목을 제거합니다.

    참고: 사용자 에이전트는 서로 다른 장치 플랫폼에서 서로 다른 암호화 알고리즘 세트를 지원할 수 있습니다.

  4. 만약 allowed_algorithms크기가 0이면, null을 반환합니다.

    참고: 이 경우 사용자 에이전트는 어떤 브라우저 바운드 출력도 추가하지 않습니다.

  5. 만약 allowed_algorithms크기가 0보다 크면,

    1. chosen_algorithmallowed_algorithms[0]으로 합니다.

    2. chosen_algorithm에 해당하는 확립된 절차를 사용하여 공개/개인 키 쌍을 생성한 결과를 bbk로 합니다. 확립된 키 생성 알고리즘은 IANA COSE Algorithms 레지스트리를 참조하세요. 결과가 실패이면 null을 반환합니다.

    3. bbk_and_algorithm을 (bbk, chosen_algorithm) 튜플로 합니다.

    4. bbk_id는 다음 중 하나입니다:

      1. 다음과 같이 초기화된 바이트 배열:

        1. bbk_id를 길이 32의 배열로 합니다.

        2. getRandomValues(bbk_id)를 호출합니다.

      2. 구현 정의된 키 생성 절차로부터 키페어 핸들의 직렬화 결과인 바이트 배열. 이 바이트 배열은 브라우저 바운드 키를 가져오거나 생성할 때 나중에 키를 검색할 수 있도록 식별해야 합니다.

      참고: 많은 암호화 API는 개인키 대신 식별자, 핸들, 또는 래핑된 키를 공개키와 함께 반환할 수 있습니다. 예를 들어 개인키가 보안 요소에 저장된 경우 개인키는 사용자 에이전트가 직접 접근할 수 없으며, 식별자/핸들/래핑된 키를 암호화 API의 다른 함수에 전달하여 사용해야 합니다.

    5. keypair_map[bbk_id]를 bbk_and_algorithm으로 설정합니다.

    6. (bbk_and_algorithm, bbk_id)를 반환합니다.

스펙은 하드웨어 저장소(주어진 순서)에서의 알고리즘을 우선하고 소프트웨어의 알고리즘을 그 다음으로 우선하도록 지정할 수 있습니다. 당분간 이 주제는 무의미할 수 있는데, Chrome은 플랫폼별 하드웨어에서 하나의 알고리즘만 지원할 계획이기 때문입니다. BBK의 저장 유형과 허용되어야 할 저장 유형, 저장 유형을 의존 당사자에 노출하는 문제 등을 논의하는 Secure Payment Confirmation issue #288을 참조하세요.

6.2. 키 쌍 바인딩

바이트 배열인 키 페어 식별자 bbk_id와 바이트 배열 credential_id가 주어졌을 때(실패를 반환할 수 있음), 다음 단계를 수행하여 키 쌍을 바인딩합니다:

참고: 브라우저 바운드 키 식별자를 Credential ID(예: 패스키 식별자)에 대해 browser_bound_map에 저장합니다.

  1. browser_bound_map[credential_id]를 bbk_id로 설정합니다. 그 과정에서 발생할 수 있는 InvalidStateError, TypeErrorQuotaExceededError를 잡아서 처리하고 실패를 반환합니다.

    참고: 오류는 write(buffer, options)와 같은 기본 파일 시스템 작업에서 발생할 수 있습니다. 여기서는 false가 반환되어 사용자 에이전트가 거래에 브라우저 바운드 키를 포함하지 않게 되며, 이 경우 의존 당사자(relying party)는 해당 거래에서 브라우저 바운드 키를 얻지 못합니다. 이후의 결제 어설션에서는 새로운 키 쌍 생성을 시도합니다.

6.3. 키 쌍 검색

바이트 배열 credential_id와 PublicKeyCredentialParameters의 목록allowed_algorithms가 주어졌을 때, 키 페어와 그 COSEAlgorithmIdentifier를 포함하는 튜플을 반환하기 위해 브라우저 바운드 키를 가져오거나 생성합니다:

참고: Credential ID에 대한 기존 연관 키 쌍을 찾거나 새로운 연관 키 쌍을 생성하고 해당 키 쌍과 알고리즘을 반환합니다.

  1. 만약 browser_bound_map[credential_id]가 존재하면

    1. bbk_idbrowser_bound_map[credential_id]로 합니다.

    2. bbk_and_algorithmkeypair_map[bbk_id]로 합니다.

  2. 만약 browser_bound_map[credential_id]가 존재하지 않으면

    1. (new_bbk_and_algorithm, bbk_id)를 create-a-key-pair를 사용하여 allowed_algorithms로 생성한 결과로 합니다.

    2. binding_resultbind-a-key-pair를 사용하여 bbk_idcredential_id로 바인딩한 결과로 합니다. 결과가 실패이면 bbk_and_algorithm을 null로 합니다.

    3. 만약 binding_result가 실패가 아니면, bbk_and_algorithmnew_bbk_and_algorithm으로 합니다.

  3. bbk_and_algorithm을 반환합니다.

6.4. 브라우저 바운드 공개키 가져오기

브라우저 바운드 공개키 가져오기는 키 쌍과 COSEAlgorithmIdentifier튜플bbk_and_algorithm이 주어지면 바이트 배열을 반환한다:

참고: 브라우저 바운드 키의 인코딩된 공개키를 가져온다.

  1. bbkbbk_and_algorithm[0]으로 합니다.

  2. algorithmbbk_and_algorithm[1]으로 합니다.

  3. public_keyalgorithm에 대한 확립된 절차에 따라 검색한 bbk의 공개키로 합니다.

  4. encoded_public_keypublic_key의 COSE_Key 인코딩으로 합니다. WebAuthn의 credentialPublicKey를 참조하세요.

  5. encoded_public_key를 반환합니다.

6.5. 클라이언트 데이터 서명

브라우저 바운드 서명 생성은 키 쌍과 COSEAlgorithmIdentifier튜플(bbk_and_algorithm)과 바이트 배열 client_data_json이 주어지면 바이트 배열을 반환한다:

참고: CollectedClientData (CollectedClientAdditionalPaymentData 또는 CollectedClientAdditionalPaymentRegistrationData 포함)의 내용을 브라우저 바운드 키개인키로 서명한다.

  1. bbkbbk_and_algorithm[0]으로 합니다.

  2. algorithmbbk_and_algorithm[1]으로 합니다.

  3. signaturealgorithm을 사용하여 bbk의 개인키로 client_data_json에 대해 수행한 결과로 합니다. IANA COSE Algorithms 레지스트리를 참조하세요.

    참고: 결과는 bbk의 개인키를 사용하여 client_data_json에 대한 암호학적 서명이 됩니다.

  4. signature를 반환합니다.

7. 공통 데이터 구조

다음 데이터 구조는 등록과 인증에서 공유됩니다.

7.1. PaymentCredentialInstrument 사전

dictionary PaymentCredentialInstrument {
    required USVString displayName;
    required USVString icon;
    boolean iconMustBeShown = true;
    USVString details;
};

PaymentCredentialInstrument 사전은 사용자에게 보여질 정보와 거래 세부사항과 함께 서명될 정보를 포함합니다. 다음 구성원을 포함합니다:

displayName

사용자에게 표시할 결제 수단의 이름입니다.

참고: § 14 국제화 고려사항에서 displayName 의 국제화에 대해 논의합니다.

icon

결제 수단 아이콘의 URL입니다.

참고: icon URL은 인터넷 접근이 가능한 서버(예: https://bank.example/card.png)의 이미지를 식별할 수 있고, Data URL로 아이콘 데이터를 직접 인코딩할 수도 있습니다 [RFC2397]. 두 종류의 URL 중 Data URL은 Relying Party 에 여러 이득을 줍니다. 예를 들어 아이콘 호스팅 서버가 사용 불가한 상황에서도 신뢰성을 높일 수 있습니다. 또한 Relying Party가 사용자에게 실제로 브라우저가 보여준 아이콘에 대해 암호학적으로 증명할 수 있어 검증에 유리합니다. 아이콘 URL은 CollectedClientAdditionalPaymentData 구조의 일부로 서명됩니다.

참고: 관련 접근성 고려사항을 참고하세요.

iconMustBeShown

요청이 성공하기 위해 지정된 아이콘이 반드시 정상적으로 가져와지고 표시되어야 하는지를 나타냅니다.

details

사용자에게 표시할 결제 수단의 선택적 추가 설명 문자열입니다.

참고: § 14 국제화 고려사항에서 details 의 국제화에 대해 논의합니다.

7.2. 사전

dictionary PaymentEntityLogo {
    required USVString url;
    required USVString label;
};

PaymentEntityLogo 사전은 현재 거래를 중개하는 결제 주체의 로고를 설명하는 정보를 포함합니다. 다음 구성원들을 포함합니다:

url

로고의 URL입니다.

참고: url 은 인터넷 접근 가능한 서버(예: https://bank.com/logo.png)의 이미지를 식별하거나, Data URL ([RFC2397])로 로고 데이터를 직접 인코딩할 수 있습니다. 두 종류의 URL 중 Data URL은 Relying Party 에 여러 이점을 제공합니다. 예를 들어, 로고 호스팅 서버가 불가용한 경우 신뢰성을 높일 수 있습니다. 또한 Relying Party가 브라우저가 실제로 사용자에게 보여준 로고에 대한 암호학적 증거를 가질 수 있습니다. 로고 URL은 CollectedClientAdditionalPaymentData 구조의 일부로 서명됩니다.

label

로고에 대한 설명 라벨입니다. 사용자 에이전트는(필수는 아니지만) 이 라벨을 사용자에게 표시할 수 있고(§ 4.9 거래 확인 UX 표시 참고) 접근성 목적(예: 스크린 리더가 이 UX에서 해당 로고를 설명할 때 읽어주기 위해)으로 사용해야 합니다.

8. 권한 정책 통합

본 명세는 SPC 인증 접근 제어를 위해 [payment-request]의 "payment" 정책 식별자 문자열을 PaymentRequest 생성자와 같이 사용합니다.

이 명세의 이전 버전과의 하위 호환을 위해 Credential Management Credential Type Registry에 "payment" 정책 식별자 문자열을 타입 public-key의 대체 Create Permissions Policy로 확장합니다. 향후 버전에서는 이 동작을 완전히 폐기할 수 있습니다.

테스트

참고: [CREDENTIAL-MANAGEMENT-1]에서 명시된 알고리즘이 실제 권한 정책 평가를 수행합니다. 이는 정책 평가는 현재 설정 객체에 접근할 수 있을 때 일어나야 하기 때문입니다. [[Create]](origin, options, sameOriginWithAncestors)[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors) 내부 메서드에서는 그러한 접근이 불가합니다. 해당 메서드는 병렬로 호출되기 때문입니다 ([CREDENTIAL-MANAGEMENT-1] 참고).

9. SPC Relying Party 작업

9.1. 인증 어설션 검증

Secure Payment Confirmation의 인증 절차를 수행하려면, Relying Party는 아래와 같이 진행해야 합니다:

  1. credentialSecure Payment Confirmation 결제 처리기를 성공적으로 호출해서 SPC 호출자가 획득한 PublicKeyCredential 객체로 둡니다.

    참고: SPC는 상점의 인증 통제를 지원하도록 설계되어 있기 때문에 SPC를 호출하는 주체가 Relying Party와 다를 수 있습니다. 첫 번째 단계는 SPC 호출자가 SPC를 통해 획득한 자격 증명을 Relying Party로 반환했다고 전제합니다.

  2. WebAuthn에 명시된 3-21단계를 아래 변경사항과 함께 수행합니다:

    1. 5단계에서 credential.id 값이 Relying PartySPC 호출자에게 제공한 공개키 자격 중 하나를 식별하는지 확인합니다.

    2. 11단계에서 C["type"] 값이 문자열 payment.get인지 확인합니다.

    3. 12단계에서 C["challenge"] 값이 Relying PartySPC 호출자에게 제공한 챌린지의 base64url 인코딩과 같은지 확인합니다.

    4. 13단계에서 C["origin"] 값이 Relying Party가 SPC 호출을 예상한 출처와 일치하는지 확인합니다.

    5. 13단계 이후, 다음 단계를 삽입합니다:

      • C["payment"]["rpId"] 값이 Relying Party의 origin과 일치하는지 검증합니다.

      • C["payment"]["topOrigin"] 값이 Relying Party가 기대한 최상위 출처와 일치하는지 검증합니다.

      • C["payment"]["payeeName"] 값이 사용자에게 표시되어야 했던 수취인의 이름(존재하는 경우)과 일치하는지 검증합니다.

      • C["payment"]["payeeOrigin"] 값이 사용자에게 표시되어야 했던 수취인 출처(존재하는 경우)와 일치하는지 검증합니다.

      • C["payment"]["paymentEntitiesLogos"] 값이(존재 시) 사용자에게 표시되어야 했던 로고들에 대한 엄격하고 순서가 보장되는 부분집합인지 검증합니다.

        참고: 사용자 에이전트는 모든 로고를 표시하지 않을 수 있으나, 사용자에게 표시하지 않은 로고는 CollectedClientAdditionalPaymentData 에 포함하면 안 됩니다.

      • C["payment"]["total"] 값이 사용자에게 표시되었던 거래 금액과 일치하는지 검증합니다.

      • C["payment"]["instrument"] 값이 사용자에게 표시되었던 결제 수단 상세정보와 일치하는지 검증합니다.

10. 사용자 에이전트 자동화

사용자 에이전트 자동화 및 웹사이트 테스트 목적으로, 본 문서는 아래의 [WebDriver2] 확장 명령을 정의합니다. 관심있는 분들은 WebAuthn의 자동화 섹션도 참고하시기 바랍니다 ([webauthn-3]).

10.1. SPC 거래 모드 설정

SPC 거래 모드 설정 WebDriver 확장 명령은 사용자 에이전트에게 Secure Payment Confirmation을 사용자가 거래 확인 UX를 자동으로 승인 또는 거절하도록 시뮬레이션하는 모드로 전환하도록 지시합니다.

현재 거래 자동화 모드는 SPC에 대해 현재 어떤 자동화 모드가 활성화되어 있는지 추적합니다. 초기값은 "none"입니다.

HTTP 메서드 URI 템플릿
POST /session/{session id}/secure-payment-confirmation/set-mode

원격 측 단계는 아래와 같습니다:

  1. parameters가 JSON Object가 아니면, WebDriver 오류와 함께 WebDriver 오류 코드 invalid argument를 반환합니다.

  2. modeparameters에서 "mode"라는 이름의 프로퍼티 값으로 둡니다.

  3. modeundefined이거나 "autoAccept", "autoChooseToAuthAnotherWay", "autoReject", "autoOptOut" 중 하나가 아니면 WebDriver 오류와 함께 WebDriver 오류 코드 invalid argument를 반환합니다.

  4. 현재 거래 자동화 모드mode로 설정합니다.

  5. 성공과 함께 data null을 반환합니다.

11. 보안 고려사항

이 명세는 WebAuthn을 기반으로 하므로, WebAuthn 보안 고려사항 이 적용됩니다. 아래 하위 섹션은 Secure Payment Confirmation에서 WebAuthn과 다른 부분에 대한 보안 고려사항입니다.

11.1. 크로스 오리진 인증 절차

Secure Payment Confirmation이 WebAuthn과 가장 크게 다른 점은, 제3자가 다른 Relying Party의 자격으로 인증 절차를 시작하고 그 어설션을 제3자에게 반환할 수 있다는 점입니다. 이 기능은 Relying Party가 로그인 및 결제 공격에 노출되게 할 수 있습니다. 이 내용을 설명합니다.

11.1.1. 로그인 공격

Secure Payment Confirmation용 자격 증명은 유효한 WebAuthn 자격 증명이므로, Relying Party가 동일 사용자의 로그인과 결제 모두에 대해 같은 자격을 사용하고 싶을 수 있습니다. 이는 Relying Party가 어설션을 신중하게 검증하지 않으면 로그인 시스템에 공격이 될 수 있습니다.

공격 절차는 다음과 같습니다:

  1. 사용자가 attacker.example을 방문합니다. 이 사이트는 상점 사이트이거나 그런 척 합니다.

  2. attacker.examplerelyingparty.example의 자격을 합법적이거나 relyingparty.example 또는 자격을 공유받은 타자로부터 탈취해서 얻습니다.

  3. attacker.example이 SPC 인증을 시작하고 사용자가 거래에 동의(합법적일 수도 아닐 수도 있음)합니다.

  4. attacker.example은 API에서 반환받은 결제 어설션을 relyingparty.example의 로그인 엔드포인트(예, https://relyingparty.example/login)로 POST로 전송합니다.

  5. relyingparty.example이 어설션 유효성 검사 코드가 잘못되어 서명만 확인하고 필요한 필드 검증을 건너뛰고(아래 참고), 로그인 시도가 합법적이라고 인식합니다.

  6. relyingparty.example이 로그인 쿠키 등을 attacker.example에 반환합니다. 이로써 사용자의 relyingparty.example 계정이 위협당하게 됩니다.

Relying Party는 두 가지 방법으로 이 공격을 방어할 수 있습니다.

첫째, Relying Party는 상황에 맞게 WebAuthn 로그인 또는 SPC 결제 에 대한 어설션 검증 절차를 항상 따라야 합니다. 특히, 아래 필드는 자격의 부적절한 사용을 감지하는데 모두 활용될 수 있습니다:

둘째, Relying Party는 결제와 로그인 자격을 분리 보관할 수 있습니다. 이 경우, Relying Party는 Secure Payment Confirmation 자격 등록을 하위 도메인(예, https//payment.relyingparty.example)에서만 수행하고, 결제/로그인 자격을 데이터베이스에서 분리해야 합니다.

참고: 현재 명세상 Secure Payment Confirmation은 모든 WebAuthn 자격의 SPC 인증 사용을 허용합니다. 하지만 실제 구현에서는 payment 확장으로 생성된 자격만 SPC 인증에 사용할 수 있으며, 명세도 향후 이 점을 반영할 수 있습니다.

현재 구현과 명세 모두 payment 확장으로 생성된 자격은 Relying Party가 원할 경우 로그인에도 사용할 수 있습니다. 이 점은 변경되지 않을 예정입니다.

11.1.2. 결제 공격

Secure Payment Confirmation 어설션은 진행 중인 온라인 거래의 일부가 아닐 경우에는 사실상 무용지물입니다.

다양한 메커니즘은 악의적 제3자가 사용자의 계정 탈취 대신 Secure Payment Confirmation 자격(합법적 또는 비합법적 취득)으로 무단 결제를 시도하는 공격을 방지합니다:

11.2. 판매자 제공 인증 데이터

은행은 판매자가 제공한 거래 세부 정보와 일치하는지 인증 어설션을 검증함으로써 스푸핑에 대응할 수 있으며, 또한 그렇게 해야 합니다.

그 이유는 이 명세의 제3자 인증 절차 결과, 유효한 거래(즉, Relying Party가 예측한 거래)에서도 제3자가 사용자에게 표시되는 거래 세부 정보를 제공하기 때문입니다:

이로 인해 판매자가 사용자에게 잘못된 데이터를 보여주는 스푸핑 공격이 발생할 수 있습니다. 예를 들어, 판매자가 뒷단에서 은행에 $100 구매 거래를 시작한다고 전달하면서 SPC API에는 $1을 넘겨 사용자에게는 $1 거래로 보이게 할 수도 있습니다. 또는, 올바른 거래 세부 정보를 제공하면서도 Relying Party가 기대하는 것과 일치하지 않는 Secure Payment Confirmation 자격을 전달할 수도 있습니다.

Secure Payment Confirmation은 오히려 이런 유형의 공격을 막기 쉽게 만들어 줍니다. 오늘날 온라인 결제에서는, 은행이 상점이 체크아웃 과정에서 정확한 금액을 보여줬다는 사실을 신뢰해야만 하며(사기 발견은 결제 후, 사용자가 계좌 명세서를 확인할 때야 일어납니다).

11.3. 공격자가 생성한 브라우저 바운드 키

Relying Party(예: 은행)는 인증 어설션에서 BBK 공개키를 검증하고 잘못된 서명의 크립토그램에서 BBK 공개키를 무시하는 방식으로 공격자가 생성한 BBK로부터 보호할 수 있습니다(그리고 그렇게 해야 합니다). 여기에는 SPC Credential의 서명과 BBK의 서명이 포함되어 있으며, 어설션은 BBK뿐만 아니라 패스키를 사용해 검증해야 합니다. BBK 서명은 추가 서명일 뿐 패스키 서명을 대체하지 않습니다.

상점 행세를 하는 공격자는 자신이 제어하는 개인키와 짝을 이루는 다른 BBK 공개키로 교체를 시도할 수 있습니다. 이후 공격자가 사용자를 가장해 상점 웹사이트를 방문하고, 상점이 SPC를 호출할 때 공격자 개인키로 SPC 크립토그램 서명을 시도합니다. 이로써 BBK의 디바이스 바인딩 측면이 훼손됩니다.

그러나, 이 공격은 실질적으로 불가능합니다:

  1. 공격자가 BBK를 교체하려 할 때: Relying Party는 패스키 공개키로 CollectedClientData (여기에는 BBK 공개키가 들어있음)을 검증할 때 잘못된 서명을 감지할 수 있습니다. Relying Party는 해당 거래를 거절하고 이 BBK 공개키를 신뢰하지 않아야 합니다.

  2. 공격자가 사용자를 사칭하려 할 때: Relying Party 역시 패스키 공개키로 검증 시 잘못된 서명을 감지할 수 있습니다. Relying Party는 해당 BBK 공개키를 계속 신뢰하지 않아야 합니다.

11.4. 사용자 활성화 요건 부재

만약 사용자 에이전트가 § 4.3 사용자 활성화 요건 수정에서 설명한 바와 같이 사용자 활성화를 요구하지 않는다면, 추가적인 보안 완화책을 고려해야 합니다. 사용자 활성화가 요구되지 않으면 사용자가 페이지와 바로 상호작용하지 않아도 Secure Payment Confirmation 플로가 시작될 수 있어 스팸 및 클릭재킹 공격의 위험이 증가합니다.

이런 스팸을 완화하기 위해, 사용자 에이전트는 예를 들어 현재 페이지에서 이미 사용자 활성화 없이 SPC 플로를 보여준 이후에는 사용자 활성화 요건을 enforced 할 수 있습니다. 클릭재킹 공격을 완화하기 위해, 사용자 에이전트는 다이얼로그 표시 직후 일정 시간 동안 클릭을 무시하도록 임계값을 구현할 수 있습니다.

추가적으로, PaymentRequest.show()에도 관련된 완화책이 있습니다: Payment Request API는 문서가 보여지고 있을 때만 호출할 수 있으므로, SPC는 백그라운드 탭, 최소화된 창, 기타 숨겨진 상황에서는 트리거될 수 없습니다.

12. 개인정보 보호 고려사항

이 명세는 WebAuthn을 기반으로 하므로, WebAuthn 개인정보 보호 사항 이 적용됩니다. 아래 하위 섹션은 Secure Payment Confirmation에서 WebAuthn과 다른 부분에 대한 현재 개인정보 보호 관련 사항입니다.

12.1. 자격(credential) id 탐색

WebAuthn의 인증 절차 개인정보 보호 섹션에 따라, Secure Payment Confirmation 구현자는 악의적 호출자(이제는 Relying Party가 아니어도 됨)가 다음 상황을 구분하지 못하게 해야 합니다:

위 상황이 구분 가능해지면, 악의적 Relying Party가 사용자를 추적할 수 있는 정보가 노출될 수 있습니다. 즉 어떤 credential이 있는지 탐색할 수 있습니다.

이 위험은 Secure Payment Confirmation에서 항상 거래 확인 UX를 표시하고, 두 경우 모두 동일한 반환 오류("NotAllowedError" DOMException) 를 반환해서 완화됩니다. 즉, SPC Credential이 없거나, 있지만 사용자가 사용을 거부한 두 경우 모두 같은 오류가 반환됩니다.

12.2. 서로 다른 결제 수단 결합

하나의 사용자가 여러 결제 수단에서 같은 자격을 사용하는 경우, 결과적으로 판매자가 본래 연결할 수 없던 결제 수단 정보를 결합할 수 있습니다. 즉, 사용자 U가 결제 수단 P1과 P2로 각각 상점 M에서(또는 공모하는 두 상점 M1, M2에서) 두 번 거래할 때, 상점들이 P1과 P2가 동일 사용자임을 알아낼 수 있습니다.

많은 현행 온라인 결제 플로에서는, 사용자가 이름·이메일·배송지 등으로 인해 이미 결합 가능하므로 이것이 별로 큰 위험이 아닐 수 있습니다.

그러나 토큰화 등으로 식별 정보가 적은 결제 방식이 보편화된다면, 업계는 개인정보 보호를 위해 다음과 같은 방안을 고려해야 합니다:

12.3. Credential ID의 추적 벡터화

하나의 결제 수단만 있어도, Relying Party가 반환하는 credential ID는 악의적 주체에게 강력한 교차 사이트 추적 벡터가 될 수 있습니다. 그러나 이것을 얻으려면 Relying Party로부터 대상이 되는 식별자를 이미 알아야 하므로(예: 신용카드 번호), 위험이 크지 않습니다.

12.4. 브라우저 바운드 키의 추적 벡터화

BBK 공개키는 오직 SPC 결제 어설션의 각각(및 최초의 일차 SPC Credential 생성 시 한 번)만 반환됩니다. 그러나 BBK를 얻으려면 상점이 Relying Party에서 credential ID를 얻어야 하며, 사용자가 패스키로 거래를 인증해야만 합니다. 즉 credential ID가 선행되지 않으면 BBK 공개키 접근도 불가하므로 BBK 공개키가 추가 추적 요소가 되지는 않습니다.

12.5. securePaymentConfirmationAvailability를 통한 핑거프린팅

securePaymentConfirmationAvailability API는, Secure Payment Confirmation API가 특정 프레임에서 불가한 경우 그 이유를 조용히 반환할 수 있으므로, 핑거프린팅 위험 요소가 됩니다. 이러한 이유들은 중요한 정보를 누설하는 것으로 보이지 않지만 다음과 같이 고려되어야 합니다:

또한 이 명세는, 사용자 프라이버시 보호를 위해 원한다면 사용자 에이전트가 구체적 이유를 알 때 다른 이유 대신 unavailable-unknown-reason 을 반환하게 허용합니다. 예를 들어 현재 프레임이 이미 위험한 타 API를 사용 중임을 감지했다면, 이렇게 처리할 수 있습니다.

12.6. 사용자 옵트아웃

API 옵션 showOptOut 은 사용자에게 Relying Party의 정보 저장 거부(옵트아웃)의사를 표시할 방법을 제공하라고 사용자 에이전트에 지시합니다. 사용자가 Opt Out을 선택하면, OptOutError 가 호출자에 반환되어 사용자의 의사를 표시합니다. 이후 결제 정보 삭제 등 처리는 호출자 몫입니다.

구현자는 OptOutError 반환이 사용자가 자격이 있으나 인증을 마치지 않았음을 노출하지 않도록 해야 합니다. 이를 위해서는 §12.1 자격 id 탐색과 유사하게, 자격이 없는 경우에도 중간 UX에서 옵트아웃을 제공하는 식으로 처리할 수 있습니다.

이것은 브라우저 데이터 또는 자격 삭제 메커니즘이 아니라 개발자가 사용자 에이전트를 통해 옵트아웃 프롬프트를 띄우게 하는 것입니다. 사용자 에이전트는 예컨대 "이 공급자가 귀하의 결제수단 정보를 저장했을 수 있습니다. 삭제를 요청할 수 있습니다."와 같이 명확한 설명을 사용자에게 제공해야 합니다.

13. 접근성 고려사항

사용자 에이전트는 icondisplayName 을 함께 랜더링합니다. Relying Party는 displayName 을 통해 아이콘 표시 접근성을 충분히 보장해야 합니다(예: 아이콘이 은행 로고라면 displayName에 은행명을 포함).

본 명세를 구현하는 사용자 에이전트는 WebAuthn 접근성 고려사항PaymentRequest 접근성 고려사항을 모두 따라야 합니다.

14. 국제화(i18n) 고려사항

API 호출자는 거래 대화상자에서 사용될 locale 및 표시용 문자열의 현지화를 locale 멤버로 지정해야 합니다. 일반적으로 이 멤버는 요청이 시작된 페이지의 현지화(예: 요청 트리거 버튼의 lang 속성)와 일치해야 합니다.

현재 이 명세에는 API에 입력하는 표시 문자열(예: displayName)에 언어나 방향 메타데이터를 연결하는 메커니즘은 포함되어 있지 않습니다.

그 동안 API 호출자는 다음을 준수해야 합니다:

구현체(및 값을 표시하려는 다른 프로세스)는 사용자 인터페이스에 표시 문자열을 삽입할 때 반드시 bidi isolation을 적용해야 합니다. 방향성이 알려져 있다면 명시적으로, 그렇지 않으면 기본(자동 추론)으로 설정해야 합니다.

15. IANA 고려사항

본 절은 확장 식별자를 IANA "WebAuthn Extension Identifiers" 레지스트리에 추가합니다([IANA-WebAuthn-Registries], [RFC8809] 참고).

적합성(Conformance)

문서 규약(Document conventions)

적합성 요구사항은 설명적 진술과 RFC 2119 용어의 조합으로 표현됩니다. 이 명세의 준거(normative) 부분에서 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", "OPTIONAL"과 같은 핵심 단어들은 RFC 2119에서 정의된 대로 해석되어야 합니다. 다만, 가독성을 위해 본 명세에서는 이러한 단어들이 모두 대문자로 나타나지 않습니다.

이 명세의 모든 텍스트는 별도 비준거, 예제 또는 주석임이 명시된 부분을 제외하고 준거성이 있습니다. [RFC2119]

이 명세의 예제는 "for example"이라는 단어로 시작하거나 class="example"를 통해 준거 텍스트와 구분됩니다. 예시는 아래와 같습니다:

이것은 참고용 예제의 예시입니다.

참고 주석(Informative notes)은 "Note"라는 단어로 시작하며, class="note"를 통해 준거 텍스트와 구분됩니다. 아래와 같이 사용합니다:

Note, 이것은 참고용 노트입니다.

테스트

이 명세와 관련된 테스트는 본문과 같이 “Tests” 블록으로 문서화될 수 있습니다. 이와 같은 블록은 모두 비준거(non-normative)입니다.


적합한 알고리즘(Conformant Algorithms)

알고리즘의 일부로 명령형으로 표현된 요구사항(예: "strip any leading space characters" 또는 "return false and abort these steps")은 알고리즘 도입부에 사용된 핵심 단어("must", "should", "may" 등)의 의미로 해석해야 합니다.

알고리즘이나 특정 단계로 표현된 적합성 요구사항은 결과만 동일하다면 어떤 방식으로 구현해도 됩니다. 특히, 본 명세에 정의된 알고리즘은 이해하기 쉽도록 설계되었으며 성능을 염두에 둔 것이 아닙니다. 구현자는 최적화를 권장합니다.

색인(Index)

이 명세에서 정의한 용어(Terms defined by this specification)

참조에 의해 정의되는 용어(Terms defined by reference)

참고문헌

표준(규범) 참고문헌

[CREDENTIAL-MANAGEMENT-1]
Nina Satragno; Marcos Caceres. Credential Management Level 1. 28 October 2025. WD. URL: https://www.w3.org/TR/credential-management-1/
[DOM]
Anne van Kesteren. DOM 표준. Living Standard. URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript 언어 명세. URL: https://tc39.es/ecma262/multipage/
[FETCH]
Anne van Kesteren. Fetch 표준. Living Standard. URL: https://fetch.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML 표준. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[I18N-GLOSSARY]
Richard Ishida; Addison Phillips. 국제화 용어집. 17 October 2024. NOTE. URL: https://www.w3.org/TR/i18n-glossary/
[IANA-COSE-ALGS-REG]
IANA CBOR Object Signing and Encryption (COSE) 알고리듬 레지스트리. URL: https://www.iana.org/assignments/cose/cose.xhtml#algorithms
[IANA-WebAuthn-Registries]
웹 인증(WebAuthn) 레지스트리. URL: https://www.rfc-editor.org/rfc/rfc8809.html
[IMAGE-RESOURCE]
Aaron Gustafson; Rayan Kanso; Marcos Caceres. 이미지 리소스. 4 June 2021. WD. URL: https://www.w3.org/TR/image-resource/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 표준. Living Standard. URL: https://infra.spec.whatwg.org/
[PAYMENT-METHOD-ID]
Marcos Caceres. 결제 수단 식별자. 8 September 2022. REC. URL: https://www.w3.org/TR/payment-method-id/
[PAYMENT-REQUEST]
Marcos Caceres; Ian Jacobs; Stephen McGruer. Payment Request API. 30 September 2025. CRD. URL: https://www.w3.org/TR/payment-request/
[RFC2119]
S. Bradner. RFC에서 요구 레벨을 지시하는 핵심 단어. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[RFC8809]
J. Hodges; G. Mandyam; M. Jones. 웹 인증(WebAuthn) 레지스트리. August 2020. Informational. URL: https://www.rfc-editor.org/rfc/rfc8809
[URL]
Anne van Kesteren. URL 표준. Living Standard. URL: https://url.spec.whatwg.org/
[WEBAUTHN-3]
Tim Cappalli; et al. 웹 인증: 공개키 자격에 접근하는 API - 레벨 3. 27 January 2025. WD. URL: https://www.w3.org/TR/webauthn-3/
[WEBCRYPTO-2]
Daniel Huigens. 웹 암호화(Web Cryptography) 레벨 2. 22 April 2025. FPWD. URL: https://www.w3.org/TR/webcrypto-2/
[WEBDRIVER1]
Simon Stewart; David Burns. WebDriver. 5 June 2018. REC. URL: https://www.w3.org/TR/webdriver1/
[WebDriver2]
Simon Stewart; David Burns. WebDriver. 28 October 2025. WD. URL: https://www.w3.org/TR/webdriver2/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 표준. Living Standard. URL: https://webidl.spec.whatwg.org/

참고(정보) 참고문헌

[BCP47]
A. Phillips, Ed.; M. Davis, Ed.. 언어 식별용 태그. September 2009. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc5646
[FS]
Austin Sullivan. 파일 시스템 표준. Living Standard. URL: https://fs.spec.whatwg.org/
[RFC2397]
L. Masinter. The "data" URL scheme. August 1998. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc2397
[RFC4647]
A. Phillips, Ed.; M. Davis, Ed.. 언어 태그 매칭. September 2006. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc4647
[WEBAUTHN-CONDITIONAL-UI]
WebAuthn 조건부 UI 제안. URL: https://github.com/w3c/webauthn/issues/1545

IDL 색인

dictionary SecurePaymentConfirmationRequest {
    required BufferSource challenge;
    required USVString rpId;
    required sequence<BufferSource> credentialIds;
    required PaymentCredentialInstrument instrument;
    unsigned long timeout;
    USVString payeeName;
    USVString payeeOrigin;
    sequence<PaymentEntityLogo> paymentEntitiesLogos;
    AuthenticationExtensionsClientInputs extensions;
    sequence<PublicKeyCredentialParameters> browserBoundPubKeyCredParams;
    sequence<USVString> locale;
    boolean showOptOut;
};

enum SecurePaymentConfirmationAvailability {
  "available",
  "unavailable-unknown-reason",
  "unavailable-feature-not-enabled",
  "unavailable-no-permission-policy",
  "unavailable-no-user-verifying-platform-authenticator",
};

partial interface PaymentRequest {
    static Promise<SecurePaymentConfirmationAvailability> securePaymentConfirmationAvailability();
};

partial dictionary AuthenticationExtensionsClientInputs {
  AuthenticationExtensionsPaymentInputs payment;
};

dictionary AuthenticationExtensionsPaymentInputs {
  boolean isPayment;
  sequence<PublicKeyCredentialParameters> browserBoundPubKeyCredParams;

  // Only used for authentication.
  USVString rpId;
  USVString topOrigin;
  USVString payeeName;
  USVString payeeOrigin;
  sequence<PaymentEntityLogo> paymentEntitiesLogos;
  PaymentCurrencyAmount total;
  PaymentCredentialInstrument instrument;
};

partial dictionary AuthenticationExtensionsClientOutputs {
  AuthenticationExtensionsPaymentOutputs payment;
};

dictionary AuthenticationExtensionsPaymentOutputs {
  BrowserBoundSignature browserBoundSignature;
};

dictionary BrowserBoundSignature {
  required ArrayBuffer signature;
};

dictionary CollectedClientPaymentData : CollectedClientData {
    required (CollectedClientAdditionalPaymentData or CollectedClientAdditionalPaymentRegistrationData) payment;
};

dictionary CollectedClientAdditionalPaymentData {
    required USVString rpId;
    required USVString topOrigin;
    USVString payeeName;
    USVString payeeOrigin;
    sequence<PaymentEntityLogo> paymentEntitiesLogos;
    required PaymentCurrencyAmount total;
    required PaymentCredentialInstrument instrument;
    USVString browserBoundPublicKey;
};

dictionary CollectedClientAdditionalPaymentRegistrationData {
    USVString browserBoundPublicKey;
};

dictionary PaymentCredentialInstrument {
    required USVString displayName;
    required USVString icon;
    boolean iconMustBeShown = true;
    USVString details;
};

dictionary PaymentEntityLogo {
    required USVString url;
    required USVString label;
};

이슈 색인

이 명세의 현재 버전은 PaymentEntityLogo당 하나의 URL만 지원하므로, 단순히 해당 데이터 구조를 복사하고 서명하는 것만으로 사용자에게 무엇이 표시되었는지 나타내기에 충분합니다. 추후 버전에서는 예를 들어 다크 모드를 기본 지원하기 위해 PaymentEntityLogo당 여러 개의 URL을 허용할 수 있습니다. 그런 경우 명세는 Relying Party에게 어떤 URL이 특정 PaymentEntityLogo에 대해 사용자에게 실제 표시되었는지 명확히 알려야 합니다.
명세는 하드웨어 저장소의 알고리즘(주어진 순서)을 우선한 뒤, 소프트웨어의 알고리즘(같은 순서)을 우선하도록 지정할 수도 있습니다. 현재로서는 Chrome이 하드웨어별로 플랫폼당 한 가지 알고리즘만 지원하려 하기 때문에 이 주제가 무의미할 수 있습니다. BBK의 저장 유형 및 허용 가능한 저장 유형, Relying Party에 저장 유형 노출 등 주제를 다루는 Secure Payment Confirmation 이슈 #288을 참조하세요.