โ† ๋ชฉ๋ก์œผ๋กœ
Passkey & WebAuthn Deep Dive

Google์— ๋กœ๊ทธ์ธํ•  ๋•Œ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋Œ€์‹  ์ง€๋ฌธ์„ ์ฐ๋Š” ๊ฒฝํ—˜์„ ํ•ด๋ณธ ์ ์ด ์žˆ์„ ๊ฒ๋‹ˆ๋‹ค. ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์ง€๋„ ์•Š์•˜๋Š”๋ฐ ๋กœ๊ทธ์ธ์ด ๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒŒ passkey์ž…๋‹ˆ๋‹ค.

๊ฒ‰์œผ๋กœ๋Š” ๋‹จ์ˆœํ•ด ๋ณด์ด์ง€๋งŒ, ๊ทธ ์•ˆ์—์„œ๋Š” ๊ณต๊ฐœ ํ‚ค ์•”ํ˜ธํ™”, origin ๊ฒ€์ฆ, ํ•˜๋“œ์›จ์–ด ๋ณด์•ˆ ์˜์—ญ์ด ์กฐํ•ฉ๋œ ์ธ์ฆ ํ๋ฆ„์ด ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ธ€์—์„œ๋Š” ๊ทธ ๋‚ด๋ถ€ ๊ตฌ์กฐ๋ฅผ ํ’€์–ด๋ด…๋‹ˆ๋‹ค.

๋น„๋ฐ€๋ฒˆํ˜ธ์˜ ๊ตฌ์กฐ์  ๋ฌธ์ œ

์™œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋Œ€์ฒดํ•˜๋ ค๋Š” ๊ฑธ๊นŒ์š”? ๋ถˆํŽธํ•ด์„œ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๊ตฌ์กฐ๊ฐ€ ๊ทผ๋ณธ์ ์œผ๋กœ ์ทจ์•ฝํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ๊ณต์œ  ๋น„๋ฐ€(shared secret)์ž…๋‹ˆ๋‹ค. "์„œ๋ฒ„์™€ ๋‚ด๊ฐ€ ๊ฐ™์€ ๋น„๋ฐ€์„ ์•Œ๊ณ  ์žˆ๋‹ค"๋Š” ์‚ฌ์‹ค๋กœ ๋ณธ์ธ์„ ์ฆ๋ช…ํ•ฉ๋‹ˆ๋‹ค. ์ž๋ฌผ์‡ ์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์„œ๋ฒ„๋„ ์•Œ๊ณ  ๋‚˜๋„ ์•„๋Š” ๊ตฌ์กฐ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์ด ๊ตฌ์กฐ์—์„œ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋Š” ์ง€์ ์€ ๋‘ ๊ตฐ๋ฐ์ž…๋‹ˆ๋‹ค.

์„œ๋ฒ„๊ฐ€ ํ„ธ๋ฆฌ๋ฉด? ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•ด์‹œ๊ฐ€ ์œ ์ถœ๋ฉ๋‹ˆ๋‹ค. ์˜คํ”„๋ผ์ธ์—์„œ ํฌ๋ž˜ํ‚นํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ”ผ์‹ฑ ์‚ฌ์ดํŠธ์— ์ž…๋ ฅํ•˜๋ฉด? ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” "๊ฐ’"์ด๊ธฐ ๋•Œ๋ฌธ์—, ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ€์งœ ์‚ฌ์ดํŠธ์— ์ž…๋ ฅํ•˜๋ฉด ๊ทธ๋Œ€๋กœ ๊ณต๊ฒฉ์ž์—๊ฒŒ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. OTP๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ์ฝ๊ณ  ํƒ€์ดํ•‘ํ•˜๋Š” ๊ตฌ์กฐ์ธ ์ด์ƒ, ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ค‘๊ณ„ํ•˜๋ฉด ๋šซ๋ฆฝ๋‹ˆ๋‹ค.

Passkey๋Š” ์ด ๋‘ ๋ฌธ์ œ๋ฅผ ๋™์‹œ์— ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

  • ์„œ๋ฒ„์—๋Š” ๊ณต๊ฐœ ํ‚ค๋งŒ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ์œ ์ถœ๋˜์–ด๋„ ์“ธ๋ชจ์—†์Šต๋‹ˆ๋‹ค.
  • ์ธ์ฆ์€ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋„๋ฉ”์ธ์„ ์ž๋™ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€์งœ ์‚ฌ์ดํŠธ์—์„œ๋Š” ์•„์˜ˆ ๋™์ž‘ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ•ต์‹ฌ ์•„์ด๋””์–ด: ์ž๋ฌผ์‡ ์™€ ์—ด์‡ 

Passkey์˜ ์›๋ฆฌ๋ฅผ ํ•œ ๋ฌธ์žฅ์œผ๋กœ ์š”์•ฝํ•˜๋ฉด ์ด๋ ‡์Šต๋‹ˆ๋‹ค.

์„œ๋ฒ„์—๋Š” ์ž๋ฌผ์‡ (๊ณต๊ฐœ ํ‚ค)๋ฅผ ์ฃผ๊ณ , ์—ด์‡ (๊ฐœ์ธ ํ‚ค)๋Š” ๋‚ด ๊ธฐ๊ธฐ์—๋งŒ ๋ณด๊ด€ํ•œ๋‹ค.

๋น„๋ฐ€๋ฒˆํ˜ธ ๋ฐฉ์‹์€ ์„œ๋ฒ„์™€ ๋‚ด๊ฐ€ ๊ฐ™์€ ์—ด์‡ ๋ฅผ ๊ณต์œ ํ•˜๋Š” ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค. Passkey๋Š” ์•„์˜ˆ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ์„œ๋ฒ„๊ฐ€ ๊ฐ€์ง„ ๊ฑด ์ž๋ฌผ์‡ ๋ฟ์ด๋ผ, ์„œ๋ฒ„๊ฐ€ ํ•ดํ‚น๋‹นํ•ด๋„ ์—ด์‡ ๋Š” ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

์ธ์ฆํ•  ๋•Œ๋Š” ์„œ๋ฒ„๊ฐ€ "์ด ์ƒ์ž๋ฅผ ์—ด์–ด๋ด"(์ฑŒ๋ฆฐ์ง€)๋ผ๊ณ  ๋ณด๋‚ด๋ฉด, ๋‚ด ๊ธฐ๊ธฐ๊ฐ€ ์—ด์‡ ๋กœ ์—ด์–ด์„œ ์ฆ๊ฑฐ๋ฅผ ๋Œ๋ ค๋ณด๋ƒ…๋‹ˆ๋‹ค. ์„œ๋ฒ„๋Š” ์ž๋ฌผ์‡ ๋กœ "๋งž๋Š” ์—ด์‡ ๋กœ ์—ด์—ˆ๋Š”์ง€" ํ™•์ธ๋งŒ ํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒŒ ๊ณต๊ฐœ ํ‚ค ์•”ํ˜ธํ™”(public key cryptography)์˜ ํ•ต์‹ฌ์ด๊ณ , Passkey๋Š” ์ด๊ฑธ ์›น ์ธ์ฆ์— ์ ์šฉํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

FIDO2: ๋‘ ๊ฐœ์˜ ํ‘œ์ค€

์ด ์ธ์ฆ ๊ตฌ์กฐ๋ฅผ ์‹คํ˜„ํ•˜๋Š” ๊ธฐ์ˆ  ํ‘œ์ค€์ด FIDO2์ž…๋‹ˆ๋‹ค. FIDO2๋Š” ๋‘ ๊ฐœ์˜ ํ‘œ์ค€์ด ํ•ฉ์ณ์ง„ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค.

WebAuthn (W3C): ์›น ๊ฐœ๋ฐœ์ž๊ฐ€ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•˜๋Š” JavaScript API์ž…๋‹ˆ๋‹ค. navigator.credentials.create()๋กœ ํ‚ค ์Œ์„ ๋งŒ๋“ค๊ณ , navigator.credentials.get()์œผ๋กœ ์ธ์ฆํ•ฉ๋‹ˆ๋‹ค. ๋ธŒ๋ผ์šฐ์ €์™€ ์„œ๋ฒ„ ์‚ฌ์ด์˜ ํ†ต์‹ ์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.

