iOSやAndroidで入力しやすい記号だけを含むようにランダムパスワードを作る

# パスワードにはランダム文字列を使いたいけど、スマートフォンでは記号は案外入力しづらい…

いまどきのエンジニアの方は何らかの形でパスワード管理ソフトウェアをお使いのケースが増えていることと思います。たとえば、1Passwordとか、KeePass などでしょうか。あるいは iCloud キーチェーンもありますね。この手のパスワードマネージャーを使えばランダム文字列のパスワードを生成、管理できますから、利用するサービスごとに十分な強度の文字列長でランダムパスワードが用いられていることでしょう。あるいは、パスワードの生成機能はありませんけど、Google Chrome のパスワードマネージャーも案外便利ですね。

そしてパスワードマネージャーを使えば、パスワードを手入力するのではなく、コピー＆ペーストで済ませるか、または自動入力を行うケースが多いと思います。

しかしながら、なんらかの理由で手入力しなければならない場合もあります。そういうときにスマートフォンやタブレットのオンスクリーンキーボードで行うのは案外苦痛です。この最たる例は、スマートフォンやタブレットの初期セットアップ作業です。初期セットアップ作業ではパスワードマネージャーからのコピー＆ペーストが行いづらく手打ちせねばならないのですが、スマートフォンやタブレットで入力しづらい文字がパスワードに含まれていると大変です。

というのも、オンスクリーンキーボードは記号入力に制限があるわけですが、パスワードマネージャーやパスワードジェネレータで記号を含むパスワードを生成させると、デフォルト設定ではスマートフォンでの入力のしやすさなど考慮せずにランダムな記号を突っ込んでくれるからです。

たとえば以下の内容は、macOS 上に brew でインストールした pwgen を用いてアルファベット、数字、記号を用いた文字列を生成させる例です。しかしここで生成した文字列には、スマートフォンで入力しづらい文字がたくさん含まれています。

