[WWDC2018]セキュリティ強度の高いパスワードとセキュリティコードの自動入力

[Automatic Strong Passwords and Security Code AutoFill](https://developer.apple.com/videos/play/wwdc2018/204/)のざっくりとした内容メモです。内容、画像は全てセッション動画から持ってきています。

# パスワードの自動入力

## 今までのパスワード管理と入力

今までパスワード管理はユーザにとって悩みのタネだった。セキュリティ強度が高いパスワードを各サービスごとに用意する必要があるが、それはパスワードマネージャーを使ったとしても面倒なことだった。そこで今回、パスワードで憂慮することなく、簡単に、安全に管理できるようにした。

現在、多くのユーザはiCloud Keychainを使っている。Apple自身もKeychainにはアクセスできないため、Keychainでの管理は生体認証の次に安全で簡単な方法である。これがiOS 12でもっと簡単になる。

## iOS 12からのパスワード管理と入力

新規アカウント作成画面でメール欄にフォーカスするとユーザ名がサジェストされる。サジェストは今までKeychainに保存されていた情報を元にされる。

<img src="https://qiita-image-store.s3.amazonaws.com/0/100736/97794ed3-72d3-eb1f-e689-e8ceac7370d1.png" width="600px">

次にパスワード欄にフォーカスするとセキュリティ強度の高いパスワード候補が提示される。もちろん今までのKeychain同様、他のデバイスでもパスワードを引き継いで使うことができる。たった数回のタップでサインアップすることができる。

<img src="https://qiita-image-store.s3.amazonaws.com/0/100736/9622281c-a2f0-28c6-a552-133df7c8dd07.png" width="600px">

再度このアプリにログインする場合はこのようにサービスごとにIDとパスワードのセット候補が表示されます。これをタップすると生体認証が起動し、生体認証に成功すると自動でID、パスワード欄が入力されます。

<img src="https://qiita-image-store.s3.amazonaws.com/0/100736/c0f69af9-e0f7-f22f-1d6f-14625f6ec647.png" width="600px">

2ファクター認証がかかっている場合は、テキストメッセージを受信した時に認証コードがキーボード上部に表示され、これをタップするだけで認証コードの入力ができる。

<img src="https://qiita-image-store.s3.amazonaws.com/0/100736/b8eaee07-8042-9662-ca92-16b7149c4043.png" width="600px">

## 実現方法

このパスワードの自動入力技術はwebベースで作られている。QuickType bar（キーボードの上の、候補が表示される欄）に認証情報を表示するには、この入力欄が何のサービスのものなのかを判断できるよう、アプリとドメインを対応させる必要がある。関連付けはいたって簡単で、アプリのentitlementsファイルとサーバ側の認証情報ファイルを少し変えるだけでよい。これについてはWWDC 2017の[Introducing Password AutoFill for Apps](https://developer.apple.com/videos/play/wwdc2017/206/)で紹介しているのでこれも見ると良い。

ネイティブ側でやることとしてはContent typesの設定がある。適切なFieldに適切なtypeを指定する。

```swift
let userTextField = UITextField()
userTextField.textContentType = .username

let passwordTextField = UITextField()
passwordTextField.textContentType = .password
```

WKWebviewもAutoFillをサポートしている。パスワードマネージャーに関する具体的な実装方法は[Implementing AutoFill Credential Provider Extensions](https://developer.apple.com/videos/play/wwdc2018/721)を参照すると良い。

新しいアカウントでログインするとその認証情報を保存するかどうか聞かれる。ここで保存すると他のデバイス、SafariでもAutoFillの恩恵を受けることができる。

<img src="https://qiita-image-store.s3.amazonaws.com/0/100736/299a7ff3-d165-a0b0-580c-e9c6bab704f9.png" width="600px">

## パスワード保存のフロー

1. ログインシナリオを推測
2. 関連するドメインを元に適格性をチェック
3. ユーザ名とパスワードを見つけ出す
4. サインインアクションを検知
5. パスワードを保存or更新するかどうかを聞く

## 適合性チェック

パスワード保存周りに関してはiOS 12で目新しいものはないが、適合性を確かめたいなら以下のことを確認する。

* Fieldに適切なcontent typeを付与していること
* ログインフォームをview階層から除外していること
* view階層から除外した後にログインフォームをクリアすること
* 正しいドメインに保存していること
* SecAddSharedWebCredential()はもういらなくなった

## 重要ポイント

* アプリとドメインを関連付ける
* Fieldに適切なタグを付ける
* ログインしたということを検知する

# セキュリティ強度の高いパスワードの自動生成

サインアッププロセスを簡単に、安全に、便利にした。ユーザはもう面倒なことは考えず、数回タップすればよい状態になる。

## セキュリティ強度の高いパスワードの自動生成フロー

1. ViewControllerのタイプを推測
2. 関連するドメインを元に適格性をチェック
3. 対応するログインフォーム要素を検知
4. ユーザ名フィールドにフォーカスしたらユーザ名をサジェスト
5. パスワードフィールドにフォーカスしたらセキュリティ強度の高いパスワードが自動入力される

## 適合性チェック

※AutoFillと項目は同じ

コードに関しては、ユーザ名のFieldはAutoFillの時と同じだが、パスワードの方はiOS 12で新しく登場した`newPassword`を指定する必要がある。

```swift
let newPasswordTextField = UITextField()
newPasswordTextField.textContentType = .newPassword

let confirmPasswordTextField = UITextField()
confirmPasswordTextField.textContentType = .newPassword
```

ちゃんと適切なFieldを検知するために、UITableViewで認証画面を作る場合は1つのセルに置くUITextFieldは1つにする必要がある。

## 心に留めておくこと

* `.username`と`.newPassword`のフォームは同じ画面に配置する
* ユーザ名フォームはread-onlyにできる
* パスワード変更フォームにも同じように適用する

## パスワードフォーマット

* 20文字
* 大文字、数字、ハイフン、小文字を含む
* エントロピーが71ビット以上
* ほとんどのサービスと互換性がある

セキュリティ強度の高いパスワードの自動生成は、カスタムルールを作って自分で定義することもできる。

```swift
let newPasswordTextField = UITextField()
…
let rulesDescriptor = "allowed: upper, lower, digit; required: [$];"
newPasswordTextField.passwordRules = UITextInputPasswordRules(descriptor: rulesDescriptor)
```

[Password Rules Validation Tool](https://developer.apple.com/password-rules/)というのがあるので使ってみると良い。

## デモ

EmailのTextFieldは`Content Type = Username`、`Keyboard Type = E-mail Address`を、PasswordのTextFieldは`Content Type = New Password`、`Secure Text Entry = true`を指定してデモを見せる。多くの場合はここまでの設定だけで良い。ただ今回のサービスではパスワードに`$`を含めることを必須としており、バリデーションエラーになってしまった。この場合パスワード生成ルールに手を加える必要があるのでPassword Rules Validation Toolページに飛び、`required`に'$'を指定する。成形されたものはUIKit用、HTML用が出力される。今回はUIKit用のテキストをコピーし、Storyboard - Password の TextField - Password Rulesの欄にペーストした。

## 重要ポイント

* アプリとドメインを関連付ける
* Fieldに適切なタグを付ける
* ログインしたということを検知する
* （必要があれば）独自のパスワードルールを作る

# セキュリティコードの自動入力

2ファクタ認証などの、テキストベースのセキュリティコードの自動入力を可能にするためには以下のように`.oneTimeCode`を使用することと、システムキーボードを使うことが求められる。

```swift
let securityCodeTextField = UITextField()
securityCodeTextField.textContentType = .oneTimeCode
```

セキュリティコードを含んだメッセージは、サポートされている全ての言語で利用することができる。

この機能はiOSだけでなくmacOSでも使うことができるし、ネイティブだけでなくSafariでも使うことができる。加えて、例えばMacのSafari上でセキュリティコードが必要になった時に、iPhoneで受け取ったセキュリティコードを自動入力することもできる。

<img width="600" alt="スクリーンショット 2018-06-10 14.24.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/100736/5eab7bf9-74a6-b35a-f254-5fac1d1a01a3.png">

AutoFillをSafariで使う場合は以下の表に従って設定をする。

<img width="600" alt="スクリーンショット 2018-06-10 14.36.41.png" src="https://qiita-image-store.s3.amazonaws.com/0/100736/e4813c21-323e-ca0a-bff3-50ca8c83b842.png">

# Federated認証

今までFederated認証を使おうとした場合はこのような表示が出ていました。

<img width="600" alt="スクリーンショット 2018-06-10 15.03.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/100736/5dda3037-fbca-cb05-8d9f-0bd68b3432cc.png">

それがボタンを1回タップするだけでスムーズにSafariとCookie共有ができるようになる。ASWebAuthenticationSessionを使うメリットとしては以下の通り。

* ログインフローが早くなる
  * サードパーティだったとしても特に気にすることなく使うことができる
* パスワード自動入力、セキュリティコードの自動入力に対応している
* 簡単なブロックベースのAPIになっている

## 使い方

```swift
import AuthenticationServices
guard let oauthURL = URL(string: "https://www.example.com/oauth/...") else {
    return
}
self.authenticationSession = ASWebAuthenticationSession(url: oauthURL, callbackURLScheme: nil) { (callbackURL, error) in
    guard error == nil, let callbackURL = callbackURL else {
        // Process error.
        return
    }
    // Process token.
}
self.authenticationSession.start()
```

# 新しいパスワード管理機能

iCloud keychainで他のデバイスでも認証情報を共有することができる。Siriに「○○サイトのパスワードを教えて」と言うと入力してくれたりもする。

iOS 12では設定アプリのパスワードページのデザインが変わり、パスワードのコピーなどができるようになっており、パスワードをAirDropで送ることもできる。これはmacOSでも同様である。

<img width="600" alt="スクリーンショット 2018-06-10 16.14.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/100736/e6032c4e-b519-5cd5-a837-9d124f0b711e.png">

もう一つ大きな変更がある。もし複数のwebサイトで同じパスワードを使っていた場合、同じパスワードを設定しているwebサイトを教えてくれ、さらにこの画面上でパスワードの変更ができる。

<img width="600" alt="スクリーンショット 2018-06-10 16.27.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/100736/f0acd008-79eb-5e39-e04b-591afb457d24.png">

tvOSでもAutoFillの恩恵を受けることができる。詳しくは[What's New in tvOS 12](https://developer.apple.com/videos/play/wwdc2018/208/)を参照すると良い。

# まとめ

* AutoFillはパワフル
* この機能は自動で動く
* 使えるかどうか確認するためにテストをするように


# おまけ：関連ドキュメント

* [Enabling Password AutoFill on a Text Input View](https://developer.apple.com/documentation/security/password_autofill/enabling_password_autofill_on_a_text_input_view)
* [Password AutoFill](https://developer.apple.com/documentation/security/password_autofill)
* [ASWebAuthenticationSession](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession?language=objc)