CTAP2 (FIDO Alliance): ๋ธŒ๋ผ์šฐ์ €์™€ ์™ธ๋ถ€ ์ธ์ฆ๊ธฐ(YubiKey ๊ฐ™์€ ํ•˜๋“œ์›จ์–ด ๋ณด์•ˆ ํ‚ค) ์‚ฌ์ด์˜ ํ†ต์‹  ํ”„๋กœํ† ์ฝœ์ž…๋‹ˆ๋‹ค. USB, NFC, Bluetooth๋กœ ํ†ต์‹ ํ•ฉ๋‹ˆ๋‹ค.

์„œ๋ฒ„(Relying Party)                   ๋ธŒ๋ผ์šฐ์ €                    ์ธ์ฆ๊ธฐ
       โ”‚                                โ”‚                         โ”‚
       โ”‚โ—„โ”€โ”€โ”€โ”€ WebAuthn API โ”€โ”€โ”€โ”€โ–บโ”‚                         โ”‚
       โ”‚      (์›น ๊ฐœ๋ฐœ์ž๊ฐ€                โ”‚โ—„โ”€โ”€โ”€โ”€ CTAP2 โ”€โ”€โ”€โ”€โ–บโ”‚ ์™ธ๋ถ€: YubiKey ๋“ฑ
       โ”‚       ๋‹ค๋ฃจ๋Š” ์˜์—ญ)              โ”‚                         โ”‚   (USB/NFC/BLE)
       โ”‚                                โ”‚                         โ”‚
       โ”‚                                โ”‚โ—„โ”€โ”€ OS ๋‚ด๋ถ€ API โ”€โ”€โ–บโ”‚ ๋‚ด์žฅ: Touch ID,
       โ”‚                                โ”‚    (CTAP2 ์•ˆ ๊ฑฐ์นจ)       โ”‚   Windows Hello ๋“ฑ

ํ•ต์‹ฌ์€ ์ด๊ฒ๋‹ˆ๋‹ค. ์›น ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ๋‹ค๋ฃจ๋Š” ๊ฑด WebAuthn API๋ฟ์ž…๋‹ˆ๋‹ค. CTAP2๋Š” ๋ธŒ๋ผ์šฐ์ €์™€ OS๊ฐ€ ์•Œ์•„์„œ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. Touch ID๋‚˜ Windows Hello ๊ฐ™์€ ๋‚ด์žฅ ์ธ์ฆ๊ธฐ๋Š” CTAP2๋„ ๊ฑฐ์น˜์ง€ ์•Š๊ณ  OS๊ฐ€ ์ง์ ‘ ํ†ต์‹ ํ•ฉ๋‹ˆ๋‹ค.

์„œ๋ฒ„(WebAuthn ์ŠคํŽ™์—์„œ๋Š” Relying Party, ์ค„์—ฌ์„œ RP๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค)๋Š” ์ฑŒ๋ฆฐ์ง€๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ๋Œ์•„์˜จ ์„œ๋ช…์„ ๊ฒ€์ฆํ•˜๋Š” ์—ญํ• ๋งŒ ํ•ฉ๋‹ˆ๋‹ค.

๋“ฑ๋ก: ํ‚ค ์Œ ๋งŒ๋“ค๊ธฐ

WebAuthn์—๋Š” ๋‘ ๊ฐ€์ง€ ํ๋ฆ„์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋“ฑ๋ก(Registration)๊ณผ ์ธ์ฆ(Authentication)์ž…๋‹ˆ๋‹ค. ์ŠคํŽ™์—์„œ๋Š” ์ด ํ๋ฆ„์„ "ceremony"๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค.

๋จผ์ € ๋“ฑ๋ก์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์„œ๋น„์Šค์— passkey๋ฅผ ์ฒ˜์Œ ์„ค์ •ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค.

1. ์„œ๋ฒ„๊ฐ€ "์ด ๋žœ๋ค ๊ฐ’์— ์„œ๋ช…ํ•ด๋ด" (challenge)๋ฅผ ๋ณด๋ƒ„
2. ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ธ์ฆ๊ธฐ์—๊ฒŒ ์ „๋‹ฌ
3. ์ธ์ฆ๊ธฐ๊ฐ€ ์ƒˆ ํ‚ค ์Œ์„ ์ƒ์„ฑ โ†’ ๊ฐœ์ธ ํ‚ค๋Š” ๊ธฐ๊ธฐ์— ์ €์žฅ
4. ๊ณต๊ฐœ ํ‚ค + ์„œ๋ช…์„ ์„œ๋ฒ„์— ์ „๋‹ฌ
5. ์„œ๋ฒ„๊ฐ€ ๊ณต๊ฐœ ํ‚ค๋ฅผ ์‚ฌ์šฉ์ž ๊ณ„์ •์— ์ €์žฅ

์ฝ”๋“œ๋กœ ๋ณด๋ฉด ์ด๋ ‡์Šต๋‹ˆ๋‹ค.

// ์„œ๋ฒ„๊ฐ€ ๋งŒ๋“œ๋Š” ๋“ฑ๋ก ์˜ต์…˜
const options: PublicKeyCredentialCreationOptions = {
  // ๋žœ๋ค ์ฑŒ๋ฆฐ์ง€. ๋งค๋ฒˆ ์ƒˆ๋กœ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  // ์ด์ „์— ์“ด ๊ฐ’์„ ์žฌ์‚ฌ์šฉํ•˜๋ฉด ์žฌ์ „์†ก ๊ณต๊ฒฉ(replay attack)์— ์ทจ์•ฝํ•ฉ๋‹ˆ๋‹ค.
  challenge: crypto.getRandomValues(new Uint8Array(32)),

  // Relying Party ์ •๋ณด.
  // id๋Š” ๋„๋ฉ”์ธ ์ด๋ฆ„์ด๊ณ , ๋“ฑ๋ก ์ดํ›„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
  // ์ด ๊ฐ’์ด ํ”ผ์‹ฑ ๋ฐฉ์–ด์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค (๋’ค์—์„œ ์ž์„ธํžˆ ๋‹ค๋ฃน๋‹ˆ๋‹ค).
  rp: {
    id: 'example.com',
    name: 'Example Service',
  },

  // ์‚ฌ์šฉ์ž ์ •๋ณด.
  // id๋Š” ๋‚ด๋ถ€ ์‹๋ณ„์ž๋กœ, ์ด๋ฉ”์ผ ๊ฐ™์€ ๊ฐœ์ธ์ •๋ณด๋ฅผ ๋„ฃ์œผ๋ฉด ์•ˆ ๋ฉ๋‹ˆ๋‹ค.
  // ์ธ์ฆ๊ธฐ์— ์ €์žฅ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
  user: {
    id: new Uint8Array(16),
    name: 'user@example.com',
    displayName: 'User',
  },

  // ํ—ˆ์šฉํ•  ์•”ํ˜ธํ™” ์•Œ๊ณ ๋ฆฌ์ฆ˜.
  // -7์€ ES256 (ํƒ€์›๊ณก์„ ), -257์€ RS256 (RSA).
  // ES256์ด ๊ฐ€์žฅ ๋„๋ฆฌ ์“ฐ์ด๊ณ , ์„œ๋ช… ํฌ๊ธฐ๋„ ์ž‘์Šต๋‹ˆ๋‹ค.
  pubKeyCredParams: [
    { alg: -7, type: 'public-key' },
    { alg: -257, type: 'public-key' },
  ],

  // ์ธ์ฆ๊ธฐ ์š”๊ตฌ์‚ฌํ•ญ.
  authenticatorSelection: {
    // 'required': ์ธ์ฆ๊ธฐ๊ฐ€ ํ‚ค๋ฅผ ์ž์ฒด ์ €์žฅํ•ด์•ผ ํ•จ (passkey์˜ ํ•„์ˆ˜ ์กฐ๊ฑด)
    residentKey: 'required',
    // 'preferred': ๊ฐ€๋Šฅํ•˜๋ฉด ์ƒ์ฒด์ธ์‹/PIN์œผ๋กœ ๋ณธ์ธ ํ™•์ธ
    userVerification: 'preferred',
  },

  timeout: 60000,
};
// ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‹คํ–‰. ์ด ์‹œ์ ์— ์ƒ์ฒด์ธ์‹ ํ”„๋กฌํ”„ํŠธ๊ฐ€ ๋œน๋‹ˆ๋‹ค.
const credential = await navigator.credentials.create({
  publicKey: options,
});

