
序章
Webシステム開発部門に配属されましたか?おめでとうございます。
早速ですが,ユーザー登録のフォームを作る前に,まずはこの記事の内容を頭に叩き込んでください。そして,以下の27か条に反するフォームを作らないでください。

1. 名前の入力欄は「姓」と「名」で分割してはならない
名前は必ずしも常に「姓」と「名」のみに分離できるとは限りません。
ミドルネームが存在する人もいれば,さらにそれ以上に分割された名前や,名前自体の概念が曖昧な場合もあります。
あらゆる登録フォームは,名前を「フルネーム」として入力させるべきです。

2. 名前の文字数に下限や上限を設けてはならない
名前の文字数に下限を設けたり,反対に上限を設けるべきではありません。
一文字の名前の人も存在すれば,逆に数百文字以上の名前の人も存在します。
例えば天才芸術家パブロ・ピカソの本名は「Pablo Diego José Francisco de Paula Juan Nepomuceno María de los Remedios Cipriano de la Santísima Trinidad Ruiz y Picasso」(122 bytes)です。

3. 名前はしばしば変わることがある
名前は変わることがあります。それも「人生で数回」どころではなく,数十回,数百回単位で名前が変わる人がいます。決して,変更不能な値として記録したり,あるいは一度変更してから再度変更するまでに一定期間を設けてはいけません。

4. 名前は常に特定の正規表現に収まるとは限らない
登録されるものが日本語の名前だけとは限らず,ありとあらゆる言語,それどころか記号を含むような名前が登録されることがあります。「名前はこの正規表現に収まるはずだ」という偏見は捨てて,すべてUnicodeとして保存してください。

5. メールアドレスは勝手に他人のものを入力できる
ユーザー登録フォームは,必ずしも入力している人が善意とは限りません。
例えばユーザー登録フォームにおけるメールアドレス認証機能を利用して,他の人に対する嫌がらせの踏み台としてあなたのサービスが利用される危険性があります。
ユーザーの入力したメールアドレスがかならずユーザーのものであるとは思い込まず,さらに踏み台にされないよう,認証メールの送信回数に制限を設けてください。

6. メールアドレスを収集するならプライバシーポリシーを用意すべし
もしあなたのサービスでユーザーのメールアドレスを収集するなら,かならずサイト内にプライバシーポリシーを設置して,その利用範囲と運用方法を明示してください。
また,収集したメールアドレスに対して,ユーザーの許可なしに広告メールを送付する行為は特定電子メール法に抵触します。さらに,広告メールを許可の下で送付する場合でも,「オプトアウト」のためのURLをかならずメール内に明示してください。

7. メールアドレスには捨てアドサービスが存在することを理解せよ
世の中には,捨てアドと呼ばれる一時的なメールアドレスを取得できるサービスがたくさんあります。これを利用してアカウントを量産できる可能性があるため,原則として捨てアドはドメイン単位で規制すべきです。

8. パスワードの「コピペ禁止」は厳禁
パスワード入力欄のコピー,あるいはペースト(貼り付け)を禁止してはいけません。
コピペ禁止はセキュリティに一切寄与しません。それどころか,外部ツールで生成したパスワードを入力できなかったり,逆にパスワードを外部ツールに保存しておきたいのにコピーできなかったりして不便です。
そもそもパスワードをユーザーにその場で考えさせて入力・暗記させても,一週間後にはもうパスワードを忘れてログインできなくなっているか,ユーザーがありとあらゆるサービスに同じパスワードを使いまわす羽目になるかの二択です。

9. パスワードに複雑な制約を求めるなら、JavaScriptで自動生成すべし
パスワードを作る作業は苦痛です。
特に制約が多ければ多いほど,非常に面倒で時間のかかる作業となります。
もしパスワードについて「英数字混合,〇〇文字以上,記号含む」というような複雑な制約を課す場合には,JavaScriptを利用して,その場で要件に合致し暗号学的に安全なパスワードを生成してください。
ただし後述するように,パスワード管理ツールを阻害しないように注意すること。

