パスワードやBearer token、2FAコードに利用できるような暗号論的にもランダムな文字列を返す関数を追加するという提案です。 承認済み(Accepted)の提案であり具体的な追加時期は決まっていませんが将来的に追加される予定です。
承認された仕様は https://github.com/golang/go/issues/67057#issuecomment-2261204789 にある Russ Cox氏のコメントで確認できます。 生成される文字列に含む文字種別が RFC 4648 base32 固定になったこと以外は、ほぼ当初の提案から変わっていません。
// Text returns a cryptographically random string using the standard RFC 4648 base32 alphabet // for use when a secret string, token, password, or other text is needed. // The result contains at least 128 bits of randomness, enough to prevent brute force // guessing attacks and to make the likelihood of collisions vanishingly small. // A future version may return longer texts as needed to maintain those properties. func Text() string
この提案の議論を読んで面白いと思った点は、次のことです。
- メソッド名がTextと一般的であること
- 返されるランダムな文字列の長さが固定長(128 bit)であること
- 文字種別にRFC 4648で定義されているbase32のアルファベットが採用されたこと
1. メソッド名がTextと一般的であること
承認された仕様を見たときの感想は、Base32のアルファベットを使ったランダムな文字列を返すという実装にしては名前が一般的過ぎないかでした。
実際、Issue コメント中でも一般的すぎる名前なので、rand.Base32()や rand.Token128() のような名前のほうが良いのではという意見もありましたが、rand.Text() が良い名前として受け入れられています。
この理由について同僚と催しているProposalを読む会 1の会話で rand.Text() はランダムな文字列を生成することが役割なのであって文字種別は本質的には関係がないからだろうという推測をしていました。
もし、将来的にコンピュータの計算性能が向上するなどして現在の仕様よりもエントロピーを増やさないと暗号論的に安全でないとなった場合に、文字長や文字の種類などを変えて対応できる余地を残しているということです。
メソッドの役割を中心にしていて、かつ将来の変更にも耐えられるように備えた良い命名だと思います。 普段の実装でも参考にしたいです。
2. 返されるランダムな文字列の長さが固定長(128 bit)であること
普通なら bit 数を引数で渡すような実装にしてしまうだろうところを固定にして、それ以外の長さの場合は自分で対応しましょうと決断した点に感心します。 128 bit 以外の長さを利用したい場合は、次のような数行程度のメソッドを定義すれば実現できます。
func Text256bit() string { return rand.Text() + rand.Text() } func Text64bit() string { return rand.Text()[:64] }
これを利用者側でやらせて、ライブラリ側をシンプルに保ちデフォルトを安全に倒すのは難しい判断です。 このバランスのとり方は、Goの設計の好きなところです。
3. 文字種別にRFC 4648で定義されているbase32のアルファベットが採用されたこと
文字種別については Issue コメント中で色々な意見がかわされていました。
生成される文字列に使われる文字種別を引数として渡す案が議論さていましたが、文字種を固定にした場合に得られる実装のシンプルさやユーザーが間違った利用をして脆弱性を生む可能性が検討された結果、文字種別が固定になりました。
文字の種類は、大文字と小文字や読み間違えやすい文字が混在してないこと、セキュリティ的に十分な128bitを表すのに26文字になり適度に短いことからBase 32が選ばれました。
表示時の視認性に対する考慮は、2FA/MFAでの利用などでユーザーが視認して入力するときのことを考慮してのことのようです2。
Base 32以外の文字を含むランダム文字列が求められるケースでは、専用のメソッドを自分で定義するので標準パッケージにrand.Text() が追加されても便利にならないだけで悪影響はないことも理由の1つです。
必要最小限の仕様で利用可能なものを初めに追加して使われ方を確認して、一般化していくのはGoで良く行われる仕様追加の手順だと思います。
まとめ
今までGoで暗号的にランダムな文字列を得るには crypt/rand パッケージを使って自分で実装するしかありませんでしたが、標準パッケージにあるものを使えば良いとなれば安心して使えるようになるので嬉しい提案でした。 もしBase32 以外の文字種別でランダムな文字列を生成するときがあれば実装の参考にもしたいです。
- 同僚と行っているGo本体や標準パッケージに対する提案を読む会のことです。↩
- https://github.com/golang/go/issues/67057#issuecomment-2161221119 前後のコメント↩