// ์‘๋‹ต์—๋Š” ๋‘ ๊ฐ€์ง€๊ฐ€ ๋“ค์–ด์žˆ์Šต๋‹ˆ๋‹ค:
// - clientDataJSON: ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋งŒ๋“  ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ (origin, challenge ๋“ฑ)
// - attestationObject: ์ธ์ฆ๊ธฐ๊ฐ€ ๋งŒ๋“  ๊ณต๊ฐœ ํ‚ค์™€ credential ID
// ์„œ๋ฒ„์—์„œ ๊ฒ€์ฆํ•  ๊ฒƒ๋“ค:
// 1. clientDataJSON์˜ origin์ด ์šฐ๋ฆฌ ๋„๋ฉ”์ธ์ธ์ง€ (ํ”ผ์‹ฑ ๋ฐฉ์–ด)
// 2. challenge๊ฐ€ ์šฐ๋ฆฌ๊ฐ€ ๋ฐœ๊ธ‰ํ•œ ๊ฒƒ๊ณผ ๊ฐ™์€์ง€ (์žฌ์ „์†ก ๋ฐฉ์–ด)
// 3. attestationObject์—์„œ ๊ณต๊ฐœ ํ‚ค๋ฅผ ๊บผ๋‚ด์„œ ์‚ฌ์šฉ์ž ๊ณ„์ •์— ์ €์žฅ

์—ฌ๊ธฐ์„œ rp.id๊ฐ€ ์™œ ์ค‘์š”ํ•œ์ง€ ์งš๊ณ  ๋„˜์–ด๊ฐ‘๋‹ˆ๋‹ค. example.com์œผ๋กœ ๋“ฑ๋ก๋œ passkey๋Š” evil-example.com์—์„œ ์ ˆ๋Œ€ ๋™์ž‘ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ˜„์žฌ ํŽ˜์ด์ง€์˜ ๋„๋ฉ”์ธ๊ณผ rp.id๋ฅผ ๋น„๊ตํ•ด์„œ, ๋‹ค๋ฅด๋ฉด ์ธ์ฆ๊ธฐ์— ์•„์˜ˆ ์š”์ฒญ์„ ๋ณด๋‚ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ URL์„ ๊ผผ๊ผผํžˆ ํ™•์ธํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋Œ€์‹  ํ•ด์ค๋‹ˆ๋‹ค.

์ธ์ฆ: ๋ณธ์ธ ์ฆ๋ช…ํ•˜๊ธฐ

๋“ฑ๋ก์ด ๋๋‚˜๋ฉด, ์ดํ›„ ๋กœ๊ทธ์ธ์€ ์ธ์ฆ ํ๋ฆ„์„ ํƒ‘๋‹ˆ๋‹ค.

1. ์„œ๋ฒ„๊ฐ€ ์ƒˆ challenge๋ฅผ ๋ณด๋ƒ„
2. ์ธ์ฆ๊ธฐ๊ฐ€ ์ €์žฅ๋œ ๊ฐœ์ธ ํ‚ค๋กœ challenge์— ์„œ๋ช…
3. ์„œ๋ช…์„ ์„œ๋ฒ„์— ์ „๋‹ฌ
4. ์„œ๋ฒ„๊ฐ€ ๋“ฑ๋ก ๋•Œ ์ €์žฅํ•œ ๊ณต๊ฐœ ํ‚ค๋กœ ์„œ๋ช…์„ ๊ฒ€์ฆ
// ์„œ๋ฒ„๊ฐ€ ๋งŒ๋“œ๋Š” ์ธ์ฆ ์˜ต์…˜
const options: PublicKeyCredentialRequestOptions = {
  challenge: crypto.getRandomValues(new Uint8Array(32)),
  rpId: 'example.com',
  // allowCredentials๋ฅผ ๋น„์›Œ๋‘๋ฉด ์ธ์ฆ๊ธฐ๊ฐ€ ์•Œ์•„์„œ
  // ์ด ๋„๋ฉ”์ธ์— ๋งž๋Š” passkey๋ฅผ ์ฐพ์•„์„œ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.
  // ์‚ฌ์šฉ์ž๊ฐ€ ์ด๋ฉ”์ผ์„ ๋จผ์ € ์ž…๋ ฅํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
  allowCredentials: [],
  userVerification: 'preferred',
  timeout: 60000,
};
const assertion = await navigator.credentials.get({
  publicKey: options,
});

// ์‘๋‹ต ๊ตฌ์กฐ:
// - clientDataJSON: origin, challenge (๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ฑ„์›€)
// - authenticatorData: ๋„๋ฉ”์ธ ํ•ด์‹œ, ์‚ฌ์šฉ์ž ํ™•์ธ ์—ฌ๋ถ€, ์„œ๋ช… ์นด์šดํ„ฐ
// - signature: ์œ„ ๋‘ ๊ฐ’์„ ๊ฐœ์ธ ํ‚ค๋กœ ์„œ๋ช…ํ•œ ๊ฒฐ๊ณผ
// - userHandle: ๋“ฑ๋ก ๋•Œ ์„ค์ •ํ•œ user.id (์ด๊ฑธ๋กœ ๋ˆ„๊ตฌ์ธ์ง€ ์‹๋ณ„)

์„œ๋ฒ„ ๊ฒ€์ฆ ๊ณผ์ •์„ ํ’€์–ด๋ณด๋ฉด ์ด๋ ‡์Šต๋‹ˆ๋‹ค.

// ์„œ๋ฒ„๊ฐ€ ํ™•์ธํ•˜๋Š” ๊ฒƒ๋“ค:

// 1. origin ํ™•์ธ: clientDataJSON์˜ origin์ด 'https://example.com'์ธ์ง€
//    โ†’ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ์—์„œ ์˜จ ์š”์ฒญ์ด๋ฉด ๊ฑฐ๋ถ€

// 2. challenge ํ™•์ธ: ์šฐ๋ฆฌ๊ฐ€ ๋ฐฉ๊ธˆ ๋ฐœ๊ธ‰ํ•œ challenge์™€ ๊ฐ™์€์ง€
//    โ†’ ์ด์ „ ์š”์ฒญ์„ ๋ณต์‚ฌํ•ด์„œ ๋ณด๋‚ด๋Š” ์žฌ์ „์†ก ๊ณต๊ฒฉ ์ฐจ๋‹จ

// 3. ์„œ๋ช… ๊ฒ€์ฆ: ๋“ฑ๋ก ๋•Œ ์ €์žฅํ•œ ๊ณต๊ฐœ ํ‚ค๋กœ signature๊ฐ€ ์œ ํšจํ•œ์ง€ ํ™•์ธ
//    ๊ฒ€์ฆ ๋Œ€์ƒ = authenticatorData + SHA-256(clientDataJSON)
//    โ†’ ์ด ๋ฐ์ดํ„ฐ๊ฐ€ ์กฐ๊ธˆ์ด๋ผ๋„ ๋ณ€์กฐ๋˜์—ˆ์œผ๋ฉด ์„œ๋ช…์ด ๊นจ์ง

// 4. ์‚ฌ์šฉ์ž ์‹๋ณ„: userHandle ๊ฐ’์œผ๋กœ ์–ด๋–ค ๊ณ„์ •์ธ์ง€ ํ™•์ธ