10. パスワードの文字数に上限を設けてはならない
パスワードの文字数に上限があるサービスは本当に意味が分かりません。もしかすると平文でデータベースに保存しているのではないかと不安になって,少なくとも私はその時点でサービスを使うのをやめます。
もちろんデータベースの都合上,パスワードを本当の意味で上限撤廃することはできませんが,少なくともハッシュ化された結果の文字長よりは大きくするようにしてください。例えばSHA-512でハッシュ化しているならば,512 bytesまでは登録できるようにすべきです。

11. パスワード管理・自動入力ツールを阻害してはならない
多くのユーザーは,パスワードをいちいち考えたり思い出すことに時間を割きたくないので,パスワードを管理したり,自動で入力するツールを利用しています。最近だと,もはや普通にブラウザに標準搭載されていますね。
パスワードを入力する要素をブラウザにきちんとパスワードの入力項目として認識させるために,以下を徹底してください。
入力用inputの属性(id,name,type)をデプロイごとに変えないようにする。
重複/ダミーの入力inputを置かない。
送信成功をブラウザに明示する。 (ページ遷移するか,history.pushState()を使用)
autocomplete属性を利用して,ブラウザに明示する。
パスワードの入力: autocomplete="current-password"
アカウント作成/変更の新パスワードや確認欄: autocomplete="new-password"
ユーザー名/メール: autocomplete="username"
SMS等のワンタイムコード: autocomplete="one-time-code"
※new-passwordを利用する場合,Safariでは「passwordrules」属性を指定して自動生成されるパスワードの形式を制御できます。例えば以下のような形式です。
```MarkDown
passwordrules="minlength: 12; required: upper; required: lower; required: digit;"
```

12. パスワードの保管はソルト付きハッシュを利用するか、クラウドに頼れ
パスワードを平文でデータベースに保存するのは,1200%絶対に止めてください。
また,ハッシュの種類にもよりますが,現在のGPUでは毎秒数億~数千億以上のハッシュ値を総当たりで計算することが可能ですので,だいたい英数字混合でも12文字未満くらいのパスワードのハッシュはブルートフォース攻撃で簡単に解読できてしまいます。
実際に手元のRTX 5090を12枚搭載したGPUクラスターでは,hashcatと呼ばれる専用のツールを用いることで,MD5を2594.5 GH/s,SHA1を845.9 GH/s,SHA2-256を340.5 GH/s程度のハッシュレートで攻撃できました。
そのため,MD5やSHA1のような単純なハッシュ関数をそのまま利用せず,代わりにパスワードに適当な文字を付ける「ソルト付与」を行った上で,何度もハッシュ化を繰り返す「ストレッチング」をしてください。
検証をする際には,同じ方式でハッシュ化を行った上で,保存しておいたハッシュ値と同じ値になることを確かめることでパスワードが正しいことを確認できます。
これは一見複雑そうに見えますが,大抵の言語に標準実装されており,例えばPHPなら`password_hash`関数でソルトを利用すれば,生成からストレッチングまで全部自動的にやってくれます。

13. パスワードで確認用の再入力を求めてはならない
パスワード入力欄に「確認用」として再入力を求めないでください。余計なお世話です。これが「コピペ禁止」と一緒に実装されていたならば,それはもう発狂モノです。
もはやユーザーは自力でパスワードを考えて,手動で入力していると考えるべきではありません。また万が一パスワードを間違えて入力した場合でも,パスワードをリセットすれば済む話です。一律でユーザーの時間を奪う実装をしないでください。

14. パスワードはそもそも半世紀前の技術であり、今はパスキーを利用すべし
そもそも根本的に,パスワードは非常に古くからある認証技術であり,時代遅れなシステムだと言わざるを得ません。
もちろん実験用途や簡易実装のための使用なら全く問題はありませんが,大企業のプロダクトのような場面にまでパスワードの概念を持ち込むのは頓珍漢です。
きちんと予算があり,真っ当なセキュリティのサービスを実装しようと思うのならば,最初からパスワードではなく「パスキー」の導入を検討してください。