```
$ pwgen -sy
k_B6~|Al *YQ4WkQ2 ckJt?h0" 8J(sc*DN D/@)eyt6 0X2hXY^m 7FD6c"GA /M9)pwcJ
i+L<8'G_ [{Od2j-% )pGXwH^8 yMC(8#F@ tRRe2zj~ )+2'}L[X Ev+r_c5{ WX]0aykE
pQMb#@3b WZr35e&l 6VJN2#9E }fDEi3+Q <j3kILH\ 4'8*!T%% M/P5D6Y_ x4Zci3b^
YaU"i7;B W^YiDH5g yBi1&!mk @t]3"&&B y\)9VyR/ #q0YI#Rm '$<u1'&F I;qT2mX5
/h+!pNQ0 1e[?"J%4 L9(N.t7U Y|Piv7i* }N^6FCJi ~W,2O\7( E@ez2Ic2 5htJ)jV3
zGl8&=DE g:aG%-D6 ;;B6CCEU )R(^8*,\ FJysI5,r >^qAyt8v @<%2]Jh+ nT"E^!*2
45[Ks,A{ V<~7,'{9 t3VXBT*q Fn&;W]t7 [`+kitU9 Hkj0[J1- LNg9u!D: nw5kk+MX
jCn)8iG* /:]fU1/y 9.75Zdx+ s$H>=1'r Lx~~]n1> g4@G/2Yd F[+gY1W, kbV50T$a
S@tg6e0= nk%G>O~1 $e0YlnTb l=>Mk.\1 Jc.)&3/a u9c-Zsn@ Ojey0]1$ n^1#s1kR
Zp={5-x\ ^0p?m|,X ~0JyS~-2 e4KK)Sd+ ue%S9i[' %5n,wTb$ nD(A>'G7 .w2==~rO
@9ll}TSj $Yk@6v$? 3&/z,N+C _0/a7NCI <1<jp1V- Y.nq6lAz e"B6y~z1 jv%3sZqZ
6V^'x`mL }eC*g\.6 Sw0LA;~2 ]f]1t:B{ g%7}E](5 /"4pH?M< !;C3Z;mZ jIi+Pza6
DX$01}ZR Z7Eyz|s? gF8CzR|[ e&G{i,@0 x%1Kjqm. 8.e|H6q< O4n1bJD( yjz@78%L
'9=_jEj4 t5h.Yf-E rO,4'G%1 84(sT!D; 2^]9)BQ( ,1\kiX$c JY(S.9:< ']#Ov74[
mX_4r"t/ F0?R5VLp mCE^eP<1 4-|iM[T* ~+EEHP3T t@VE5%!^ b0)k1ZX{ {qhPI,`3
;8\T0g[e X5{yk4-C FLV3?v|a +B8ey,/, 2(Q}Uq~W yp^CY{3S lq@QsRv5 ,tp3tF8;
:{keZy7e UJ2SU;Gu pN06_F:. StXo:^9+ 0Zbdko%S i>4E6?ZY |NNs2P62 7`EMHX1Q
x-ZP]o1{ 7q$F8qO# 6XX}<vIe 2O42iW1> D'8!:oMI X96U?Xk: ;78N@*xH J-Ei312T
{-7<A&st kh;5-i*L tDiT"6!= p)4D@d-S r42*'RtF Hi*:Vvf1 B+U?IPr9 #qWIo8pS
2(XE`?a4 Tu16oM;- 6tpB\W6# (w"}-3L! %O=iF6"] 0YNH`5<) B4069+I+ cd'%S8V+
```

わかりやすくするために、上記文字列の中でスマートフォンで入力しづらい文字に着色してみると、こんな結果になります。これはほとんど全滅やんけ。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/c7de3141-b8bb-49cc-9bfa-4f1843dd1920.png)

なお、上記の着色には、[指定したキーワードに着色してログなどを見やすくするフィルタ処理をperlで書く](http://qiita.com/kazinoue/items/bb23971fc675d5e47c1c) で紹介したスクリプトを用いています。

こういった文字列をパスワードに使うとしんどいので、パスワードの強度をできるだけ損なわずに、でも入力が面倒ではないランダム文字列をパスワードに使いたい、というのがこの記事の趣旨です。

# 入力しにくい文字とは何か？

ここでいう入力しづらい文字とは「スマートフォンやタブレットでその文字を入力するために、キーボードの切り替え作業が２回以上必要な文字」のことを指すことにします。

たとえば、以下のスクリーンショットは入力しにくい文字の例です。iOSでこのキーボードを表示するにはフルキーボードを選んだ上で数字記号に切り替えて、さらにシフトキーを押す、的な作業になるので２回の操作が必要です。ここに出ている記号とアルファベットを交互に入力してみれば、いかにめんどくさい作業であるかを実感できます。

<img width="375" alt="IMG_0043.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/149350/1bc8f81a-d8a9-d216-b1aa-74bd4316ef18.png">


入力しやすいのはこの状態。これはフルキーボードから数値記号の入力に切り替えただけですね。

<img width="375" alt="IMG_0042.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/149350/bce6d43c-42f5-5642-859f-3f94b2b40d79.png">

ちなみに Android7 Nexus 5X のフルキーボードだとこんな感じ。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/8288071e-bd3e-4ebb-b1d0-e0bbdbb1c52e.png)

Android 6 Nexus 5 だとこうです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/b9cefc9a-3770-aef1-0578-d6e42d623871.png)

そこで、スマートフォンやタブレットで入力しやすい記号を含むランダム文字列を生成するパスワードジェネレータ処理を書いてみることにします。

なお、フリック入力のほうが入力できる記号数が案外多いようですので、フリック入力とフルキーボードに共通する文字で、なおかつ iOS と Android の両方を対象に文字列を絞り込みます。スクリーンショットは提示しませんけど、お手元のスマートフォンでフリック入力とフルキーボードで入力できる記号の違いを確認してみるとよいでしょう。

# スマートフォンで入力しやすい記号とは？

iOS, Android の標準キーボードで入力しやすいと考えられる文字は以下のとおりです。

```
:!?'"()@ 
```

この文字列は以下の4種類のキーボードで面倒な操作無しに入力可能な文字を絞り込んでいます。

- iOS フルキーボード
- iOS フリック入力
- Android フルキーボード
- Android フリック入力

具体的な絞り込みには uniq コマンドを使いました。素材として、これらのキーボードで入力した文字を、1文字1行で記入したテキストファイルを用意します。それを ```sort |  uniq -c``` すれば出現回数をカウントできます。あとは4回出現した文字だけを抽出すれば目的の文字集合が手に入ります。

なお、今回はOS標準のキーボードだけを対象に文字列を絞り込んでいることにご注意ください。その他のソフトウェアキーボードは入力可能な文字の仕様が異なると思うのですが、それを調べるだけのモチベーションは無いのです。

# iOSやAndroidで入力しやすい文字を使ったランダムパスワードを生成してみよう

記号に使用できる文字集合が決まったら、実際の実装を行ってみます。この記事では以下の３つを行ってみます。

- シェルスクリプトによる実装
- KeePass をカスタマイズする
- DelphiでiOS/Andoridアプリとして実装してみる

## シェルスクリプトによる実装

使用する記号の制限を含めて、以下のような仕様で書いてみることにします。

- 8文字のランダム文字列を生成する
- 20件表示する
- 記号はiOSとAndroidで入力しやすい文字だけを使用
- 似た文字 (0O1lI|) は除外する

この手の処理はシェル上で cat /dev/urandom して　tr, fold, head の組み合わせで絞り込むのが定番の方法ですので、その方法に手を入れて以下のように実装してみました。

なお、似た文字の除外については、ここに挙げた以外でも qg9 があります。あるいは音声読み上げのときに聞き間違いや伝わりづらい文字も場合によっては除外の候補になり得ます。しかしここではそこまで踏み込んだ除外設定は作っていません。

```bash:mkpasswd.sh
#!/bin/sh

LENGTH=8
NUM=20

# macOS で tr がエラーを吐かないためのおまじない
export LC_CTYPE=C

# 入力が面倒ではない記号：フルキーボード、フリック共通
SYMBOL=":!?'\"()@"

# 似た文字を出力しないための設定
SIMILAR_CHARS="0O1lI|" 

# アルファベット、記号、文字を基本的な母集団とする
ALNUM="[:alnum:]"

cat /dev/urandom | 
	tr -dc "${ALNUM}${SYMBOL}" | 
	tr -d  "${SIMILAR_CHARS}" |
	fold -w ${LENGTH} | head -${NUM}
```

このスクリプトは macOS Sierra と Ubuntu 16.04LTS の bash で動作確認しています。Windows10 の Bash on Windows や Cygwin でも当然動く筈です。

実行するとこんな感じになります。LENGTH や NUM は必要に応じて調整してご利用いただけます。

```
$ ./mkpasswd.sh
v@"KCTpA
!doTDwT8
BUJoHwop
Y"y3w'dc
RucEV(o)
RfzZRK5U
"tCu(Sxb
2Fj4A7Qo
9dGB6UFw
mLAf4Vnk
rd"?'LFQ
r8(pWWiY
@?PkWK@i
7oevP8(c
t?UxGtrW
pXhbb9jM
X@:':bbs
DAtb:wrX
bNTRdW'G
tS64p!La
```

## KeePass をカスタマイズする

KeePassではパスワード生成時のプロファイルを複数登録できます。そこで、大文字、小文字、数字に加えて :!?'"()@ を使用するプロファイルを作成して保存しておくと便利です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/7aab41c9-5a0c-7d1e-2745-05596ef11c87.png)

このときに、よく似た文字の除外も指定したプロファイルを作成しておくと、今回のシェルスクリプトの実装と同等のルールでランダムパスワードが生成できます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/dc782937-5bd7-f18b-0ec5-ab66e798a6a6.png)

## DelphiでiOS/Andoridアプリとして実装してみる

ちょっと長くなってきたので、これは別記事にしました。

[スマートフォンで入力しやすい記号だけを含むランダムパスワードを生成するアプリをDelphiで作ってiOSやAndroidで動かす](https://qiita.com/kazinoue/items/12bca48b33ffa309b845)


# まとめ
入力しづらいパスワード文字列はランダム文字列での運用を阻害する要因になりえます。使用する文字列を制限する形でランダムパスワードを生成するのは、自分的には十分アリと考えています。なお、使用する文字を絞ることはパスワードの強度を下げることになりますが、それでも強度の弱いパスワードが用いられるよりは、ランダム文字列のほうが十分な強度が得られることでしょう。