// 5. signCount ํ™•์ธ: ์ด์ „ ๋กœ๊ทธ์ธ ๋•Œ๋ณด๋‹ค ํฐ ๊ฐ’์ธ์ง€
//    โ†’ ์ธ์ฆ๊ธฐ๊ฐ€ ๋ณต์ œ๋˜์—ˆ๋Š”์ง€ ํƒ์ง€ (๋’ค์—์„œ ์ž์„ธํžˆ)

์„œ๋ช… ๊ฒ€์ฆ ๋Œ€์ƒ์ด authenticatorData + SHA-256(clientDataJSON)์ด๋ผ๋Š” ์ ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. clientDataJSON์—๋Š” origin๊ณผ challenge๊ฐ€ ๋“ค์–ด์žˆ๊ณ , ์ด๊ฑธ ํ•ด์‹ฑํ•œ ๊ฐ’์ด ์„œ๋ช…์— ํฌํ•จ๋˜๋ฏ€๋กœ, origin์ด๋‚˜ challenge๊ฐ€ ํ•œ ๋น„ํŠธ๋ผ๋„ ๋ฐ”๋€Œ๋ฉด ์„œ๋ช… ๊ฒ€์ฆ์ด ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. ์ค‘๊ฐ„์—์„œ ๊ฐ’์„ ๋ฐ”๊พธ๋Š” ๊ฒƒ์ด ๋ถˆ๊ฐ€๋Šฅํ•œ ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.

Passkey: ๋™๊ธฐํ™”๋˜๋Š” ํ‚ค

์—ฌ๊ธฐ๊นŒ์ง€๊ฐ€ WebAuthn์˜ ์›๋ž˜ ์„ค๊ณ„์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ด ์„ค๊ณ„์—๋Š” ์‹ค์šฉ์ ์ธ ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๊ฐœ์ธ ํ‚ค๊ฐ€ ํ•˜๋“œ์›จ์–ด ์ธ์ฆ๊ธฐ(YubiKey ๋“ฑ) ์•ˆ์— ๊ฐ‡ํ˜€ ์žˆ์œผ๋‹ˆ, ๊ธฐ๊ธฐ๋ฅผ ์žƒ์–ด๋ฒ„๋ฆฌ๋ฉด ํ‚ค๋„ ๊ฐ™์ด ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค. ๋ณด์•ˆ์ ์œผ๋กœ๋Š” ์ด์ƒ์ ์ด์ง€๋งŒ, ์ผ๋ฐ˜ ์‚ฌ์šฉ์ž์—๊ฒŒ "YubiKey๋ฅผ ํ•ญ์ƒ ๋“ค๊ณ  ๋‹ค๋‹ˆ์„ธ์š”"๋ผ๊ณ  ํ•  ์ˆ˜๋Š” ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

Passkey๋Š” ์ด ๋ฌธ์ œ๋ฅผ ๊ฐœ์ธ ํ‚ค์˜ ํด๋ผ์šฐ๋“œ ๋™๊ธฐํ™”๋กœ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค. "Passkey"๋ผ๋Š” ๋‹จ์–ด๋Š” ๊ธฐ์ˆ  ํ‘œ์ค€ ์ด๋ฆ„์ด ์•„๋‹ˆ๋ผ, Apple/Google/Microsoft๊ฐ€ ๋งŒ๋“  ๋งˆ์ผ€ํŒ… ์šฉ์–ด์ž…๋‹ˆ๋‹ค. ๊ธฐ์ˆ ์ ์œผ๋กœ๋Š” "๋™๊ธฐํ™”๋˜๋Š” WebAuthn discoverable credential"์ž…๋‹ˆ๋‹ค.

Device-bound (๊ธฐ๊ธฐ ๊ท€์†)Synced Passkey (๋™๊ธฐํ™”)
๊ฐœ์ธ ํ‚ค ์œ„์น˜๊ธฐ๊ธฐ์˜ ๋ณด์•ˆ ์นฉ ์•ˆ. ์ ˆ๋Œ€ ๋ฐ–์œผ๋กœ ์•ˆ ๋‚˜๊ฐํด๋ผ์šฐ๋“œ์— E2E ์•”ํ˜ธํ™”๋กœ ์ €์žฅ, ๊ธฐ๊ธฐ ๊ฐ„ ๋™๊ธฐํ™”
๊ธฐ๊ธฐ๋ฅผ ์žƒ์œผ๋ฉดํ‚ค๋„ ์‚ฌ๋ผ์ง. ๋ฐฑ์—… ์ˆ˜๋‹จ ํ•„์š”๋‹ค๋ฅธ ๊ธฐ๊ธฐ์—์„œ ์ž๋™ ๋ณต์›
์–ด๋””์„œ ์“ธ ์ˆ˜ ์žˆ๋‚˜๋“ฑ๋กํ•œ ๊ธฐ๊ธฐ์—์„œ๋งŒ๊ฐ™์€ ์ƒํƒœ๊ณ„(Apple/Google/MS)์˜ ๋ชจ๋“  ๊ธฐ๊ธฐ
๋ณด์•ˆ ์ˆ˜์ค€์ตœ๊ณ  (๋ฌผ๋ฆฌ์  ์†Œ์œ  ์ฆ๋ช…)๋†’์Œ (ํด๋ผ์šฐ๋“œ ๊ณ„์ • ๋ณด์•ˆ์— ์˜์กด)
์ ํ•ฉํ•œ ์ƒํ™ฉ๊ธฐ์—… ํ™˜๊ฒฝ, ๊ณ ๋ณด์•ˆ ์‹œ์Šคํ…œ์ผ๋ฐ˜ ์†Œ๋น„์ž ์„œ๋น„์Šค

ํ”Œ๋žซํผ๋ณ„ ๋™์ž‘ ๋ฐฉ์‹

Apple: iCloud Keychain์œผ๋กœ ๋™๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค. iPhone์—์„œ ๋งŒ๋“  passkey๊ฐ€ Mac์—์„œ๋„ ์“ธ ์ˆ˜ ์žˆ๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค. ๊ฐœ์ธ ํ‚ค๋Š” ๊ธฐ๊ธฐ์˜ Secure Enclave์—์„œ๋งŒ ๋ณตํ˜ธํ™”๋˜๊ณ , iCloud์—๋Š” ์•”ํ˜ธํ™”๋œ ์ƒํƒœ๋กœ๋งŒ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

Google: Google Password Manager๋ฅผ ํ†ตํ•ด Android ๊ธฐ๊ธฐ ๊ฐ„, ๊ทธ๋ฆฌ๊ณ  Chrome ๋ฐ์Šคํฌํ†ฑ์—์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Microsoft: Windows Hello๋ฅผ ํ†ตํ•ด ๊ด€๋ฆฌํ•˜๋ฉฐ, Microsoft ๊ณ„์ •์— ์—ฐ๊ฒฐ๋œ ๊ธฐ๊ธฐ ๊ฐ„ ๋™๊ธฐํ™”๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

์ƒํƒœ๊ณ„ ๊ฐ„ ์ด๋™: CXP/CXF

ํ•œ ๊ฐ€์ง€ ์ œ์•ฝ์ด ์žˆ์Šต๋‹ˆ๋‹ค. Apple์—์„œ ๋งŒ๋“  passkey๋Š” Android๋กœ ์ž๋™ ๋™๊ธฐํ™”๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ƒํƒœ๊ณ„๊ฐ€ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด FIDO Alliance๊ฐ€ ๋‘ ๊ฐ€์ง€ ํ‘œ์ค€์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

  • CXF (Credential Exchange Format): passkey ๋ฐ์ดํ„ฐ์˜ ๊ตํ™˜ ํ˜•์‹. 2025๋…„ 8์›” ํ‘œ์ค€ ์Šน์ธ.
  • CXP (Credential Exchange Protocol): ๊ตํ™˜์ด ์–ด๋–ป๊ฒŒ ์ด๋ฃจ์–ด์ง€๋Š”์ง€. ์ „์†ก ์ค‘ E2E ์•”ํ˜ธํ™”๋ฅผ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