15. 性別は二分できない、そもそも入力させるべきではない
性別というのはとても複雑な概念で,社会的役割,行動,機会などについて研究する「ジェンダー論」という一つの学問が台頭するほどに難しいものです。
ここで多様性の議論に深入りするつもりはありませんが,少なくとも単純な二分法で男性・女性に分離できるものでは決してありません。
そもそもユーザーの性別が分からなければサービスとして成立しないような特殊な開発要件でない限り,根本的にユーザーに性別を問うべきではありません。もし任意で収集するにしても,「男」か「女」の二つのドロップダウンで選択させずに,「その他」あるいは「自由入力」を許すべきです。

16. 郵便番号と市区町村までの住所はお互いに補完し合える存在である
郵便番号から住所の途中までは調べることができ,逆に住所から郵便番号を調べることも容易です。これらは本質的に同一の情報であるため,ユーザーに二度同じ内容を求めるべきではなく,自動的に一方を入力した時点でもう一方の内容を補完するようにすべきです。

17. 住所の入力欄を分割してはならない
住所の入力欄が「住所1」「住所2」「住所3」...のように分割されているフォームが稀によくあります。やめてください。
どこまでの記述欄にどこまでの住所を入力すれば良いのか分からないのはもちろんのこと,そもそも住所というのは正規化が非常に難しい項目であり,一定の形式にすべての住所が収まることはありません。
住所の入力欄は,複数に分割せずに一つの自由記述欄として作ってください。

18. 生年月日はドロップダウンではなくカレンダーで実装すべし
生年月日の入力欄において,「1900年以降の全ての年を一覧にしてある」ような実装を見かけます。全く考えられていないずさんな設計だと,西暦1年からの全ての年が一覧にされているものも見たことがあります。
生年月日はドロップダウンではなく,カレンダーとして実装すべきです。ただしカレンダーも日めくりのようなものではなく,かならず年を一気に跨いで移動できるものを採用すべきです。
ありがたいことにブラウザのinputタグには標準で type="date" というカレンダー入力機能が用意されていますし,カレンダーで入力させるためのJavaScriptフレームワークは数多く転がっています。

19. 電話番号を全ての人が持っているとは思うな
電話番号を全ての人が当たり前に思っていると思っているのならば,それは間違いです。世の中には電話番号を持っていない人がたくさんいます。
サービスの悪用防止のためとして平然と電話番号を求めるサービスがありますが,電話番号を所有していないユーザーのために代替の選択肢を用意すべきです。
なお,そもそも電話番号はオンラインSIMと呼ばれる一時利用ができるサービスがあるため,電話番号認証だけで完全に悪用を防ぎ切ることは不可能です。

20. 電話番号の区切りの有無や半角・全角の正規化をユーザーにやらせるな
電話番号の入力欄において,区切り文字「-」の有無の統一や数字の半角統一はすべてJavaScriptで自動化してください。決して,ユーザーにやらせないでください。普通のユーザーはそもそも「全角」や「半角」の違いが分からないことがあります。

21. 電話番号には通話専用・SMS専用が存在するので、片方に依存するな
電話番号を持っているからといって,必ずしも通話とSMSの機能の両方が使える状況にあるとは限りません。世の中には通話専用の電話番号や,逆にSMS専用の電話番号が存在します(電話番号それ自体の性質というよりは,ユーザーがキャリアと契約している内容次第ですが)。
そのため,電話番号による本人確認のプロセスなどにおいては,「通話で確認」あるいは「SMSで認証コードを送信」のどちらか一方に絞った実装はせず,どちらかからユーザーが好きな方を任意で選択できるようにすべきです。

22. 必ずしも必要のない情報を「必須」にしてはならない
ありとあらゆるサービスに言えることですが,必須の入力欄が多ければ多いほど入力は面倒で,時間もかかります。これはユーザーの離脱などにもつながる可能性があり,ユーザーのみならずサービス運営者にとってもマイナスです。
必ずしも必要のない情報をrequired属性で必須項目扱いしないでください。

