macOS の標準機能でパスワードをランダム生成する方法

## TL; DR <sub><sup><sub><sup>（今北産業）</sup></sub></sup></sub>

1. `pwgen` なんかないのよ。
1. 「パスワードアシスタント」を使う。
1. 標準インストールされたコマンドやプログラム言語のワンライナーで生成する。

## TS; DR <sub><sup>「`pwgen` ねぇ。`sw-pwgen` ねぇ。 `Homebrew` なんか見たこたねぇ」な時のサバイバル術</sup></sub>

お客様先で急<ruby>遽<rt>きょ</rt></ruby>ランダムなパスワードを目の前で作成する必要に迫られました。

オンラインのパスワード・ジェネレーターなどもってのほか、思いついた適当な文字列や数字でも自他共にパターン（自分のクセ）が現れる懸念があります。

`brew`（<code><a href="https://ja.wikipedia.org/wiki/Homebrew_(%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E7%AE%A1%E7%90%86%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0" target="_blank">Homebrew</a></code>）が使えるなら、[pwgen](https://formulae.brew.sh/formula/pwgen) や [sf-pwgen](https://formulae.brew.sh/formula/sf-pwgen) などをインストールして便利なパスワード・ジェネレーター（パスワード生成）コマンドが利用できます。

しかし、いずれも **macOS には標準でインストールされていません**。一時的な利便性のために、お客様のマシンに不要なツールをインストールすることは、余計なトラブルを招くことになるので避けたいと考えています。

そのような、`brew` 含め余計なアプリもインストールできない場合の「**OS 標準の機能でランダム生成する」いくつかのサバイバル術**です。

他にも「こんなサバイバル術あるよ！」という方は[編集リクエスト](https://qiita.com/KEINOS/items/fd49c4ece0890fb5843f/edit)で遠慮なく追記ください。

### 「キーチェーンアクセス」から「パスワードアシスタント」をそれらしく起動して生成する

<img width="100%" alt="2018-03-02_2.58.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/78776/671cd9a7-b45a-0e0a-22cf-a87629a2f381.png">


1. **「Spotlight」を開く。**<br>タスクバー/ステータスバーの右端にある:mag:もしくは「control+スペース」 or 「command+スペース」で `Spotlight` を開きます。
2. **「キーチェーンアクセス」を開く。**<br>"パスワード" もしくは "keychain access" と カチャカチャカチャ…ッターン すると「キーチェーンアクセス.app」が起動します。<br>（入力途中からアプリ名が表示されるので、選択してアプリを開いてください。）
3. **「パスワードアシスタント」を起動する。**<br>「キーチェーンアクセス」ウィンドウの下部にある「＋」ボタンを押して「パスワード」欄にある「:key:」（鍵ボタン）を押します。
4. **パスワードを生成する。**<br>「パスワードアシスタント」が起動したら、「品質」（パスワード強度）を見ながら生成します。
5. **アプリを終了する。**<br>良い感じのパスワードが生成できたら、パスワードを控えてアプリを終了します。<br>（この時、後述する**キーチェーンアクセスに追加・保存しておくと便利**です）

#### キーチェーンアクセスを使うメリット

1. **安心感**
    お客様が見ている前で作成する際に「アップルのパスワード管理システムを使って作成します」と安心感を与えることができます。[^1]
2. **バックアップ**
    「パスワードアシスタント」で作成したパスワードを「キーチェーンアクセス」内に任意の「キーチェーン項目名」を付けて保存しておくことができます。
    これは<strong><font color="darkorange">「あの時のパスワードって何だっけ？」と後から聞かれた際のバックアップ</font>として、とても有効</strong>です。特に、OS の設定で AppleID の iCloud 項目にある「キーチェーン」にチェックが入っていれば、**iCloud バックアップの同期対象となる**ので OS の再インストールでも安心。
3. **セキュリティ**
    上記 2 で保存したパスワードは、同じログインユーザー（キーチェーンを開けるユーザー）のパスワードを入れないと見れないので安心感を与えられます。

[^1]: スケープゴートのつもりはないのですが、Google アカウントしかり「アップル（もしくは Google）のセキュリティが破られるレベルの攻撃を受けたら私でも歯が立ちません」といった言葉を添えるようにしています。だって、事実そうだもの。トドメは「これで破られた場合は、マスターパスワードの使い回しか、社内からの漏洩ですね」というのが私のおきまりのルーチンです。だって、事実そうだもの。

#### デメリット

- 途中マウス操作が必要なため、終始カチャターンができないので、いささか格好悪い。

#### キーチェーンアクセスに任意のメモやパスワードを保存する @ iCloud

<font color="darkorange">**キーチェーンアクセスには、任意の値を保存することができます**</font>。パスワードや PIN コード関連はもちろんのこと、API などのアクセス用に発行したアクセストークンなど、どこかにメモしておかないといけないが、「メモ」アプリでは不安な場合などにも活用できます。

1. キーチェーンアクセスを開く
2. 左ペイン（左側の項目）の「キーチェーン」にある「iCloud」を選ぶ。（これで iCloud に保存されます）
3. 保存したいデータの種類を選ぶ。（「秘密メモ」or「パスワード」など）
4. データを入力する
    - <strong><font color=green>「秘密メモ」の場合</font></strong>
        1. 「キーチェーン項目名」欄に後で用途は思い出せるも具体的な内容は推測できない名前を付ける
        2. 「メモ」欄に秘密のメモの内容を書いて「保存」する
    - <strong><font color=green>「パスワード」の場合</font></strong>
        1. 「キーチェーン項目名」欄に後で用途を思い出せる名前を付ける
        2. 「アカウント名」欄にパスワードと紐付けるアカウント名を入れる
            - Web ブラウザやアプリなどにフォーカスが当たっていた場合、OS が「これはアカウント名の項目だ」「これはパスワード欄の項目だ」と検知できた場合に、この項目の内容が参照されます
        3. 「パスワード欄」に該当するパスワードを入力、もしくは生成して「保存」する
        4. キーチェーン項目名に補足したいメモがある場合
            - 保存された項目を開いて属性の「メモ」欄に追記し「変更内容を保存」します。パスワードの用途などが良いでしょう。「最初に飼ったペットの名前」など、**パスワードの内容そのものが推測できるものは、ここには記載しない**ようにしましょう
        5. 特定の URL や URI のみで自動挿入させたい場合（1Password 的な使い方）
            -  保存された項目を開いて属性の「場所」欄に URL を記載します。（必ず反応するわけではありません）
5. 保存した秘密メモやパスワードを確認する
    - 追加した項目をダブル・クリックで開き「パスワードを表示」をクリックすると、現在 OS にログインしているユーザーのパスワードを聞かれ、パスワードの内容が確認できます

#### 余談

残念ながら**コマンドラインから直接「パスワードアシスタント」を起動する方法がわかりません**でし。せいぜい `$ open -a "Keychain Access"` で「キーチェーンアクセス.app」を起動させるくらいでしょうか。意味ないですけど。

ちなみに「キーチェーンアクセス」に保存されているデータは `security` コマンドで確認は出来ます（要ログイン・パスワード）。また、SSH 接続時のパスワードや Web サイトなどで自動入力されるパスワードなどもキーチェーンアクセスで確認できます。

### コマンドラインからパスワードを作成するワンライナー

macOS の標準で使えるコマンドやプログラム言語でターミナルからパスワードを作成する方法です。他にも方法がありましたら、コメントいただけると嬉しいです。

#### /dev/urandom のランダム発生器を使う <sup><font color=red>New</font></sup>

`head -c` で長さを調整でき、たいていの Linux でも使える。Docker の Alpine でも使えた。

```bash
$ base64 /dev/urandom | tr -d -c '[:alnum:]' | head -c 32
fYP59YWxLq0KiAWMp44UqPoUaRijlBO2
```

- 詳しい使い方: [パスワードの簡単な生成方法](https://qiita.com/ktateish/items/c415f82ab52ab51a3e28#_reference-ce443940921bd290ce26) @ Qiita

#### openssl コマンドを使う

以下はいずれも16桁のパスワードです。（正確にはランダム値のハッシュなんですが）

```bash:8桁の数値（0-9a-f）で撃ちやすいが強度が弱いパスワード
$ openssl rand -hex 8
163ff7872a9b495e
```

```bash:ランダムな12バイトのバイナリデータをBase64にエンコードして作成するランダムパスワード
$ openssl rand -base64 12
6kRz+U9TCFXs5Us=
```

<details><summary>🐒 &nbsp; OpenSSL の rand で Base64 の指定バイト数と出力桁数（のバイト数）が合わない理由</summary><div><br>

上記 `-base64` で 12 と指定しているのに 16 桁になるのは、Base64 に変換するとバイト長が 4/3 倍になるからです。

> `12バイト * 4 / 3　=　16桁`

逆に言えば、欲しい桁数を 3/4 倍すると必要なバイト数が出て来ます。

> `16桁 * 3 / 4　=　12バイト`

```bash:20桁のランダムなパスワードが欲しい場合
$ # 20 桁 * 3 / 4 = 15 バイト
$ openssl rand -base64 15
Gz930m5pUxD+DWfZ/EFW
```

しかし、**整数に割り切れない場合は注意が必要**です。バイト長を小数で指定しても、小数点以下は切り捨てられてしまうからです。例えば 10 桁欲しい場合はバイト長は 7.5 になります。 `10 * 3 / 4 = 7.5`

```shell-session:以下は同じ長さになる=小数以下は切り捨てられている
$ openssl rand -base64 7.5
xPIuDOt87w==
$ openssl rand -base64 7
fkLTZPHLfA==
```

そのため、欲しい桁数が微妙な場合は長めに出力して欲しい桁数ぶんにカットする必要があります。

```shell-session:長めに出力し、あたまの10文字だけ出力する
$ # 10 桁の次に出てくる整数に割り切れる値は 12 桁なので 12 * 3 / 4 = 9
$ openssl rand -base64 9 | head -c 10
kytm+5VNM3$ # この場合、改行が付かないので注意
```

---

</div></details>

#### PHP の OpenSSL を使って生成する

以下の `$n` の値を出力したい桁数に置き換えて実行する。

```php
echo substr(base_convert(bin2hex(openssl_random_pseudo_bytes($n)),16,36),0,$n) . PHP_EOL;
```

例：16桁のパスワード

```bash
$ php -r "echo substr(base_convert(bin2hex(openssl_random_pseudo_bytes(16)),16,36),0,16) . PHP_EOL;"
1j131w7idxgks4gw
```

## 参考文献

- 「[ランダムなパスワードを1行で生成する with openssl](https://qiita.com/ngyuki/items/dd947aae213327cbeb70)」@ Qiita
- [パスワードの簡単な生成方法](https://qiita.com/ktateish/items/c415f82ab52ab51a3e28#_reference-ce443940921bd290ce26) @ Qiita
- [entropy of /dev/random vs. openssl rand](https://linux.kernel.narkive.com/N58MnPPP/entropy-of-dev-random-vs-openssl-rand) @ linux.kernel.narkive.com