Apple์ด iOS 26(2025๋…„ 9์›”)์—์„œ CXP๋ฅผ ์ตœ์ดˆ ๊ตฌํ˜„ํ•˜์—ฌ, iCloud Keychain์˜ passkey๋ฅผ Bitwarden ๊ฐ™์€ ์„œ๋“œํŒŒํ‹ฐ ์•ฑ์œผ๋กœ ๋‚ด๋ณด๋‚ผ ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋‹น์žฅ ๋‹ค๋ฅธ ์ƒํƒœ๊ณ„ ๊ธฐ๊ธฐ์—์„œ passkey๋ฅผ ์จ์•ผ ํ•œ๋‹ค๋ฉด, Cross-Device Authentication์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

1. PC ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋กœ๊ทธ์ธ ์‹œ๋„
2. "๋‹ค๋ฅธ ๊ธฐ๊ธฐ์˜ ํŒจ์Šคํ‚ค ์‚ฌ์šฉ" ์„ ํƒ
3. QR ์ฝ”๋“œ๊ฐ€ ํ‘œ์‹œ๋จ
4. ์Šค๋งˆํŠธํฐ์œผ๋กœ QR ์Šค์บ”
5. ์Šค๋งˆํŠธํฐ๊ณผ PC๊ฐ€ Bluetooth(BLE)๋กœ ๊ฐ€๊นŒ์ด ์žˆ๋Š”์ง€ ํ™•์ธ
6. ์Šค๋งˆํŠธํฐ์—์„œ ์ƒ์ฒด์ธ์‹
7. ์„œ๋ช…์ด PC ๋ธŒ๋ผ์šฐ์ €๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„๋กœ ์ „๋‹ฌ

5๋ฒˆ์˜ Bluetooth ๊ทผ์ ‘์„ฑ ํ™•์ธ์ด ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค. QR ์ฝ”๋“œ๋งŒ์œผ๋กœ๋Š” ์›๊ฒฉ ๊ณต๊ฒฉ์ž๊ฐ€ ์Šคํฌ๋ฆฐ์ƒท์„ ์ฐ์–ด์„œ ์•…์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, Bluetooth ๋ฒ”์œ„(์•ฝ 10m) ์•ˆ์— ๋ฌผ๋ฆฌ์ ์œผ๋กœ ์žˆ์–ด์•ผ ํ•˜๋ฏ€๋กœ ์›๊ฒฉ ํ”ผ์‹ฑ์ด ์ฐจ๋‹จ๋ฉ๋‹ˆ๋‹ค.

์ด๋ฉ”์ผ ์ž…๋ ฅ ์—†์ด ๋กœ๊ทธ์ธํ•˜๊ธฐ

๋น„๋ฐ€๋ฒˆํ˜ธ ๋กœ๊ทธ์ธ์—์„œ๋Š” ์ด๋ฉ”์ผ์„ ๋จผ์ € ์ž…๋ ฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„๊ฐ€ "์ด ์‚ฌ์šฉ์ž์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•ด์‹œ๊ฐ€ ๋ญ์˜€์ง€?" ํ•˜๊ณ  ์ฐพ์•„์•ผ ํ•˜๋‹ˆ๊นŒ์š”.

์ดˆ๊ธฐ WebAuthn์—์„œ๋„ ๋น„์Šทํ–ˆ์Šต๋‹ˆ๋‹ค. ์„œ๋ฒ„๊ฐ€ allowCredentials์— "์ด credential ID๋“ค ์ค‘ ํ•˜๋‚˜๋ฅผ ์จ๋ผ"๊ณ  ๋ชฉ๋ก์„ ๋„˜๊ฒจ์ค˜์•ผ ํ–ˆ๊ณ , ๊ทธ๋Ÿฌ๋ ค๋ฉด ๋จผ์ € ๋ˆ„๊ตฌ์ธ์ง€ ์•Œ์•„์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

Discoverable Credential(๊ณผ๊ฑฐ์—๋Š” Resident Key๋ผ๊ณ  ๋ถˆ๋ €์Šต๋‹ˆ๋‹ค)์ด ์ด ์ œ์•ฝ์„ ์—†์•ฑ๋‹ˆ๋‹ค. ์ธ์ฆ๊ธฐ๊ฐ€ credential ID์™€ user.id๋ฅผ ์ž์ฒด ์ €์žฅํ•˜๋ฏ€๋กœ, ์„œ๋ฒ„๊ฐ€ ๋ชฉ๋ก์„ ์•ˆ ๋„˜๊ฒจ์ค˜๋„ ์ธ์ฆ๊ธฐ๊ฐ€ "์ด ๋„๋ฉ”์ธ์— ๋‚ด๊ฐ€ ๊ฐ€์ง„ ํ‚ค๊ฐ€ ์žˆ๋„ค" ํ•˜๊ณ  ์•Œ์•„์„œ ์ฐพ์Šต๋‹ˆ๋‹ค.

const options: PublicKeyCredentialRequestOptions = {
  challenge: crypto.getRandomValues(new Uint8Array(32)),
  rpId: 'example.com',
  // ๋นˆ ๋ฐฐ์—ด = "๋„ค๊ฐ€ ์•Œ์•„์„œ ์ฐพ์•„๋ด"
  // ์ธ์ฆ๊ธฐ๊ฐ€ ์ด ๋„๋ฉ”์ธ์— ๋“ฑ๋ก๋œ passkey๋ฅผ ์ž์ฒด์ ์œผ๋กœ ์ฐพ์•„์„œ ๋ณด์—ฌ์คŒ
  allowCredentials: [],
  userVerification: 'required',
};

const assertion = await navigator.credentials.get({ publicKey: options });

// assertion.response.userHandle์— ๋“ฑ๋ก ๋•Œ ์„ค์ •ํ•œ user.id๊ฐ€ ๋‹ด๊ฒจ์˜ด
// ์„œ๋ฒ„๋Š” ์ด ๊ฐ’์œผ๋กœ "์•„, ์ด ์‚ฌ์šฉ์ž๊ตฌ๋‚˜" ํ•˜๊ณ  ์‹๋ณ„

๋ชจ๋“  passkey๋Š” ์ •์˜์ƒ discoverable credential์ž…๋‹ˆ๋‹ค. Passkey๋ฅผ ์ง€์›ํ•œ๋‹ค๋ฉด ์ด๋ฉ”์ผ ์ž…๋ ฅ ์—†๋Š” ๋กœ๊ทธ์ธ์ด ์ž๋™์œผ๋กœ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

Conditional UI: ๊ธฐ์กด ๋กœ๊ทธ์ธ ํผ๊ณผ ๊ณต์กดํ•˜๊ธฐ

"passkey๋กœ๋งŒ ๋กœ๊ทธ์ธ"์„ ๊ฐ•์ œํ•˜๋ฉด passkey๊ฐ€ ์—†๋Š” ์‚ฌ์šฉ์ž๋Š” ๋กœ๊ทธ์ธ์„ ๋ชป ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์‹ค์ ์œผ๋กœ๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ์™€ passkey๊ฐ€ ๊ณต์กดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Conditional UI๊ฐ€ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ๋ณ„๋„์˜ "ํŒจ์Šคํ‚ค๋กœ ๋กœ๊ทธ์ธ" ๋ฒ„ํŠผ ์—†์ด, ๊ธฐ์กด ์ด๋ฉ”์ผ ์ž…๋ ฅ ํ•„๋“œ์˜ ์ž๋™ ์™„์„ฑ ๋ชฉ๋ก์— passkey ์˜ต์…˜์ด ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค. ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž๋™ ์™„์„ฑ ์˜†์— passkey๊ฐ€ ํ•จ๊ป˜ ๋œจ๋Š” ๊ฑฐ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๊ตฌํ˜„์€ ์„ธ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค.

<!-- 1๋‹จ๊ณ„: input์— webauthn ํžŒํŠธ ์ถ”๊ฐ€ -->
<input
  type="text"
  name="username"
  autocomplete="username webauthn"
  placeholder="์ด๋ฉ”์ผ ๋˜๋Š” ์‚ฌ์šฉ์ž ์ด๋ฆ„"