23. 利用規約に同意するためのチェックボックスは大きく目立つように
利用規約に同意するボタンが小さく全然気づかず,「送信」を押したら赤字のエラーメッセージで怒られてしまった経験は誰しもあるはず。
利用規約の同意をうながすチェックボックスは大きく,目立つよう,見逃しにくい位置に実装してください。
また小さなチェックボックスはスマホ利用者にとって非常に厄介です。タッチ判定領域を大きく確保し,簡単にチェックを付けられるようにしてください。

24. データベースに何度もクエリを送るな、一つに統合して一気に送れ
サーバー側の内部実装にかんして,ユーザーの送信した情報をデータベースに保存する際には何度もSQLを発行して保存するような真似はせず,かならず一つの長文SQLにまとめて送るようにしてください。
SQLサーバーとHTTPサーバー間の通信処理は想像以上にブロッキングを生むものであり,ましてや一つ一つの情報を`update`で追加していては,アカウントを一つ追加しただけで莫大な負荷がサーバーにかかってしまいます。

25. データベースにクエリを送るときはプレースホルダを利用すべし
ユーザーの送信したデータをそのまま文字列としてSQLに連結してデータベースにクエリを発行すると,SQLインジェクションと呼ばれる脆弱性を生む危険性があります。
ユーザーの情報を登録する際には,かならず「プレースホルダ」と呼ばれる機能を利用してあらかじめSQLの構文を確定させてから,実際のデータを記録してください。

26. ユーザーが送る全ての情報は信用するな、XSS対策を徹底せよ
クライアント側から来たあらゆる内容は,すべてユーザーが任意に細工できるものです。例えば名前のなかにscriptタグが埋め込まれていたり,生年月日の欄からonclick属性つきのimgタグが送られてくるかもしれません。
ユーザーの入力したありとあらゆる内容は信用せず,常に形式チェックとHTMLエスケープを行ってXSS対策を徹底してください。

27. まずユーザー登録を求める前に外部サービスのOAuth連携を検討せよ
そもそも,本当にそのサービスにユーザー登録フォームって必要でしょうか?外部サービスのOAuth連携を利用すれば,例えば「Googleアカウントで続行」や「Xのアカウントで続行」などと別の大手サービスのアカウントを利用してユーザーにログインさせることができます。
小規模で実験レベルのサービスならば,そもそも自前でアカウント登録フォームを用意する代わりに,外部のOAuth連携を利用することを検討しましょう。

最後に
以上,合計27個($3^3$個ですね,キリが良い!)のユーザー登録フォームを作るときにまず理解すべき27か条でした。
最後までお読みいただきありがとうございます。今後ユーザー登録の機能を持ったサービスを開発する際には,ぜひこの27か条を守って,サービス利用者にとっても管理者にとっても安全で扱いやすいサービスを作ってください。
なお,本記事はMIT Licenseの下で公開されているものとしますので,教育現場から事業計画の際にまで,あらゆる場面で自由に使っていただいて構いません。改変や複製,引用,翻訳も歓迎です。それでは!

参考文献
あまり日本語の文献がありませんでしたが,本記事を執筆するにあたって参考になったページを置いておきます。
Personal names around the world
https://www.w3.org/International/questions/qa-personal-names
STG - v4.1 | OWASP Foundatio
https://owasp.org/www-project-web-security-testing-guide/v41/4-Web_Application_Security_Testing/03-Identity_Management_Testing/02-Test_User_Registration_Process
Password Storage - OWASP Cheat Sheet Series
https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
HTML Standard
https://html.spec.whatwg.org/multipage/form-control-infrastructure.html
HTML attribute: autocomplete - HTML | MDN
https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/autocomplete
また,特に「名前」の多様性については以下の記事(日本語訳ver.)が詳しいです。
プログラマの抱いている名前についての誤謬
https://emptypage.jp/translations/kalzumeus/falsehoods-programmers-believe-about-names.html