私は、疑似ランダムな英数字文字列を生成するためのシンプルな500K+
Java アルゴリズムを探していました。私の状況では、これは、生成後も「おそらく」一意である一意のセッション/キー識別子として使用されます (私のニーズでは、これより高度なものは必要ありません)。
理想的には、一意性のニーズに応じて長さを指定できるはずです。たとえば、長さ 12 の生成された文字列は次のようになります"AEYGF7K0DM1X"
。
ベストアンサー1
アルゴリズム
ランダムな文字列を生成するには、文字列が目的の長さに達するまで、許容される記号のセットからランダムに抽出された文字を連結します。
実装
ランダムな識別子を生成するための、かなりシンプルで非常に柔軟なコードを以下に示します。重要なアプリケーション ノートについては、以下の情報をお読みください。
public class RandomString {
/**
* Generate a random string.
*/
public String nextString() {
for (int idx = 0; idx < buf.length; ++idx)
buf[idx] = symbols[random.nextInt(symbols.length)];
return new String(buf);
}
public static final String upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static final String lower = upper.toLowerCase(Locale.ROOT);
public static final String digits = "0123456789";
public static final String alphanum = upper + lower + digits;
private final Random random;
private final char[] symbols;
private final char[] buf;
public RandomString(int length, Random random, String symbols) {
if (length < 1) throw new IllegalArgumentException();
if (symbols.length() < 2) throw new IllegalArgumentException();
this.random = Objects.requireNonNull(random);
this.symbols = symbols.toCharArray();
this.buf = new char[length];
}
/**
* Create an alphanumeric string generator.
*/
public RandomString(int length, Random random) {
this(length, random, alphanum);
}
/**
* Create an alphanumeric strings from a secure generator.
*/
public RandomString(int length) {
this(length, new SecureRandom());
}
/**
* Create session identifiers.
*/
public RandomString() {
this(21);
}
}
使用例
8 文字の識別子用の安全でないジェネレーターを作成します。
RandomString gen = new RandomString(8, ThreadLocalRandom.current());
セッション識別子の安全なジェネレーターを作成します。
RandomString session = new RandomString();
印刷用に読みやすいコードを含むジェネレーターを作成します。使用する記号の数が少ないため、文字列は完全な英数字文字列よりも長くなります。
String easy = RandomString.digits + "ACEFGHJKLMNPQRUVWXYabcdefhijkprstuvwx";
RandomString tickets = new RandomString(23, new SecureRandom(), easy);
セッション識別子として使用
一意である可能性が高いセッション識別子を生成するだけでは不十分です。単純なカウンターを使用することもできます。予測可能な識別子を使用すると、攻撃者がセッションを乗っ取る可能性があります。
長さとセキュリティの間には緊張関係があります。識別子が短いほど、可能性が少なくなるため推測しやすくなります。しかし、識別子が長いほど、より多くのストレージと帯域幅を消費します。記号セットを大きくすると役立ちますが、識別子が URL に含まれていたり、手動で再入力されたりすると、エンコードの問題が発生する可能性があります。
セッション識別子のランダム性、つまりエントロピーの根底にあるソースは、暗号化用に設計された乱数ジェネレーターから取得する必要があります。ただし、これらのジェネレーターの初期化は、計算コストがかかったり、遅くなったりすることがあるため、可能な場合は再利用するように努める必要があります。
オブジェクト識別子として使用
すべてのアプリケーションにセキュリティが必要なわけではありません。ランダム割り当ては、複数のエンティティが調整やパーティション分割を行わずに共有スペースで識別子を生成するための効率的な方法です。調整は、特にクラスター環境や分散環境では遅くなることがあり、スペースを分割すると、エンティティの共有が小さすぎたり大きすぎたりする場合に問題が発生します。
ほとんどの Web アプリケーションで発生しているように、予測不可能にする対策を講じずに生成された識別子は、攻撃者がそれを表示および操作できる可能性がある場合は、他の手段で保護する必要があります。アクセス権限のない攻撃者が識別子を推測できるオブジェクトを保護する別の認証システムが必要です。
予想される識別子の総数を考慮して、衝突が起きにくいほど長い識別子を使用するように注意する必要があります。これは「誕生日のパラドックス」と呼ばれます。衝突の確率、 pはおよそ n 2 /(2q x ) です。ここで、nは実際に生成される識別子の数、qはアルファベット内の異なる記号の数、x は識別子の長さです。これは 2 ‑50以下などの非常に小さい数である必要があります。
これを計算すると、500k 個の 15 文字の識別子が衝突する確率は約 2 -52であることがわかります。これは、宇宙線などによる検出されないエラーよりも可能性が低いと考えられます。
UUIDとの比較
彼らの仕様によれば、UUID予測不可能になるように設計されていないため、セッション識別子として使用しないでください。
標準形式の UUID は多くのスペースを占有します。36 文字に対してエントロピーはわずか 122 ビットです。(「ランダム」な UUID のすべてのビットがランダムに選択されるわけではありません。) ランダムに選択された英数字文字列は、わずか 21 文字でより多くのエントロピーを詰め込みます。
UUID は柔軟性に欠け、構造とレイアウトが標準化されています。これが UUID の主な長所であると同時に、主な短所でもあります。外部の関係者と共同作業を行う場合、UUID が提供する標準化は役立つ場合があります。純粋に内部で使用する場合、UUID は非効率的である可能性があります。