/>
// 2๋‹จ๊ณ„: ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ด ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜๋Š”์ง€ ํ™•์ธ
if (await PublicKeyCredential.isConditionalMediationAvailable()) {
  // 3๋‹จ๊ณ„: mediation: 'conditional'๋กœ ํ˜ธ์ถœ
  const assertion = await navigator.credentials.get({
    publicKey: {
      challenge: serverChallenge,
      rpId: 'example.com',
      allowCredentials: [],
      userVerification: 'preferred',
    },
    mediation: 'conditional',  // ์ด๊ฒŒ ํ•ต์‹ฌ
  });
}

mediation: 'conditional'์ด ์—†์œผ๋ฉด navigator.credentials.get()์€ ์ฆ‰์‹œ ๋ชจ๋‹ฌ ํŒ์—…์„ ๋„์›๋‹ˆ๋‹ค. 'conditional'์„ ์ฃผ๋ฉด ํŒ์—… ๋Œ€์‹  ์ž๋™ ์™„์„ฑ ๋ชฉ๋ก์— ์กฐ์šฉํžˆ ๋ผ์–ด๋“ญ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ passkey๋ฅผ ์„ ํƒํ•˜๋ฉด ๊ทธ๋•Œ ์ƒ์ฒด์ธ์‹์ด ์‹œ์ž‘๋˜๊ณ , passkey๊ฐ€ ์—†์œผ๋ฉด ๊ธฐ์กด๋Œ€๋กœ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.

Conditional Create: ๋น„๋ฐ€๋ฒˆํ˜ธ์—์„œ Passkey๋กœ ์ž์—ฐ์Šค๋Ÿฌ์šด ์ „ํ™˜

๋ฐ˜๋Œ€ ๋ฐฉํ–ฅ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋กœ ๋กœ๊ทธ์ธ์— ์„ฑ๊ณตํ•œ ํ›„, ์ž๋™ ์™„์„ฑ ์˜์—ญ์— "์ด ์‚ฌ์ดํŠธ์— ํŒจ์Šคํ‚ค ๋งŒ๋“ค๊ธฐ" ์˜ต์…˜์„ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. ๋ณ„๋„ ํŽ˜์ด์ง€๋กœ ๋ณด๋‚ด๊ฑฐ๋‚˜ ๋ฐฐ๋„ˆ๋ฅผ ๋„์šฐ์ง€ ์•Š์•„๋„, ๋น„๋ฐ€๋ฒˆํ˜ธ ์‚ฌ์šฉ์ž๋ฅผ passkey๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ Safari์™€ ์ผ๋ถ€ ํŒจ์Šค์›Œ๋“œ ๋งค๋‹ˆ์ €์—์„œ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

๋ณด์•ˆ: ์™œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ณด๋‹ค ์•ˆ์ „ํ•œ๊ฐ€

๊ตฌ์ฒด์ ์œผ๋กœ ์–ด๋–ค ๊ณต๊ฒฉ์„ ๋ง‰์„ ์ˆ˜ ์žˆ๋Š”์ง€ ๋น„๊ตํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ณต๊ฒฉ ์œ ํ˜•๋น„๋ฐ€๋ฒˆํ˜ธOTP (TOTP/SMS)Passkey
ํ”ผ์‹ฑ๊ฐ€์งœ ์‚ฌ์ดํŠธ์— ์ž…๋ ฅํ•˜๋ฉด ๋์‹ค์‹œ๊ฐ„ ์ค‘๊ณ„ํ•˜๋ฉด ๋šซ๋ฆผ์ฐจ๋‹จ. ๋„๋ฉ”์ธ์ด ๋‹ค๋ฅด๋ฉด ์•„์˜ˆ ๋™์ž‘ ์•ˆ ํ•จ
์„œ๋ฒ„ ํ•ดํ‚นํ•ด์‹œ ์œ ์ถœ โ†’ ํฌ๋ž˜ํ‚น ๊ฐ€๋Šฅ์‹œ๋“œ ์œ ์ถœ โ†’ OTP ์ƒ์„ฑ ๊ฐ€๋Šฅ๋ฌด์˜๋ฏธ. ๊ณต๊ฐœ ํ‚ค๋งŒ ์žˆ์–ด์„œ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒŒ ์—†์Œ
ํฌ๋ฆฌ๋ด์…œ ์Šคํ„ฐํ•‘๊ฐ™์€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋‹ค๋ฅธ ์‚ฌ์ดํŠธ์— ์‹œ๋„ํ•ด๋‹น ์—†์Œ๋ถˆ๊ฐ€. ํ‚ค ์Œ์ด ๋„๋ฉ”์ธ๋ณ„๋กœ ๋‹ค๋ฆ„
์ค‘๊ฐ„์ž ๊ณต๊ฒฉ๊ฐ’์„ ๊ฐ€๋กœ์ฑ„์„œ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅ์‹ค์‹œ๊ฐ„ OTP ์ค‘๊ณ„ ๊ฐ€๋Šฅ์ฐจ๋‹จ. ์ฑŒ๋ฆฐ์ง€ ์„œ๋ช…์ด origin์— ๋ฌถ์—ฌ ์žˆ์Œ

ํ”ผ์‹ฑ ๋ฐฉ์–ด๊ฐ€ ๋™์ž‘ํ•˜๋Š” ์›๋ฆฌ

๋น„๋ฐ€๋ฒˆํ˜ธ์™€ OTP๊ฐ€ ํ”ผ์‹ฑ์— ์ทจ์•ฝํ•œ ๊ทผ๋ณธ ์›์ธ์€, ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ’์„ ๋ˆˆ์œผ๋กœ ์ฝ๊ณ  ์†์œผ๋กœ ์ž…๋ ฅํ•˜๋Š” ๊ตฌ์กฐ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. examp1e.com(์ˆซ์ž 1)๊ณผ example.com(์•ŒํŒŒ๋ฒณ l)์„ ๋งค๋ฒˆ ๊ตฌ๋ถ„ํ•˜๊ธด ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

Passkey์—์„œ๋Š” ์‚ฌ์šฉ์ž์˜ ํŒ๋‹จ์ด ๊ฐœ์ž…ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

1. ์„œ๋ฒ„๊ฐ€ rpId: 'example.com'์œผ๋กœ ์ฑŒ๋ฆฐ์ง€ ๋ฐœ๊ธ‰
2. ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ˜„์žฌ ํŽ˜์ด์ง€์˜ origin๊ณผ rpId๋ฅผ ๋น„๊ต
3. evil-example.com โ‰  example.com โ†’ ์ธ์ฆ๊ธฐ์— ์š”์ฒญ ์ž์ฒด๋ฅผ ์•ˆ ๋ณด๋ƒ„
4. ์‚ฌ์šฉ์ž์—๊ฒŒ passkey ์„ ํƒ์ง€๊ฐ€ ์•„์˜ˆ ํ‘œ์‹œ๋˜์ง€ ์•Š์Œ

์ด ๊ฒ€์ฆ์€ ๋ธŒ๋ผ์šฐ์ € ๋‚ด๋ถ€์—์„œ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค. JavaScript๋กœ ์šฐํšŒํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๋‹ค์šด๊ทธ๋ ˆ์ด๋“œ ๊ณต๊ฒฉ: ์™„๋ฒฝํ•˜์ง„ ์•Š๋‹ค

Passkey ์ž์ฒด์˜ ์•”ํ˜ธํ•™์€ ์•ˆ์ „ํ•˜์ง€๋งŒ, ์ธ์ฆ ์‹œ์Šคํ…œ ์ „์ฒด๊ฐ€ ์•ˆ์ „ํ•œ ๊ฑด ๋ณ„๊ฐœ์ž…๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด Proofpoint ์—ฐ๊ตฌํŒ€์ด ๋ฐœ๊ฒฌํ•œ Microsoft Entra ID ๊ณต๊ฒฉ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ”ผ์‹ฑ ํ”„๋ก์‹œ๊ฐ€ "์ด ๋ธŒ๋ผ์šฐ์ €๋Š” passkey๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค"๋ผ๊ณ  ๊ฑฐ์ง“๋งํ•˜๋ฉด, Entra๊ฐ€ passkey๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•˜๊ณ  ๋น„๋ฐ€๋ฒˆํ˜ธ ํด๋ฐฑ์œผ๋กœ ์œ ๋„ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜๊ฒŒ ๋˜๊ณ , ๊ทธ๊ฑธ ๊ณต๊ฒฉ์ž๊ฐ€ ๊ฐ€๋กœ์ฑ•๋‹ˆ๋‹ค.

