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
에 저장되어야 합니다. 이는 다음과 같은 문제를 야기합니다:
-
challenge필드의 오용(이 필드는 재사용 공격 방지 용도입니다). -
명세가 없어 각 은행이 결제 특화 정보를 챌린지에 어떠한 형식과 인코딩으로 담을지 직접 설계해야 하므로, 배포가 복잡해지고 파편화가 증가합니다.
-
규제에 따라 사용자에게 결제 특화 정보가 표시되고 사용자 동의 증거가 요구될 수 있습니다. 일반 [webauthn-3] 에서는 이러한 정보 표시를 위한 UX가 명세되어 있지 않고,
challenge필드의 정보에 대한 UX가 제공되지 않습니다.
이러한 제한을 해소하기 위해 다음과 같은 안전한 결제 확인(SPC) 행동 양식이 도입됩니다:
-
challenge필드는 일반 [webauthn-3] 처럼 오직 재사용 공격 방지 용도로만 사용됩니다. -
SPC는 결제 특화 정보의 형식을 명세합니다. 이를 통해 범용 검증 코드와 테스트 스위트 개발이 가능합니다.
-
SPC는 사용자 에이전트가 결제 특화 정보를 사용자에게 악의적인 웹사이트(또는 신뢰된 웹사이트 내 악의적으로 삽입된 자바스크립트 코드)가 우회할 수 없는 방식으로 표시했음을 보장합니다.
-
결제 특화 정보는
CollectedClientData딕셔너리에 포함되며, 자바스크립트에서 변조할 수 없습니다.
참고: 결제 생태계에서 은행 등 이해당사자들은 오늘날 브라우저의 TLS, iframe 등 Web 기능을 이용해 결제에 충분한 신뢰를 두고 있습니다. 현재 명세는 웹 결제의 보안성 및 사용성을 향상시키려 설계되었습니다.
-
1.1.2. 가맹점의 인증 제어
가맹점들은 결제 진행 중 사용자 이탈을 방지하고 인증 마찰을 줄이고자 합니다. 릴라잉 파티 (예: 은행)가 [webauthn-3] 로 사용자를 인증하려는 경우, 보통 iframe을 통해 인증합니다. 하지만 가맹점들은 인증 사용자 경험을 직접 관리하면서도 릴라잉 파티 가 인증 결과를 검증할 수 있기를 원합니다.
이러한 제한을 해소하기 위해 다음과 같은 안전한 결제 확인(SPC) 행동 양식이 도입됩니다:
-
SPC를 사용하면 릴라잉 파티 이외의 다른 주체가 릴라잉 파티를 대신하여 인증 자격증명을 사용할 수 있습니다. 릴라잉 파티는 인증 결과를 검증할 수 있습니다.
이 기능의 추가적 장점은 릴라잉 파티가 인증 프론트엔드 경험을 직접 구축하지 않아도 된다는 것입니다. 대신 결제 서비스 제공자가 상점들을 위해 이를 구축할 가능성이 높아집니다.
참고: 인증 사용자 경험을 직접 제공하고 싶은 릴라잉 파티는 SPC를 iframe에서 사용할 수 있습니다.
1.1.3. 장치 바인딩의 암호화 증거
결제 업계에서는 장치 소유 신호가 2차 요소로 중요한 역할을 합니다. WebAuthn은 하나의 자격증명이 여러 장치에서 동기화될 수 있는 패스키를 지원합니다(Web Authentication § 1.2.1 다중 장치 자격증명 사용 소비자). 동기화는 로그인 사용 사례에서 사용자 경험을 개선하지만, 일부 규제 환경에서는 동기화된 패스키만으로는 장치 소유 요건을 충족하지 못한다는 우려가 있습니다.
이런 우려를 해소하기 위해 사용자 에이전트가 생성하는 보조 공개/개인 키 쌍의 비밀키가 특정 장치에서만 존재(및 사용)하도록 만듭니다. 이러한 키와 SPC 내 사용을 브라우저 바인드 키 라고 합니다.
1.2. API 사용 예시 시나리오
이 절에서는 안전한 결제 확인을 위한 시나리오와 해당 API 사용 예시 코드를 설명합니다. 이것은 예시 흐름일 뿐이며 API 사용 범위를 제한하지 않습니다.
1.2.1. 결제 시 등록
이 흐름은 사용자가 어떤 상점에서 결제하는 과정 중 발급 은행이 새로운 자격증명을 생성해 저장하는 첫 등록 흐름입니다.
-
사용자가
merchant.example에 접속하여 상품을 선택하고 결제 과정을 진행합니다. 결제 수단 정보를 입력한 뒤 결제를 희망(예: "Pay" 버튼 누름)합니다. -
상점은 발급 은행과 밴드 밖(out-of-band)(예: 다른 프로토콜 사용)으로 통신합니다. 발급 은행은 사용자 확인을 요청하고, 상점이 iframe에서 열 수 있도록 은행 제어의 URL을 제공합니다.
-
상점은
bank.example에 iframe을 열고allow속성을 "publickey-credentials-create"로 설정합니다. -
iframe 내에서 발급 은행은 기존 방식(예: SMS OTP 등)으로 사용자를 확인합니다. 확인 후, 은행은 향후 결제에 SPC 인증 등록을 권유합니다.
-
사용자가 동의(예: 은행 UX의 "Register" 버튼 클릭)하면, 은행은 iframe 내에서 코드를 실행합니다(예시 아래 참고).
-
사용자는 WebAuthn 등록 과정을 거칩니다. 새 자격증명이 생성되고, 발급 은행 서버 DB에 사용자 및 결제 수단과 연계해 저장됩니다.
-
확인이 완료되면 은행 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)를 사용하고자 할 때의 흐름입니다.
-
사용자가
merchant.example에 방문하여 상품을 선택한 뒤 결제 과정을 진행합니다. 결제 수단 정보를 입력하고, 결제 의사를 표시합니다(예: "결제" 버튼 클릭). -
가맹점은 결제 수단의 발급 은행과 밴드 밖(out-of-band)으로 통신합니다(예: 다른 프로토콜 사용). 발급 은행은 사용자 확인을 요청하며, 동시에 API 사용에 필요한 정보를 제공하여 SPC 사용을 승인했음을 상점에 알립니다. 이 정보에는 챌린지와 해당 사용자 및 결제 수단과 연계된 자격증명 ID 목록이 포함됩니다.
-
가맹점은 아래 예시 코드를 실행합니다.
-
사용자는 SPC UX에 표시된 결제 특화 정보에 동의하고, 이후 WebAuthn 인증 절차를 수행합니다. 서명된 암호문(브라우저 바인드 키 출력 포함)은 가맹점에 반환됩니다(
AuthenticationExtensionsPaymentOutputs포함). -
가맹점은 서명된 암호문을 밴드 밖으로 발급 은행에 전달합니다. 발급 은행은 암호문을 검증하여 사용자의 유효성과 표시된 결제 특화 정보, 사용자의 거래 동의 여부를 확인합니다. 발급 은행이 거래를 승인하고, 가맹점은 결제 과정을 완료합니다. 발급 은행은 브라우저 바인드 키의 공개키,
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은
name
과
displayName
을 모두 요구하지만, user 멤버 정의에 따라, 구현에서는 이후 인증 절차에서 둘 중 어떤 것도
표시할 필요가 없습니다.
두 항목 중 2023년 10월 기준 name
이 더 꾸준히 표시되고 있습니다. 개발자들은 구현 변화를 모니터링해야 합니다.
4. 인증 - 안전한 결제 확인 결제 방식
안전한 결제 확인을 통해 결제를 인증하려면, 본 규격은 다음을 정의합니다:
-
결제 핸들러, 안전한 결제 확인 결제 핸들러, 결제 인증 요청을 처리합니다.
-
이 결제 핸들러의 표준화된 결제 방식 식별자, "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-3 생략]
-
seenPMIs에 "secure-payment-confirmation"이 포함돼 있고 seenPMIs의 크기가 1보다 크면
RangeError를 throw합니다.
-
4.3. 사용자 활성화 요구 조건 수정
PaymentRequest.show()
메서드 단계에서
2, 3단계를 다음과 같이 수정합니다:
-
관련 글로벌 객체에서 request 가 일시적 활성화(transient activation)가 아니면, 사용자 에이전트는 다음을 수행할 수 있습니다:
-
거부된 promise를
"SecurityError"DOMException과 함께 반환.
-
-
그렇지 않으면, 사용자 활성화 소진을 관련 글로벌 객체에 대해 수행합니다.
참고: 사용자 에이전트가 사용자 활성화를 요구하지 않을 수 있으므로, 리디렉트 인증 흐름 등 활성화가 없는 상황도 지원합니다. 보안 고려는 § 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 <PaymentEntityLogo >;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"를
반환할 수 있습니다.
-
사용자 에이전트가 안전한 결제 확인을 지원하지 않거나, 지원하지만 기능이 비활성화된 경우 "
unavailable-feature-not-enabled" 반환. -
"payment" 권한 정책이 document에 활성화 되어 있지 않으면 "
unavailable-no-permission-policy" 반환. -
플랫폼 인증기가 없으면 "
unavailable-no-user-verifying-platform-authenticator" 반환. -
기타 이유로 해당 프레임에서 안전한 결제 확인 기능이 동작하지 않으면 "
unavailable-unknown-reason" 반환. -
"
available" 반환.
개발자는 SPC 흐름 시작 여부 판단 시 다음과 같이 확인할 수 있습니다:
const spcAvailable= PaymentRequest&& PaymentRequest. securePaymentConfirmationAvailability&& await PaymentRequest. securePaymentConfirmationAvailability() === 'available' ;
참고: SPC 기능 탐지는 이미 생성된 PaymentRequest 객체에서 canMakePayment
를 호출하기보다, 정적 메서드 securePaymentConfirmationAvailability
사용을 권장합니다.
참고: 본 API의 프라이버시 고려는 § 12.5 securePaymentConfirmationAvailability를 통한 지문채취 참고.
4.7. 결제 방식 데이터 검증 단계
이 결제 방식에 대한 결제 방식 데이터 검증 단계는
입력 PaymentRequest
request와 SecurePaymentConfirmationRequest
data에 대해 다음과 같습니다:
테스트
-
data["
credentialIds"] 가 비어 있으면RangeError를 throw합니다. -
data["
credentialIds"]의 각 id에 대해:-
id가 비어 있으면
RangeError를 throw합니다.
-
-
data["
instrument"]["displayName"] 이 비어 있으면TypeError를 throw합니다. -
data["
instrument"]["icon"] 이 비어 있으면TypeError를 throw합니다. -
data["
instrument"]["icon"]에 URL 파서를 실행합니다. 실패 반환이면TypeError를 throw합니다. -
data["
instrument"]["details"] 가 존재하면서 비어 있으면TypeError를 throw합니다. -
data["
payeeName"] 와 data["payeeOrigin"] 중 둘 다 생략된 경우,TypeError를 throw합니다. -
data["
payeeName"] 또는 data["payeeOrigin"] 중 하나라도 존재하면서 비어 있으면,TypeError를 throw합니다. -
data["
payeeOrigin"] 이 존재 시:-
parsedURL = URL 파서 결과 data["
payeeOrigin"]. -
parsedURL이 실패면
TypeError를 throw합니다.
-
-
data["
paymentEntitiesLogos"] 가 존재하면서 비어 있지 않으면: -
참고:
locale은 특정 입력 멤버와 관련된 언어나 방향 메타데이터와는 구별되며, 특정 문자열 값에 대한 주장이라기보다 호출자가 요청한 지역화 경험을 나타냅니다. 자세한 논의는 § 14 국제화 고려사항 참고.
4.8. 결제 가능 여부 확인 단계
이 결제 방식에 대한 결제 가능 여부 확인 단계는
입력 SecurePaymentConfirmationRequest
data에 대해 다음과 같습니다:
테스트
-
data["
payeeOrigin"] 이 존재할 경우:-
parsedURL = URL 파서 결과 data["
payeeOrigin"]. -
parsedURL이 실패가 아니라는 것을 assert합니다.
-
parsedURL의 scheme이 "
https"임을 assert합니다.
참고: 이 사전 조건은 결제 방식 데이터 검증 단계에서 이미 확인되었습니다.
-
data["
payeeOrigin"] 을 origin의 직렬화값으로 설정합니다.
-
-
아이콘 이미지 리소스를 가져오기(fetch), «["
src" → data["instrument"]["icon"]]» 를 image로 전달합니다. 실패 시:-
data["
instrument"]["iconMustBeShown"] 이true이면false반환. -
그 외에는 data["
instrument"]["icon"] 를 빈 문자열로 설정.참고: 이 결과로 RP가 해당 아이콘이 표시되지 않았음을 알 수 있으며,
instrument값의 icon이 빈 문자열입니다.
참고: 이미지 리소스는 자격증명 매칭 여부와 관계없이 항상 가져와야 하며, 자격증명 존재 탐지(프라이버시 공격) 방지를 위해서입니다.
-
-
사용자 에이전트가 data["
paymentEntitiesLogos"]의 항목을 리스트 끝에서 시작해 처음까지 제거할 수 있습니다.참고: 사용자 에이전트가 표시할 로고 수를 제어할 수 있으며, 리스트의 로고는 호출자에게 표시 우선순위 내림차순임을 보장합니다.
-
logo = data["
paymentEntitiesLogos"]의 각 항목에 대해:-
이미지 리소스 가져오기(fetch)를 logo에 대해 수행하고, «["
src" → logo["url"]]»를 image로 전달하고 결과를 디코드합니다. -
fetch 또는 decode 실패 시, logo["
url"]를 빈 문자열로 설정합니다.참고: RP가 해당 로고가 표시되지 않았음을 알 수 있으며,
paymentEntitiesLogos값의 해당 logo의 url이 빈 문자열입니다.
참고: 로고 리소스도 자격증명 매칭 여부와 관계없이 항상 가져와야 하며, 자격증명 존재 탐지(프라이버시 공격) 방지 목적입니다.
-
-
id = data["
credentialIds"]의 각 항목에 대해:-
현재 기기에서 자격증명 사용 가능 여부 조용하게 판별하는 단계를 실행하여 data["
rpId"] 및 id를 전달합니다. 결과가false이면 id를 data["credentialIds"]에서 제거합니다. -
data["
rpId"] 가 관련 설정 객체의 origin이 아니면, SPC 자격증명이 3자 파티 허용 여부를 조용히 판별하는 단계를 실행하고, data["rpId"]와 id를 전달합니다. 결과가false이면 id를 data["credentialIds"]에서 제거합니다.
-
-
true반환.
4.9. 거래 확인 UX 표시
PaymentRequest.show()
가 호출되고 안전한 결제 확인 결제 핸들러
가 선택된 경우(해당 알고리즘 19~24단계), 사용자 에이전트는 반드시 사용자에게 진행 선택 또는 방법을 결정할 수 있는 사용자 인터페이스를 표시해야 합니다.
4.9.1. 사용자에게 제공되는 정보
사용자 에이전트의 구현 자유도를 제한하지 않기 위해 이 규격은 특정 UI 표시를 요구하지 않습니다. 그러나 릴라잉 파티가
CollectedClientPaymentData
에 포함된 정보를 신뢰할 수 있도록, 사용자 에이전트는 아래 정보를 사용자에게 반드시 전달하고 인증에 대한 사용자의 동의를 반드시 수집해야 합니다:
-
payeeName이 존재하는 경우. -
payeeOrigin이 존재하는 경우. -
결제 수단
instrument상세(즉displayName,details,icon). 이미지 리소스를 입력값icon에서 fetch/decode하지 못한 경우, 사용자 에이전트는 아이콘을 여기서 표시하지 않거나 기본 결제 수단 아이콘을 대신 사용할 수 있습니다.참고: 지정 아이콘 fetch/decode 실패 시
iconMustBeShown은 반드시false이며, 아니면 결제 가능 여부 확인 단계가 실패했을 것임. -
url항목이 비어있지 않은paymentEntitiesLogos에 해당하는 로고.-
사용자 에이전트는 각
PaymentEntityLogo에 대해label표시를 요구받지 않지만, 접근성 목적상 사용하는 것이 좋음(label은 항상 포함·서명됨).
-
사용자 에이전트는
locale
정보를 활용해 웹사이트와 일치하는 언어, 포맷의 지역화된 UX 표시를 할 수 있습니다.
showOptOut
이 true인 경우, 사용자 에이전트는 지정 릴라잉 파티에
대한 과정 거부(opt-out) 선택 기회를 반드시 제공해야
합니다.
4.9.2. 거래 확인 UX 결과
사용자 에이전트는 사용자가 진행 여부와 방법을 아래 옵션 중 하나로 표시할 수 있도록 해야 합니다:
- 사용자가 SPC 자격증명으로 인증하며 결제 진행을 희망할 경우
-
PaymentRequest에 대해 user accepts the payment request algorithm 실행. - 사용자가 결제를 진행하려 하지만 SPC 자격증명을 사용할 수 없거나 사용을 원치 않을 경우
-
아래 단계 실행:
-
data["
credentialIds"] 를 빈 리스트로 설정.참고: 이로 인해
PaymentRequest.show()promise가 "NotAllowedError"DOMException으로 reject됨(§ 4.10 결제 요청 응답 단계 참고). -
해당
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 사용자 거부 참고)참고: 해당 옵션은
showOptOut이true일 때만 반드시 사용자에게 노출되어야 함.
4.9.3. 테스트 자동화 지원
현재 거래 자동화 모드가 "none"이 아니면, 사용자 에이전트는
먼저 자동화 컨텍스트에 있는지 확인해야 하며(WebDriver 보안 고려 참고), 위 정보
전달·동의 수집을 건너뛰고 아래 각 자동화 모드 값에 따라 동작해야 합니다:
- "
autoAccept" -
사용자가 거래 세부정보를 보고 수락한 것처럼 동작.
- "
autoChooseToAuthAnotherWay" -
사용자가 거래 세부정보를 보고 수락, 동시에 SPC 자격증명 인증 사용을 원치 않은 것처럼 동작. data["
credentialIds"] 가 비어 있다면 "autoAccept"와 동일. - "
autoReject" -
사용자가 거래 세부정보를 보고 거절한 것처럼 동작(즉, 거래 진행 원치 않음).
- "
autoOptOut" -
사용자가 거래 세부정보를 보고 opt-out을 희망한 것처럼 동작.
4.10. 결제 요청 응답 단계
결제 요청 응답 단계는 해당
PaymentRequest
request와 SecurePaymentConfirmationRequest
data에 대해 다음과 같습니다.
참고: 이 단계는 사용자가 거래 확인 UX를 수락한 경우에만 실행되며, user accepts the payment request algorithm 에 의해 호출됩니다.
-
data["
credentialIds"] 가 비어 있으면, "NotAllowedError"DOMException을 throw. 이는 사용자가 SPC 인증 사용이 불가능하거나 원치 않는 경우 인증 절차 프라이버시를 보장함.참고: 여기서 throw되면
PaymentRequest.show()promise가 "NotAllowedError"DOMException으로 reject됨. -
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"].
-
extensions =
AuthenticationExtensionsClientInputs딕셔너리(payment 멤버에 payment 설정, 다른 멤버는 data["extensions"]에서 복사). -
publicKeyOpts =
PublicKeyCredentialRequestOptions딕셔너리(아래 필드 갖음):challenge-
data["
challenge"]. timeout-
data["
timeout"]. rpId-
data["
rpId"]. userVerificationextensions-
extensions
참고: 이 알고리즘은 userVerification값을 "required"로 고정(Chrome 초안 구현이 지원하는 값). 향후 WG가 다른 값(예: "preferred"/"discouraged") 수요 있으면 구현자 제안 환영.
-
id = data["
credentialIds"]의 각 항목에 대해:-
descriptor =
PublicKeyCredentialDescriptor딕셔너리(아래 필드 갖음):typeid-
id
transports-
길이 1 시퀀스로, "internal" 만 포함.
-
descriptor를 publicKeyOpts["
allowCredentials"]에 append.
-
-
outputCredential = Request a Credential 알고리즘 실행, «["
publicKey" → publicKeyOpts]» 전달.참고: Chrome 초안에서는 전체
data.credentialIds가 아닌 현재 기기와 맞는 것 하나만 넘기지만, 표준은 전체 리스트를 넘김.참고: 이 단계는 [webauthn-3]의 Get 동작을 trig함.
-
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 >; // Only used for authentication.browserBoundPubKeyCredParams 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가 기본값이 됩니다.
- 클라이언트 확장 처리(등록)
-
새 자격증명 생성 시:
-
3단계 후, 다음 단계를 삽입:
-
아래 중 하나라도 참이면:
-
pkOptions["
authenticatorSelection"]["authenticatorAttachment"] 이 "platform"이 아닌 경우. -
pkOptions["
authenticatorSelection"]["residentKey"] 가 "required" 또는 "preferred"가 아닌 경우. -
pkOptions["
authenticatorSelection"]["userVerification"] 이 "required"가 아닌 경우.
이 경우
TypeError를 throw.참고: 해당 값은 Chrome의 초기 구현 때문이며, WG는 다른 값 지원 수요 있으면 구현자 제안 환영.
-
-
-
13단계 전에(
CollectedClientData생성 직전)테스트
-
bbk_allowed_algorithms =
browserBoundPubKeyCredParams. -
browserBoundPubKeyCredParams가 비어 있다면, bbk_allowed_algorithms =PublicKeyCredentialCreationOptions.pubKeyCredParams로 설정. -
bbk_and_algorithm, bbk_id = 키 쌍 생성(bbk_allowed_algorithms 사용).
-
(bbk_and_algorithm, bbk_id)가 null이면 bbk_and_algorithm, bbk_public_key 관련 하위 단계를 건너뜁니다.
-
bbk_public_key = 브라우저 바인드 공개키 얻기(bbk_and_algorithm 사용).
-
-
13단계에서는
CollectedClientData대신,CollectedClientPaymentData를 생성하며, 필드는:payment-
CollectedClientAdditionalPaymentRegistrationData필드로 초기화:browserBoundPublicKey-
bbk_public_key - COSE_Key로 인코딩된 공개키.
- 기타 필드
-
모든 나머지 필드는 기존 13단계와 동일.
-
22단계 중, "어떤 인증기도 성공 신호" 경우, 3번째 단계에서 pubKeyCred 반환 직전:
-
키 쌍 바인딩(bbk_id, pubKeyCred.
[[identifier]]사용). 실패시UnknownError반환. -
payment_outputs =
AuthenticationExtensionsPaymentOutputs생성, 필드는:browserBoundSignature-
BrowserBoundSignature로 초기화(필드:)signature-
브라우저 바인드 서명 생성(bbk_and_algorithm, clientDataJson, create credential 14단계 사용)
-
[[clientExtensionsResults]]["payment"] 에 payment_outputs 설정.
-
-
- 클라이언트 확장 처리(인증)
-
AuthenticationExtensionsPaymentInputsextension_inputs로 어트리뷰션(assertion) 실행 시:-
"secure-payment-confirmation" 결제 핸들러가 아니면 "
NotAllowedError"DOMException반환.참고: 사이트가 SPC의 확장 권한도 트랜잭션 UX를 통하지 않고 접근하려는 것을 막습니다.
-
[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)중:-
6.1단계(options.rpId와 effectiveDomain 비교) 건너뜀
참고: 이는 교차 도메인 인증 절차를 가능케 하며, § 1.1.2 가맹점의 인증 제어 참고.
-
10단계 전에(
CollectedClientData생성 직전)-
allowed_algorithms =
SecurePaymentConfirmationRequest.browserBoundPubKeyCredParams. -
bbk_and_algorithm = 브라우저 바인드 키 조회 혹은 생성(credential_id, allowed_algorithms 사용).
-
-
10단계에서는
CollectedClientData대신,CollectedClientPaymentData생성(내용은 아래와 같음):-
type= "payment.get" -
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이면 지정하지 않음.
-
나머지 필드는 기존 10단계와 동일.
-
-
"어떤 인증기도 성공 신호" 단계에서, 2단계(세팅
[[clientExtensionsResults]]):-
payment_outputs =
AuthenticationExtensionsPaymentOutputs생성(필드들):browserBoundSignature-
bbk_and_algorithm이 null이 아니면,
BrowserBoundSignature생성(필드들:)signature-
브라우저 바인드 서명 생성(bbk_and_algorithm, clientDataJson, create credential 14단계 사용)
-
맵에
[[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라는 이름으로 추가로 포함할 수 있습니다.rp와rpId가 둘 다 존재하는 경우 그 값은 동일해야 합니다. 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
및 키 쌍 식별자의 바이트 배열을 반환하는 절차는 다음과 같다:
참고: 허용된 알고리즘 또는 기본 허용 알고리즘에서 키 쌍을 생성한다.
-
만약 allowed_algorithms가 비어 있으면, 이 목록을 다음을 순서대로 포함하는 기본 목록으로 설정합니다:
-
type=PublicKeyCredentialTypewithalg= -7 ("ES256"). -
type=PublicKeyCredentialTypewithalg= -257 ("RS256").
-
-
allowed_algorithms에서 PublicKeyCredential 타입이 아닌 모든 항목을 제거합니다.
-
사용자 에이전트가 지원하지 않는 allowed_algorithms의 모든 항목을 제거합니다.
참고: 사용자 에이전트는 서로 다른 장치 플랫폼에서 서로 다른 암호화 알고리즘 세트를 지원할 수 있습니다.
-
만약 allowed_algorithms의 크기가 0이면, null을 반환합니다.
참고: 이 경우 사용자 에이전트는 어떤 브라우저 바운드 출력도 추가하지 않습니다.
-
만약 allowed_algorithms의 크기가 0보다 크면,
-
chosen_algorithm을 allowed_algorithms[0]으로 합니다.
-
chosen_algorithm에 해당하는 확립된 절차를 사용하여 공개/개인 키 쌍을 생성한 결과를 bbk로 합니다. 확립된 키 생성 알고리즘은 IANA COSE Algorithms 레지스트리를 참조하세요. 결과가 실패이면 null을 반환합니다.
-
bbk_and_algorithm을 (bbk, chosen_algorithm) 튜플로 합니다.
-
bbk_id는 다음 중 하나입니다:
-
다음과 같이 초기화된 바이트 배열:
-
bbk_id를 길이 32의 배열로 합니다.
-
getRandomValues(bbk_id)를 호출합니다.
-
-
구현 정의된 키 생성 절차로부터 키페어 핸들의 직렬화 결과인 바이트 배열. 이 바이트 배열은 브라우저 바운드 키를 가져오거나 생성할 때 나중에 키를 검색할 수 있도록 식별해야 합니다.
참고: 많은 암호화 API는 개인키 대신 식별자, 핸들, 또는 래핑된 키를 공개키와 함께 반환할 수 있습니다. 예를 들어 개인키가 보안 요소에 저장된 경우 개인키는 사용자 에이전트가 직접 접근할 수 없으며, 식별자/핸들/래핑된 키를 암호화 API의 다른 함수에 전달하여 사용해야 합니다.
-
-
keypair_map[bbk_id]를 bbk_and_algorithm으로 설정합니다.
-
(bbk_and_algorithm, bbk_id)를 반환합니다.
-
스펙은 하드웨어 저장소(주어진 순서)에서의 알고리즘을 우선하고 소프트웨어의 알고리즘을 그 다음으로 우선하도록 지정할 수 있습니다. 당분간 이 주제는 무의미할 수 있는데, Chrome은 플랫폼별 하드웨어에서 하나의 알고리즘만 지원할 계획이기 때문입니다. BBK의 저장 유형과 허용되어야 할 저장 유형, 저장 유형을 의존 당사자에 노출하는 문제 등을 논의하는 Secure Payment Confirmation issue #288을 참조하세요.
6.2. 키 쌍 바인딩
바이트 배열인 키 페어 식별자 bbk_id와 바이트 배열 credential_id가 주어졌을 때(실패를 반환할 수 있음), 다음 단계를 수행하여 키 쌍을 바인딩합니다:
참고: 브라우저 바운드 키 식별자를 Credential ID(예: 패스키 식별자)에 대해 browser_bound_map에 저장합니다.
-
browser_bound_map[credential_id]를 bbk_id로 설정합니다. 그 과정에서 발생할 수 있는
InvalidStateError,TypeError및QuotaExceededError를 잡아서 처리하고 실패를 반환합니다.참고: 오류는
write(buffer, options)와 같은 기본 파일 시스템 작업에서 발생할 수 있습니다. 여기서는 false가 반환되어 사용자 에이전트가 거래에 브라우저 바운드 키를 포함하지 않게 되며, 이 경우 의존 당사자(relying party)는 해당 거래에서 브라우저 바운드 키를 얻지 못합니다. 이후의 결제 어설션에서는 새로운 키 쌍 생성을 시도합니다.
6.3. 키 쌍 검색
바이트 배열 credential_id와 PublicKeyCredentialParameters의 목록인 allowed_algorithms가 주어졌을 때, 키 페어와 그 COSEAlgorithmIdentifier를 포함하는 튜플을 반환하기 위해 브라우저 바운드 키를 가져오거나 생성합니다:
참고: Credential ID에 대한 기존 연관 키 쌍을 찾거나 새로운 연관 키 쌍을 생성하고 해당 키 쌍과 알고리즘을 반환합니다.
-
만약 browser_bound_map[credential_id]가 존재하면
-
bbk_id를 browser_bound_map[credential_id]로 합니다.
-
bbk_and_algorithm을 keypair_map[bbk_id]로 합니다.
-
-
만약 browser_bound_map[credential_id]가 존재하지 않으면
-
(new_bbk_and_algorithm, bbk_id)를 create-a-key-pair를 사용하여 allowed_algorithms로 생성한 결과로 합니다.
-
binding_result을 bind-a-key-pair를 사용하여 bbk_id와 credential_id로 바인딩한 결과로 합니다. 결과가 실패이면 bbk_and_algorithm을 null로 합니다.
-
만약 binding_result가 실패가 아니면, bbk_and_algorithm을 new_bbk_and_algorithm으로 합니다.
-
-
bbk_and_algorithm을 반환합니다.
6.4. 브라우저 바운드 공개키 가져오기
브라우저 바운드 공개키
가져오기는 키 쌍과 COSEAlgorithmIdentifier의
튜플인
bbk_and_algorithm이 주어지면 바이트 배열을 반환한다:
참고: 브라우저 바운드 키의 인코딩된 공개키를 가져온다.
-
bbk를 bbk_and_algorithm[0]으로 합니다.
-
algorithm을 bbk_and_algorithm[1]으로 합니다.
-
public_key를 algorithm에 대한 확립된 절차에 따라 검색한 bbk의 공개키로 합니다.
-
encoded_public_key를 public_key의 COSE_Key 인코딩으로 합니다. WebAuthn의 credentialPublicKey를 참조하세요.
-
encoded_public_key를 반환합니다.
6.5. 클라이언트 데이터 서명
브라우저 바운드
서명 생성은 키 쌍과 COSEAlgorithmIdentifier의
튜플(bbk_and_algorithm)과 바이트 배열 client_data_json이 주어지면 바이트
배열을 반환한다:
참고: CollectedClientData
(CollectedClientAdditionalPaymentData
또는
CollectedClientAdditionalPaymentRegistrationData
포함)의 내용을 브라우저 바운드 키의
개인키로 서명한다.
-
bbk를 bbk_and_algorithm[0]으로 합니다.
-
algorithm을 bbk_and_algorithm[1]으로 합니다.
-
signature를 algorithm을 사용하여 bbk의 개인키로 client_data_json에 대해 수행한 결과로 합니다. IANA COSE Algorithms 레지스트리를 참조하세요.
참고: 결과는 bbk의 개인키를 사용하여 client_data_json에 대한 암호학적 서명이 됩니다.
-
signature를 반환합니다.
7. 공통 데이터 구조
다음 데이터 구조는 등록과 인증에서 공유됩니다.
7.1. PaymentCredentialInstrument
사전
dictionary PaymentCredentialInstrument {required USVString ;displayName required USVString ;icon boolean =iconMustBeShown true ;USVString ; };details
PaymentCredentialInstrument
사전은 사용자에게 보여질 정보와 거래 세부사항과 함께 서명될 정보를 포함합니다. 다음 구성원을 포함합니다:
displayName-
사용자에게 표시할 결제 수단의 이름입니다.
참고: § 14 국제화 고려사항에서
displayName의 국제화에 대해 논의합니다. icon-
결제 수단 아이콘의 URL입니다.
참고:
iconURL은 인터넷 접근이 가능한 서버(예:https://bank.example/card.png)의 이미지를 식별할 수 있고, Data URL로 아이콘 데이터를 직접 인코딩할 수도 있습니다 [RFC2397]. 두 종류의 URL 중 Data URL은 Relying Party 에 여러 이득을 줍니다. 예를 들어 아이콘 호스팅 서버가 사용 불가한 상황에서도 신뢰성을 높일 수 있습니다. 또한 Relying Party가 사용자에게 실제로 브라우저가 보여준 아이콘에 대해 암호학적으로 증명할 수 있어 검증에 유리합니다. 아이콘 URL은CollectedClientAdditionalPaymentData구조의 일부로 서명됩니다.참고: 관련 접근성 고려사항을 참고하세요.
iconMustBeShown-
요청이 성공하기 위해 지정된 아이콘이 반드시 정상적으로 가져와지고 표시되어야 하는지를 나타냅니다.
details-
사용자에게 표시할 결제 수단의 선택적 추가 설명 문자열입니다.
참고: § 14 국제화 고려사항에서
details의 국제화에 대해 논의합니다.
7.2.
PaymentEntityLogo 사전
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는 아래와 같이 진행해야 합니다:
-
credential을 Secure Payment Confirmation 결제 처리기를 성공적으로 호출해서 SPC 호출자가 획득한
PublicKeyCredential객체로 둡니다.참고: SPC는 상점의 인증 통제를 지원하도록 설계되어 있기 때문에 SPC를 호출하는 주체가 Relying Party와 다를 수 있습니다. 첫 번째 단계는 SPC 호출자가 SPC를 통해 획득한 자격 증명을 Relying Party로 반환했다고 전제합니다.
-
WebAuthn에 명시된 3-21단계를 아래 변경사항과 함께 수행합니다:
-
5단계에서 credential.
id값이 Relying Party가 SPC 호출자에게 제공한 공개키 자격 중 하나를 식별하는지 확인합니다. -
11단계에서 C["
type"] 값이 문자열payment.get인지 확인합니다. -
12단계에서 C["
challenge"] 값이 Relying Party가 SPC 호출자에게 제공한 챌린지의 base64url 인코딩과 같은지 확인합니다. -
13단계에서 C["
origin"] 값이 Relying Party가 SPC 호출을 예상한 출처와 일치하는지 확인합니다. -
13단계 이후, 다음 단계를 삽입합니다:
-
C["
payment"]["rpId"] 값이 Relying Party의 origin과 일치하는지 검증합니다. -
C["
payment"]["topOrigin"] 값이 Relying Party가 기대한 최상위 출처와 일치하는지 검증합니다. -
C["
payment"]["payeeName"] 값이 사용자에게 표시되어야 했던 수취인의 이름(존재하는 경우)과 일치하는지 검증합니다. -
C["
payment"]["payeeOrigin"] 값이 사용자에게 표시되어야 했던 수취인 출처(존재하는 경우)와 일치하는지 검증합니다. -
C["
payment"]["paymentEntitiesLogos"] 값이(존재 시) 사용자에게 표시되어야 했던 로고들에 대한 엄격하고 순서가 보장되는 부분집합인지 검증합니다.참고: 사용자 에이전트는 모든 로고를 표시하지 않을 수 있으나, 사용자에게 표시하지 않은 로고는
CollectedClientAdditionalPaymentData에 포함하면 안 됩니다. -
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
|
원격 측 단계는 아래와 같습니다:
-
parameters가 JSON Object가 아니면, WebDriver 오류와 함께 WebDriver 오류 코드 invalid argument를 반환합니다.
-
mode를 parameters에서
"mode"라는 이름의 프로퍼티 값으로 둡니다. -
mode가 undefined이거나 "
autoAccept", "autoChooseToAuthAnotherWay", "autoReject", "autoOptOut" 중 하나가 아니면 WebDriver 오류와 함께 WebDriver 오류 코드 invalid argument를 반환합니다. -
현재 거래 자동화 모드를 mode로 설정합니다.
-
성공과 함께 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가 어설션을 신중하게 검증하지 않으면 로그인 시스템에 공격이 될 수 있습니다.
공격 절차는 다음과 같습니다:
-
사용자가
attacker.example을 방문합니다. 이 사이트는 상점 사이트이거나 그런 척 합니다. -
attacker.example이relyingparty.example의 자격을 합법적이거나relyingparty.example또는 자격을 공유받은 타자로부터 탈취해서 얻습니다. -
attacker.example이 SPC 인증을 시작하고 사용자가 거래에 동의(합법적일 수도 아닐 수도 있음)합니다. -
attacker.example은 API에서 반환받은 결제 어설션을relyingparty.example의 로그인 엔드포인트(예,https://relyingparty.example/login)로 POST로 전송합니다. -
relyingparty.example이 어설션 유효성 검사 코드가 잘못되어 서명만 확인하고 필요한 필드 검증을 건너뛰고(아래 참고), 로그인 시도가 합법적이라고 인식합니다. -
relyingparty.example이 로그인 쿠키 등을attacker.example에 반환합니다. 이로써 사용자의relyingparty.example계정이 위협당하게 됩니다.
Relying Party는 두 가지 방법으로 이 공격을 방어할 수 있습니다.
첫째, Relying Party는 상황에 맞게 WebAuthn 로그인 또는 SPC 결제 에 대한 어설션 검증 절차를 항상 따라야 합니다. 특히, 아래 필드는 자격의 부적절한 사용을 감지하는데 모두 활용될 수 있습니다:
-
CollectedClientData["type"]- 로그인에는 "webauthn.get", SPC에는 "payment.get" -
CollectedClientData["challenge"] - 이 값은 Relying Party 서버가 WebAuthn 또는 SPC 호출에 앞서 사이트에 제공한 값이어야 하며, 기대했던 값과 일치하는지 검증해야 합니다. -
CollectedClientData["origin"] - SPC가 크로스 오리진인 경우 이 값은 호출자의 origin(attacker.example등)이 들어갑니다.
둘째, Relying Party는 결제와 로그인 자격을 분리 보관할 수 있습니다. 이 경우, Relying Party는
Secure Payment Confirmation 자격 등록을 하위 도메인(예, https//payment.relyingparty.example)에서만 수행하고,
결제/로그인 자격을 데이터베이스에서 분리해야 합니다.
payment
확장으로 생성된 자격만 SPC 인증에 사용할 수 있으며,
명세도 향후 이 점을 반영할 수 있습니다.
현재 구현과 명세 모두 payment
확장으로 생성된 자격은 Relying Party가 원할 경우 로그인에도 사용할 수 있습니다. 이 점은 변경되지 않을 예정입니다.
11.1.2. 결제 공격
Secure Payment Confirmation 어설션은 진행 중인 온라인 거래의 일부가 아닐 경우에는 사실상 무용지물입니다.
다양한 메커니즘은 악의적 제3자가 사용자의 계정 탈취 대신 Secure Payment Confirmation 자격(합법적 또는 비합법적 취득)으로 무단 결제를 시도하는 공격을 방지합니다:
-
공격자가 SPC를 시작하면, 사용자 에이전트는 사용자에게 수취인과 금액 등 거래 세부 정보를 명확하게 표시하는 UI를 보여줍니다. 이 상황에서 사용자가 "취소"할 가능성이 매우 높습니다.
-
만약 사용자가 거래에 동의하고 이후 WebAuthn 인증 절차를 완료하면, 공격자는 Relying Party용 서명된 SPC 어설션을 얻게 됩니다.
-
Relying Party가 거래를 예측하지 않았다면, 그 어설션을 거절합니다.
-
Relying Party가 거래를 예측한 경우, 다음 중 최소 하나를 감지하여 어설션을 거절합니다:
-
공격자가 유효한 결제와 경쟁적으로 시도할 경우 잘못된
CollectedClientData["challenge"] -
공격자가 사용자와 유효한 상점 사이트 사이에 위치해 어설션을 중계 시도하는 경우 잘못된
CollectedClientData["origin"]
-
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의 디바이스 바인딩 측면이 훼손됩니다.
그러나, 이 공격은 실질적으로 불가능합니다:
-
공격자가 BBK를 교체하려 할 때: Relying Party는 패스키 공개키로
CollectedClientData(여기에는 BBK 공개키가 들어있음)을 검증할 때 잘못된 서명을 감지할 수 있습니다. Relying Party는 해당 거래를 거절하고 이 BBK 공개키를 신뢰하지 않아야 합니다. -
공격자가 사용자를 사칭하려 할 때: 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가 동일 사용자임을 알아낼 수 있습니다.
많은 현행 온라인 결제 플로에서는, 사용자가 이름·이메일·배송지 등으로 인해 이미 결합 가능하므로 이것이 별로 큰 위험이 아닐 수 있습니다.
그러나 토큰화 등으로 식별 정보가 적은 결제 방식이 보편화된다면, 업계는 개인정보 보호를 위해 다음과 같은 방안을 고려해야 합니다:
-
결제 시스템은 제3자가 credential ID나 BBK 공개키를 저장하는 데 제한을 두는 규칙을 세울 수 있습니다.
-
Relying Party가 하나의 SPC 자격에 여러 결제 수단을 할당하는 경우, 해당 credential ID를 다른 당사자에 공유하지 않는 방식도 선택할 수 있습니다. 이때 Relying Party는 여전히 SPC 자격 자체로(1차 또는 3차 맥락에서) 사용자를 인증할 수 있습니다.
-
Relying Party(예: 은행)는 결제 수단마다 별도의 SPC 자격을 등록하게 할 수 있습니다. 이 경우에도 Relying Party 내부적으로는 두 계정을 연결할 수 있습니다.
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-feature-not-enabled: 사용자 에이전트가 Secure Payment Confirmation의 가능여부를 어떤 조건에서 설정하는지에 따라 핑거프린팅 위험이 있을 수 있습니다. 사용자 에이전트는(명세를 구현한다면) 모든 사용자에게 Secure Payment Confirmation을 제공하거나, 또는 단일 OS 사용 모두 제공하는 등 충분히 큰 그룹에만 제공하여 추적 가능성이 추가로 생기지 않도록 권장합니다. 예: 특정 OS 전체 사용자에게만 제공하면 이미 user agent string이 공개하는 수준 이외의 추가 위험은 없습니다. -
unavailable-no-permission-policy: 추가 핑거프린팅 위험은 없습니다. "payment" 권한 정책은PaymentRequest객체를 생성시도를 통해 이미 조용히 감지 가능합니다(권한 정책이 비활성화 되어있으면 생성 시 예외가 발생함). -
unavailable-no-user-verifying-platform-authenticator: 기존isUserVerifyingPlatformAuthenticatorAvailableAPI 대비 추가 핑거프린팅 위험 없음
또한 이 명세는, 사용자 프라이버시 보호를 위해 원한다면 사용자 에이전트가
구체적 이유를 알 때 다른 이유 대신
unavailable-unknown-reason
을 반환하게 허용합니다. 예를 들어 현재 프레임이 이미 위험한 타 API를 사용 중임을 감지했다면, 이렇게 처리할 수 있습니다.
12.6. 사용자 옵트아웃
API 옵션 showOptOut
은 사용자에게 Relying Party의 정보 저장 거부(옵트아웃)의사를 표시할 방법을 제공하라고 사용자 에이전트에 지시합니다. 사용자가 Opt Out을 선택하면,
OptOutError
가 호출자에 반환되어 사용자의 의사를 표시합니다. 이후 결제 정보 삭제 등 처리는 호출자 몫입니다.
구현자는 OptOutError
반환이 사용자가 자격이 있으나 인증을 마치지 않았음을 노출하지 않도록 해야 합니다. 이를 위해서는
§12.1 자격 id 탐색과 유사하게, 자격이 없는 경우에도 중간 UX에서 옵트아웃을 제공하는 식으로
처리할 수 있습니다.
이것은 브라우저 데이터 또는 자격 삭제 메커니즘이 아니라 개발자가 사용자 에이전트를 통해 옵트아웃 프롬프트를 띄우게 하는 것입니다. 사용자 에이전트는 예컨대 "이 공급자가 귀하의 결제수단 정보를 저장했을 수 있습니다. 삭제를 요청할 수 있습니다."와 같이 명확한 설명을 사용자에게 제공해야 합니다.
13. 접근성 고려사항
사용자 에이전트는 icon
과 displayName
을 함께 랜더링합니다. Relying Party는 displayName
을 통해 아이콘 표시 접근성을 충분히 보장해야 합니다(예: 아이콘이 은행 로고라면 displayName에
은행명을 포함).
본 명세를 구현하는 사용자 에이전트는 WebAuthn 접근성 고려사항 및 PaymentRequest 접근성 고려사항을 모두 따라야 합니다.
14. 국제화(i18n) 고려사항
API 호출자는 거래 대화상자에서 사용될 locale 및 표시용 문자열의 현지화를 locale
멤버로 지정해야 합니다.
일반적으로 이 멤버는 요청이 시작된 페이지의 현지화(예: 요청 트리거 버튼의 lang 속성)와 일치해야 합니다.
현재 이 명세에는 API에 입력하는 표시 문자열(예: displayName)에
언어나 방향 메타데이터를 연결하는 메커니즘은 포함되어 있지 않습니다.
그 동안 API 호출자는 다음을 준수해야 합니다:
-
locale(지정된 경우)과 표시 문자열의 언어가 일치하도록 신경쓸 것 -
문자열 내 방향 변화가 실제 표시에도 올바로 반영되는지 보장할 것(자세한 내용은 Unicode 제어 문자를 통한 양방향 텍스트 처리 및 기본 방향의 인라인 변경 참고)
구현체(및 값을 표시하려는 다른 프로세스)는 사용자 인터페이스에 표시 문자열을 삽입할 때 반드시 bidi isolation을 적용해야 합니다. 방향성이 알려져 있다면 명시적으로, 그렇지 않으면 기본(자동 추론)으로 설정해야 합니다.
15. IANA 고려사항
본 절은 확장 식별자를 IANA "WebAuthn Extension Identifiers" 레지스트리에 추가합니다([IANA-WebAuthn-Registries], [RFC8809] 참고).
-
WebAuthn 확장 식별자: payment
-
설명: 이 확장은 Secure Payment Confirmation API에서 정의한 다음 기능을 지원합니다: (1) 교차 오리진 iframe에서 자격 생성 (2) Relying Party 이외의 주체가 Relying Party를 대신해 인증 절차를 수행 (3) Secure Payment Confirmation 자격을 브라우저가 식별하고 캐싱하기. SPC가 Web Authentication과 중요한 차이가 나는 부분은 §11 보안 고려사항과 §12 개인정보 보호 고려사항 참조
-
명세 문서: 본 명세 §5 WebAuthn 확장 - "payment" 절
-
변경 통제자: W3C Web Payments Working Group
-
비고: Web Authentication Working Group의 2023년 5월 3일 논의를 따름.