コンテンツへスキップ

パスワードレスの時代が来る!Web authentication APIの仕組みについて

by : 2018/05/23

Web authentication API(WebAuthnとも呼ばれます)がついにFirefox 60で対応し、本格的な普及に向けて進み始めました。Google ChromeやEdgeでも次期バージョンから対応が予定されています。残るはSafariですが、こちらは未定となっています。ただ、MacBook Proには指紋認証も付いており、iPhoneでも指紋や顔認証が付いていることを考えると将来的にはWeb authentication API互換で使えるようになることでしょう。

今回はWeb authentication APIの仕組みについて紹介します。

肝は登録と認証

パスワードレス認証を可能にするためには、二つのイベントに対応する必要があります。一つはデバイスの登録で、これはすでにログインした状態で行われる処理です。もう一つは認証で、これはデバイスがユーザに変わって入力を行います。Yubikeyのようなデバイスがよく使われますが、これはキーボードとして認識されるデバイスです。つまりユーザに変わって何かの文字列を入力しています。

公開鍵認証

Web authentication APIは公開鍵認証を使っています。また、認証された結果自体はサーバ側で処理されます。そのため、HTML5のAPIではありますが、サーバの存在が必須という点が特徴です。

登録処理について

登録処理では navigator.credentials.create() を使います。サーバとの流れをフローで描くと次のようになります。全部で6つのステップになります。

まずサーバからチャレンジ、ユーザ情報、PR(Relying party info)の3つを送ります。チャレンジ、PR辺りはOpenID 2.0で使われるものと似ているようです。そして、Webブラウザはそれらの情報に加えてクライアントのデータをAuthenticatorと呼ばれるデバイス(Yubikeyなど)に送ります。デバイスは新しい公開鍵、証明書ID、認証情報を返します(これを認証オブジェクトと呼びます)。Webブラウザは認証オブジェクトとクライアントのデータを合わせてサーバに送ります。そしてサーバで検証して完了となります。

送信する仕組みはfetchまたはXMLHTTPRequestになるようです。

認証処理について

認証処理では navigator.credentials.get() を使います。サーバの流れをフローで書くと次のようになります。全部で6つのステップになります。

まずサーバからチャレンジが送られてきます。WebブラウザではチャレンジとRP、クライアントデータのハッシュをAuthenticatorに送ります。Authenticatorでは値の検証とアサーションの作成を行います。Webブラウザには認証データと署名が送られてきます。Webブラウザは認証データと署名、そしてクライアントデータのJSONをサーバに送ります。サーバはそれらの値を検証します。

クライアントの実装デモ

Web Authentication API – Web APIs | MDNにクライアントのコードサンプルがあります。

RPはサービス名を指定しています。また、この例ではチャレンジを生成していますが、本来はサーバから送られるランダムな文字列になります。

// sample arguments for registration
var createCredentialDefaultArgs = {
publicKey: {
// Relying Party (a.k.a. – Service):
rp: {
name: "Acme"
},
// User:
user: {
id: new Uint8Array(16),
name: "john.p.smith@example.com",
displayName: "John P. Smith"
},
pubKeyCredParams: [{
type: "public-key",
alg: 7
}],
attestation: "direct",
timeout: 60000,
challenge: new Uint8Array([ // must be a cryptographically random number sent from a server
0x8C, 0x0A, 0x26, 0xFF, 0x22, 0x91, 0xC1, 0xE9, 0xB9, 0x4E, 0x2E, 0x17, 0x1A, 0x98, 0x6A, 0x73,
0x71, 0x9D, 0x43, 0x48, 0xD5, 0xA7, 0x6A, 0x15, 0x7E, 0x38, 0x94, 0x52, 0x77, 0x97, 0x0F, 0xEF
]).buffer
}
};
// sample arguments for login
var getCredentialDefaultArgs = {
publicKey: {
timeout: 60000,
// allowCredentials: [newCredential] // see below
challenge: new Uint8Array([ // must be a cryptographically random number sent from a server
0x79, 0x50, 0x68, 0x71, 0xDA, 0xEE, 0xEE, 0xB9, 0x94, 0xC3, 0xC2, 0x15, 0x67, 0x65, 0x26, 0x22,
0xE3, 0xF3, 0xAB, 0x3B, 0x78, 0x2E, 0xD5, 0x6F, 0x81, 0x26, 0xE2, 0xA6, 0x01, 0x7D, 0x74, 0x50
]).buffer
},
};
// register / create a new credential
navigator.credentials.create(createCredentialDefaultArgs)
.then((cred) => {
console.log("NEW CREDENTIAL", cred);
// normally the credential IDs available for an account would come from a server
// but we can just copy them from above…
var idList = [{
id: cred.rawId,
transports: ["usb", "nfc", "ble"],
type: "public-key"
}];
getCredentialDefaultArgs.publicKey.allowCredentials = idList;
return navigator.credentials.get(getCredentialDefaultArgs);
})
.then((assertion) => {
console.log("ASSERTION", assertion);
})
.catch((err) => {
console.log("ERROR", err);
});

view raw
index.js
hosted with ❤ by GitHub

試してみた結果

現在、一番分かりやすいデモは https://webauthn.org/ でしょう。ユーザ名だけ指定して登録し、さらにユーザ名を指定してログインできます。パスワードの入力は一切なく、認証できます。

GoogleやFacebook、DropboxなどのサイトではU2F(Universal Second Factor)にYubikeyなどが利用できます。これは多要素認証であって、ログインIDとパスワードで認証後、アプリやSMSで認証コードを送る代わりにYubikeyなどに認証コードを入力してもらう方法になります。Web authentication APIとは異なるのでご注意ください。

まとめ

Web authentication APIが流行っていけば、パスワードが不要なものになります。その結果として、パスワードが漏洩して不正アクセスを受けることもなくなります。デバイスを紛失した場合、管理画面から無効化することで安全に対応できるでしょう。また、ユーザ名などの情報と紐付けて用いますので、ユーザ名が分からない状態ではデバイスは使えないというメリットがあります。

問題はYubikeyなどが使えないスマートフォンやタブレットでしょう。iOSの指紋認証がWeb authentication APIに準拠してくれれば良いですが、アプリの認証には使えない可能性があります(SDKがリリースされれば良いですが)。そうなるとWebブラウザで認証後、コードを生成するような仕組みが必要になりそうです。また、専用デバイスが必要という点も面倒です。Web authentication APIに準拠した仕組みをデスクトップ向けのソフトウェアとスマートフォンアプリで開発すると使いやすくなりそうです。

Web Authentication API – Web APIs | MDN

From → HTML5

コメントは受け付けていません。

%d人のブロガーが「いいね」をつけました。