์ด๊ฑด passkey์˜ ๊ฒฐํ•จ์ด ์•„๋‹ˆ๋ผ ํด๋ฐฑ ๊ฒฝ๋กœ์˜ ์„ค๊ณ„ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. Passkey์˜ ๋ณด์•ˆ์„ ์˜จ์ „ํžˆ ์–ป์œผ๋ ค๋ฉด, ์•ฝํ•œ ์ธ์ฆ ์ˆ˜๋‹จ์œผ๋กœ์˜ ํด๋ฐฑ์„ ์—†์• ๊ฑฐ๋‚˜ ์ถ”๊ฐ€ ๊ฒ€์ฆ์„ ๊ฑธ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

signCount: ๋ณต์ œ ํƒ์ง€

์ธ์ฆ๊ธฐ๋Š” ์ธ์ฆํ•  ๋•Œ๋งˆ๋‹ค ๋‚ด๋ถ€ ์นด์šดํ„ฐ(signCount)๋ฅผ 1์”ฉ ์˜ฌ๋ฆฝ๋‹ˆ๋‹ค. ์„œ๋ฒ„๋Š” "์ง€๋‚œ๋ฒˆ์— 42์˜€๋Š”๋ฐ ์ด๋ฒˆ์— 43์ด ์™”์œผ๋‹ˆ ์ •์ƒ"์ด๋ผ๊ณ  ํŒ๋‹จํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ 42 ์ดํ•˜๊ฐ€ ์˜ค๋ฉด ์ธ์ฆ๊ธฐ๊ฐ€ ๋ณต์ œ๋˜์—ˆ์„ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์„œ๋ฒ„์— ์ €์žฅ๋œ signCount: 42
โ†’ ์ด๋ฒˆ ์š”์ฒญ์˜ signCount๊ฐ€ 43 โ†’ ์ •์ƒ
โ†’ ์ด๋ฒˆ ์š”์ฒญ์˜ signCount๊ฐ€ 42 ์ดํ•˜ โ†’ ๋ณต์ œ ์˜์‹ฌ โ†’ ๊ฒฝ๊ณ  ๋˜๋Š” ์ฐจ๋‹จ

๋‹ค๋งŒ synced passkey๋Š” ์—ฌ๋Ÿฌ ๊ธฐ๊ธฐ์—์„œ ๋™์‹œ์— ์“ธ ์ˆ˜ ์žˆ์–ด์„œ, ์นด์šดํ„ฐ๊ฐ€ ์ˆœ์„œ๋Œ€๋กœ ์ฆ๊ฐ€ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ผ๋ถ€ ํ”Œ๋žซํผ์€ synced passkey์˜ signCount๋ฅผ ํ•ญ์ƒ 0์œผ๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ํ•˜๋“œ์›จ์–ด ๋ณด์•ˆ ํ‚ค ๊ฐ™์€ device-bound credential์—์„œ๋Š” ์—ฌ์ „ํžˆ ์œ ํšจํ•œ ํƒ์ง€ ์ˆ˜๋‹จ์ž…๋‹ˆ๋‹ค.

Attestation: "์–ด๋–ค ์ธ์ฆ๊ธฐ๋กœ ๋งŒ๋“ค์—ˆ๋‚˜"

๋“ฑ๋กํ•  ๋•Œ ์ธ์ฆ๊ธฐ๋Š” ์„ ํƒ์ ์œผ๋กœ attestation(์ฆ๋ช…)์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. "์ด ํ‚ค ์Œ์€ ์ธ์ฆ๋ฐ›์€ ํ•˜๋“œ์›จ์–ด์—์„œ ๋งŒ๋“ค์–ด์กŒ์Šต๋‹ˆ๋‹ค"๋ผ๋Š” ์ผ์ข…์˜ ์›์‚ฐ์ง€ ์ฆ๋ช…์„œ์ž…๋‹ˆ๋‹ค.

์œ ํ˜•์˜๋ฏธ์–ธ์ œ ์“ฐ๋‚˜
None์ฆ๋ช… ์•ˆ ํ•จ. ์ธ์ฆ๊ธฐ๊ฐ€ ๋ญ๋“  ์ƒ๊ด€์—†์Œ์ผ๋ฐ˜ ์†Œ๋น„์ž ์„œ๋น„์Šค (๋Œ€๋ถ€๋ถ„ ์ด๊ฑธ ์”€)
Self๋ฐฉ๊ธˆ ๋งŒ๋“  ํ‚ค ์Œ์œผ๋กœ ์Šค์Šค๋กœ ์„œ๋ช…์ธ์ฆ๊ธฐ ์ข…๋ฅ˜๋ฅผ ๊ตณ์ด ํ™•์ธ ์•ˆ ํ•  ๋•Œ
Packed์ œ์กฐ์‚ฌ๊ฐ€ ์ธ์ฆ๊ธฐ์— ์‹ฌ์–ด๋‘” ์ธ์ฆ์„œ๋กœ ์„œ๋ช…ํŠน์ • ๋ชจ๋ธ๋งŒ ํ—ˆ์šฉํ•˜๊ณ  ์‹ถ์„ ๋•Œ (๊ธฐ์—…์šฉ)
TPMTPM 2.0 ์นฉ์˜ ํ‘œ์ค€ attestationWindows Hello
Android KeyAndroid Keystore attestationAndroid
AppleApple Anonymous AttestationApple ๊ธฐ๊ธฐ
FIDO U2F์ด์ „ ์„ธ๋Œ€(CTAP1) ๋ณด์•ˆ ํ‚ค์˜ attestation๋ ˆ๊ฑฐ์‹œ ํ˜ธํ™˜

๋Œ€๋ถ€๋ถ„์˜ ์„œ๋น„์Šค์—์„œ๋Š” attestation: 'none'์ด๋ฉด ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ์–ด๋–ค ์ธ์ฆ๊ธฐ๋ฅผ ์“ฐ๋“  ๊ณต๊ฐœ ํ‚ค ๊ธฐ๋ฐ˜ ์ธ์ฆ์˜ ๋ณด์•ˆ ์ด์ ์€ ๋™์ผํ•˜๋‹ˆ๊นŒ์š”. ๊ธฐ์—… ํ™˜๊ฒฝ์—์„œ "๋ฐ˜๋“œ์‹œ FIDO Certified ํ•˜๋“œ์›จ์–ด ํ‚ค๋งŒ ํ—ˆ์šฉ"ํ•˜๊ณ  ์‹ถ์„ ๋•Œ attestation ๊ฒ€์ฆ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๊ตฌํ˜„ํ•  ๋•Œ ์•Œ์•„์•ผ ํ•  ๊ฒƒ๋“ค

๋ณต๊ตฌ ์ „๋žต์€ ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•˜๋‹ค

Passkey๋งŒ ์ง€์›ํ•˜๊ณ  ๋‹ค๋ฅธ ์ธ์ฆ ์ˆ˜๋‹จ์ด ์—†์œผ๋ฉด, ๋ชจ๋“  ๊ธฐ๊ธฐ๋ฅผ ์žƒ์—ˆ์„ ๋•Œ ๊ณ„์ •์— ์ ‘๊ทผํ•  ๋ฐฉ๋ฒ•์ด ์—†์Šต๋‹ˆ๋‹ค.

  • ๋‹ค์ค‘ passkey ๋“ฑ๋ก: ์—ฌ๋Ÿฌ ๊ธฐ๊ธฐ์— passkey๋ฅผ ๋“ฑ๋กํ•˜๋„๋ก ์œ ๋„
  • ํ•˜๋“œ์›จ์–ด ๋ฐฑ์—… ํ‚ค: YubiKey ๊ฐ™์€ ๋ณด์•ˆ ํ‚ค๋ฅผ ๋ฐฑ์—…์šฉ์œผ๋กœ ๋“ฑ๋ก
  • ๋ณต๊ตฌ ์ฝ”๋“œ: ์ผํšŒ์šฉ ์ฝ”๋“œ๋ฅผ ์˜คํ”„๋ผ์ธ์— ๋ณด๊ด€ํ•˜๋„๋ก ์•ˆ๋‚ด
  • ์ ์ง„์  ์ „ํ™˜: ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ฐ”๋กœ ์ œ๊ฑฐํ•˜์ง€ ๋ง๊ณ  passkey์™€ ๋ณ‘ํ–‰ ์šด์˜

RP ID๋Š” ํ•œ ๋ฒˆ ์ •ํ•˜๋ฉด ๋ชป ๋ฐ”๊พผ๋‹ค

rp.id๋Š” credential์— ์˜๊ตฌ์ ์œผ๋กœ ๋ฌถ์ž…๋‹ˆ๋‹ค. ์ฒ˜์Œ์— ์ž˜ ์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

rp.id๋ฅผ 'example.com'์œผ๋กœ ์„ค์ •ํ•˜๋ฉด:
  example.com       โ†’ โœ… ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  app.example.com   โ†’ โœ… ํ•˜์œ„ ๋„๋ฉ”์ธ์—์„œ๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  other-domain.com  โ†’ โŒ ๋ถˆ๊ฐ€

rp.id๋ฅผ 'app.example.com'์œผ๋กœ ์„ค์ •ํ•˜๋ฉด:
  app.example.com   โ†’ โœ…
  example.com       โ†’ โŒ ์ƒ์œ„ ๋„๋ฉ”์ธ์—์„œ๋Š” ์‚ฌ์šฉ ๋ถˆ๊ฐ€

๋‚˜์ค‘์— ์„œ๋ธŒ๋„๋ฉ”์ธ์—์„œ๋„ ์“ธ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค๋ฉด, rp.id๋Š” ๊ฐ€์žฅ ์ƒ์œ„ ๋„๋ฉ”์ธ(eTLD+1)์œผ๋กœ ์„ค์ •ํ•˜๋Š” ๊ฒŒ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

Related Origin Requests: ๋‹ค๋ฅธ ๋„๋ฉ”์ธ์—์„œ๋„ ๊ฐ™์€ Passkey ์“ฐ๊ธฐ

example.com๊ณผ example.co.kr์ฒ˜๋Ÿผ ์•„์˜ˆ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ์—์„œ ๊ฐ™์€ passkey๋ฅผ ์“ฐ๊ณ  ์‹ถ๋‹ค๋ฉด? WebAuthn Level 3์˜ Related Origin Requests๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

https://example.com/.well-known/webauthn

์ด ํŒŒ์ผ์— ๊ด€๋ จ ๋„๋ฉ”์ธ ๋ชฉ๋ก์„ ์„ ์–ธํ•˜๋ฉด, ๋ธŒ๋ผ์šฐ์ €๊ฐ€ cross-origin passkey ์‚ฌ์šฉ์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ฑ„ํƒ ํ˜„ํ™ฉ

FIDO Alliance์— ๋”ฐ๋ฅด๋ฉด 10์–ต ๋ช… ์ด์ƒ์ด passkey๋ฅผ ํ™œ์„ฑํ™”ํ–ˆ๊ณ , ์ƒ์œ„ 100๊ฐœ ์›น์‚ฌ์ดํŠธ ์ค‘ 48%๊ฐ€ passkey๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

  • Google: 8์–ต ๊ฐœ ์ด์ƒ์˜ ๊ณ„์ •์ด passkey ์‚ฌ์šฉ. Workspace์—์„œ๋„ ๊ธฐ๋ณธ ์ธ์ฆ ์ˆ˜๋‹จ์œผ๋กœ ๊ถŒ์žฅ
  • Microsoft: 2025๋…„ 5์›”๋ถ€ํ„ฐ ์‹ ๊ทœ ๊ณ„์ •์˜ ๊ธฐ๋ณธ ๋กœ๊ทธ์ธ์„ passkey๋กœ ์„ค์ •. ์ธ์ฆ ํšŸ์ˆ˜ 120% ์ฆ๊ฐ€
  • Apple: iOS 26์—์„œ CXP ๊ตฌํ˜„. iCloud Keychain ์™ธ๋ถ€๋กœ passkey ๋‚ด๋ณด๋‚ด๊ธฐ ๊ฐ€๋Šฅ
  • Amazon: ์ถœ์‹œ 1๋…„ ๋งŒ์— 1์–ต 7,500๋งŒ ๋ช…์ด passkey ์ƒ์„ฑ

๊ทœ์ œ๋„ ์›€์ง์ด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. NIST SP 800-63-4(2025๋…„ 7์›”)๋Š” AAL2(์ธ์ฆ ๋ณด์ฆ ๋ ˆ๋ฒจ 2)์—์„œ ํ”ผ์‹ฑ ๋ฐฉ์ง€ ์˜ต์…˜ ์ œ๊ณต์„ ์š”๊ตฌํ•˜๋ฉฐ, synced passkey๋ฅผ AAL2 ์ธ์ฆ๊ธฐ๋กœ ์ธ์ •ํ•ฉ๋‹ˆ๋‹ค. UAE ์ค‘์•™์€ํ–‰์€ 2026๋…„ 3์›”๊นŒ์ง€ SMS/์ด๋ฉ”์ผ OTP ์ œ๊ฑฐ ์ง€์นจ์„ ๋‚ด๋ ธ์Šต๋‹ˆ๋‹ค.

OAuth, Cookie์™€์˜ ๊ด€๊ณ„

OAuth Deep Dive์—์„œ ๋‹ค๋ค˜๋“ฏ์ด OAuth๋Š” ์ธ๊ฐ€(๋ˆ„๊ฐ€ ์–ด๋–ค ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€) ํ”„๋กœํ† ์ฝœ์ž…๋‹ˆ๋‹ค. Passkey๋Š” ์ธ์ฆ(์ด ์‚ฌ๋žŒ์ด ๋ˆ„๊ตฌ์ธ๊ฐ€) ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค. ๋‘˜์€ ๊ฒฝ์Ÿ์ด ์•„๋‹ˆ๋ผ ๋‹ค๋ฅธ ๊ณ„์ธต์˜ ๋ฌธ์ œ๋ฅผ ํ’‰๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž โ”€โ”€(Passkey๋กœ ์ธ์ฆ)โ”€โ”€โ†’ IdP (Google, Apple ๋“ฑ)
                                     โ”‚
                              OAuth ํ† ํฐ ๋ฐœ๊ธ‰ (์ธ๊ฐ€)
                                     โ”‚
                              Client ์•ฑ์ด API ์ ‘๊ทผ

Google ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด Google IdP์—์„œ ๋ณธ์ธ ํ™•์ธ์„ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋‹จ๊ณ„์—์„œ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋Œ€์‹  passkey๋ฅผ ์“ฐ๋Š” ๊ฒ๋‹ˆ๋‹ค. ๊ทธ ๋’ค์˜ OAuth Authorization Code ํ๋ฆ„์€ ๋ฐ”๋€Œ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Cookie Deep Dive์—์„œ ๋‹ค๋ค˜๋˜ CSRF, XSS๋Š” ๋กœ๊ทธ์ธ ์ดํ›„ ์„ธ์…˜์„ ๋ณดํ˜ธํ•˜๋Š” ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. Passkey๋Š” ๋กœ๊ทธ์ธ ์ž์ฒด๋ฅผ ๋ณดํ˜ธํ•ฉ๋‹ˆ๋‹ค. Passkey๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ๋กœ๊ทธ์ธํ•œ ๋’ค์—๋„, ์„ธ์…˜ ์ฟ ํ‚ค์˜ ๋ณด์•ˆ(HttpOnly, SameSite ๋“ฑ)์€ ๋ณ„๋„๋กœ ์‹ ๊ฒฝ ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