PHP:ランダムで一意の英数字文字列を生成するにはどうすればよいでしょうか?
-
12-09-2019 - |
質問
検証リンクで使用するために、数字と文字を使用してランダムで一意の文字列を生成するにはどうすればよいでしょうか?たとえば、Web サイトでアカウントを作成すると、リンクが記載されたメールが送信され、アカウントを確認するにはそのリンクをクリックする必要があります...そう...その 1 つです。
PHP を使用してこれらを生成するにはどうすればよいですか?
アップデート: ちょうど思い出したのですが、 uniqid()
. 。これは、マイクロ秒単位の現在時刻に基づいて一意の識別子を生成する PHP 関数です。それを使おうと思います。
解決
セキュリティに関する通知:このソリューションは、ランダム性の品質がアプリケーションのセキュリティに影響を与える可能性がある状況では使用しないでください。特に、
rand()
そしてuniqid()
暗号的に安全な乱数生成器ではありません. 。見る スコットの答え 安全な代替手段として。
時間の経過とともに完全に一意になる必要がない場合は、次のようにします。
md5(uniqid(rand(), true))
それ以外の場合 (ユーザーの一意のログインがすでに決定されている場合):
md5(uniqid($your_user_login, true))
他のヒント
私はこれと同じ問題を解決する方法を検討していましたが、関数でパスワードの取得にも使用できるトークンを作成したいとも考えています。これは、トークンの推測能力を制限する必要があることを意味します。なぜなら uniqid
は時間に基づいており、php.net によると「戻り値は microtime() とほとんど変わりません」、 uniqid
基準を満たしていません。PHP は次の使用を推奨します openssl_random_pseudo_bytes()
代わりに、暗号的に安全なトークンを生成します。
短く、要点を絞った答えは次のとおりです。
bin2hex(openssl_random_pseudo_bytes($bytes))
これにより、長さ = $bytes * 2 の英数字のランダムな文字列が生成されます。残念ながら、これにはアルファベットしかありません [a-f][0-9]
, 、しかしそれはうまくいきます。
以下は、基準を満たす、私が作成できる最も強力な関数です (これは、Erik の回答の実装バージョンです)。
function crypto_rand_secure($min, $max)
{
$range = $max - $min;
if ($range < 1) return $min; // not so random...
$log = ceil(log($range, 2));
$bytes = (int) ($log / 8) + 1; // length in bytes
$bits = (int) $log + 1; // length in bits
$filter = (int) (1 << $bits) - 1; // set all lower bits to 1
do {
$rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
$rnd = $rnd & $filter; // discard irrelevant bits
} while ($rnd > $range);
return $min + $rnd;
}
function getToken($length)
{
$token = "";
$codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$codeAlphabet.= "abcdefghijklmnopqrstuvwxyz";
$codeAlphabet.= "0123456789";
$max = strlen($codeAlphabet); // edited
for ($i=0; $i < $length; $i++) {
$token .= $codeAlphabet[crypto_rand_secure(0, $max-1)];
}
return $token;
}
crypto_rand_secure($min, $max)
のドロップイン代替品として機能します rand()
または mt_rand
. 。openssl_random_pseudo_bytes を使用して、$min と $max の間の乱数を作成します。
getToken($length)
トークン内で使用するアルファベットを作成し、次の長さの文字列を作成します。 $length
.
編集: 出典の引用を怠りました - http://us1.php.net/manual/en/function.openssl-random-pseudo-bytes.php#104322
編集(PHP7): PHP7 のリリースにより、標準ライブラリには上記の crypto_rand_secure 関数を置き換え/改善/簡素化できる 2 つの新しい関数が追加されました。 random_bytes($length)
そして random_int($min, $max)
http://php.net/manual/en/function.random-bytes.php
http://php.net/manual/en/function.random-int.php
例:
function getToken($length){
$token = "";
$codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$codeAlphabet.= "abcdefghijklmnopqrstuvwxyz";
$codeAlphabet.= "0123456789";
$max = strlen($codeAlphabet); // edited
for ($i=0; $i < $length; $i++) {
$token .= $codeAlphabet[random_int(0, $max-1)];
}
return $token;
}
最も賛成票を集めたソリューションのオブジェクト指向バージョン
に基づいてオブジェクト指向ソリューションを作成しました スコットさんの答え:
<?php
namespace Utils;
/**
* Class RandomStringGenerator
* @package Utils
*
* Solution taken from here:
* http://stackoverflow.com/a/13733588/1056679
*/
class RandomStringGenerator
{
/** @var string */
protected $alphabet;
/** @var int */
protected $alphabetLength;
/**
* @param string $alphabet
*/
public function __construct($alphabet = '')
{
if ('' !== $alphabet) {
$this->setAlphabet($alphabet);
} else {
$this->setAlphabet(
implode(range('a', 'z'))
. implode(range('A', 'Z'))
. implode(range(0, 9))
);
}
}
/**
* @param string $alphabet
*/
public function setAlphabet($alphabet)
{
$this->alphabet = $alphabet;
$this->alphabetLength = strlen($alphabet);
}
/**
* @param int $length
* @return string
*/
public function generate($length)
{
$token = '';
for ($i = 0; $i < $length; $i++) {
$randomKey = $this->getRandomInteger(0, $this->alphabetLength);
$token .= $this->alphabet[$randomKey];
}
return $token;
}
/**
* @param int $min
* @param int $max
* @return int
*/
protected function getRandomInteger($min, $max)
{
$range = ($max - $min);
if ($range < 0) {
// Not so random...
return $min;
}
$log = log($range, 2);
// Length in bytes.
$bytes = (int) ($log / 8) + 1;
// Length in bits.
$bits = (int) $log + 1;
// Set all lower bits to 1.
$filter = (int) (1 << $bits) - 1;
do {
$rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
// Discard irrelevant bits.
$rnd = $rnd & $filter;
} while ($rnd >= $range);
return ($min + $rnd);
}
}
使用法
<?php
use Utils\RandomStringGenerator;
// Create new instance of generator class.
$generator = new RandomStringGenerator;
// Set token length.
$tokenLength = 32;
// Call method to generate random string.
$token = $generator->generate($tokenLength);
カスタムアルファベット
必要に応じてカスタムのアルファベットを使用できます。サポートされている文字を含む文字列をコンストラクターまたはセッターに渡すだけです。
<?php
$customAlphabet = '0123456789ABCDEF';
// Set initial alphabet.
$generator = new RandomStringGenerator($customAlphabet);
// Change alphabet whenever needed.
$generator->setAlphabet($customAlphabet);
出力サンプルは次のとおりです
SRniGU2sRQb2K1ylXKnWwZr4HrtdRgrM
q1sRUjNq1K9rG905aneFzyD5IcqD4dlC
I0euIWffrURLKCCJZ5PQFcNUCto6cQfD
AKwPJMEM5ytgJyJyGqoD5FQwxv82YvMr
duoRF6gAawNOEQRICnOUNYmStWmOpEgS
sdHUkEn4565AJoTtkc8EqJ6cC4MLEHUx
eVywMdYXczuZmHaJ50nIVQjOidEVkVna
baJGt7cdLDbIxMctLsEBWgAw5BByP5V0
iqT0B2obq3oerbeXkDVLjZrrLheW4d8f
OUQYCny6tj2TYDlTuu1KsnUyaLkeObwa
誰かの役に立つことを願っています。乾杯!
遅くなりましたが、提供されている機能に基づいたいくつかの優れた研究データをここに持ってきました。 スコットの答え. 。そこで、この 5 日間にわたる自動テストのために Digital Ocean ドロップレットをセットアップし、生成された一意の文字列を MySQL データベースに保存しました。
このテスト期間中、5 つの異なる長さ (5、10、15、20、50) を使用し、各長さに対して +/-050 万レコードが挿入されました。私のテストでは、50 万個のうち長さ 5 だけで +/-3K の重複が生成され、残りの長さでは重複は生成されませんでした。したがって、Scott 関数で 15 以上の長さを使用すれば、信頼性の高い一意の文字列を生成できると言えます。これが私の研究データを示す表です。
アップデート:これらの関数を使用して、トークンを JSON 応答として返す単純な Heroku アプリを作成しました。アプリには次の場所からアクセスできます https://uniquestrings.herokuapp.com/api/token?length=15
これがお役に立てば幸いです。
この関数は、数字と文字を使用してランダムなキーを生成します。
function random_string($length) {
$key = '';
$keys = array_merge(range(0, 9), range('a', 'z'));
for ($i = 0; $i < $length; $i++) {
$key .= $keys[array_rand($keys)];
}
return $key;
}
echo random_string(50);
出力例:
zsd16xzv3jsytnp87tk7ygv73k8zmr0ekh6ly7mxaeyeh46oe8
UUID(Universally Unique Identifier)を使用することができ、ユーザー認証文字列から決済トランザクションIDまで、あらゆる目的に使用できます。
UUID は 16 オクテット (128 ビット) の数値です。正規形式では、UUID は 32 桁の 16 進数で表され、8-4-4-4-12 の形式でハイフンで区切られた 5 つのグループに表示され、合計 36 文字 (英数字 32 文字とハイフン 4 文字) で構成されます。
function generate_uuid() {
return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
mt_rand( 0, 0xffff ),
mt_rand( 0, 0x0C2f ) | 0x4000,
mt_rand( 0, 0x3fff ) | 0x8000,
mt_rand( 0, 0x2Aff ), mt_rand( 0, 0xffD3 ), mt_rand( 0, 0xff4B )
);
}
//関数の呼び出し
$transationID = generate_uuid();
いくつかの出力例は次のようになります。
E302D66D-87E3-4450-8CB6-17531895BF14
22D288BC-7289-442B-BEEA-286777D559F2
51B4DE29-3B71-4FD2-9E6C-071703E1FF31
3777C8C6-9FF5-4C78-AAA2-08A47F555E81
54B91C72-2CF4-4501-A6E9-02A60DCBAE4C
60F75C7C-1AE3-417B-82C8-14D456542CD7
8DE0168D-01D3-4502-9E59-10D665CEBCB2
将来誰かに役立つことを願っています:)
私は次のワンライナーを使用します。
base64_encode(openssl_random_pseudo_bytes(3 * ($length >> 2)));
ここで、length は目的の文字列の長さです (4 で割り切れます。それ以外の場合は、4 で割り切れる最も近い数値に切り捨てられます)。
- お気に入りのランダム数ジェネレーターを使用して乱数を生成します
- それを掛けて分割して、コードアルファベットの文字の数と一致する数字を取得します
- コードアルファベットのそのインデックスでアイテムを取得します。
- 1)が必要になるまで繰り返します
例 (疑似コード内)
int myInt = random(0, numcharacters)
char[] codealphabet = 'ABCDEF12345'
char random = codealphabet[i]
repeat until long enough
以下のコードを使用して 11 文字の乱数を生成するか、要件に応じて数値を変更します。
$randomNum=substr(str_shuffle("0123456789abcdefghijklmnopqrstvwxyz"), 0, 11);
または、カスタム関数を使用して乱数を生成することもできます
function randomNumber($length){
$numbers = range(0,9);
shuffle($numbers);
for($i = 0;$i < $length;$i++)
$digits .= $numbers[$i];
return $digits;
}
//generate random number
$randomNum=randomNumber(11);
これはあなたのための究極のユニークなIDジェネレーターです。私が作りました。
<?php
$d=date ("d");
$m=date ("m");
$y=date ("Y");
$t=time();
$dmt=$d+$m+$y+$t;
$ran= rand(0,10000000);
$dmtran= $dmt+$ran;
$un= uniqid();
$dmtun = $dmt.$un;
$mdun = md5($dmtran.$un);
$sort=substr($mdun, 16); // if you want sort length code.
echo $mdun;
?>
必要に応じて、ID の「var」をエコーできます。ただし、$mdun の方が優れています。コードを改善するには、md5 を sha1 に置き換えることができますが、非常に長くなるので必要ないかもしれません。
ありがとう。
本当にランダムな文字列の場合は、次を使用できます
<?php
echo md5(microtime(true).mt_Rand());
出力:
40a29479ec808ad4bcff288a48a25d5c
そのため、まったく同時に文字列を複数回生成しようとしても、異なる出力が得られます。
私が使用しているものは次のとおりです。
md5(time() . rand());
// Creates something like 0c947c3b1047334f5bb8a3b7adc1d97b
function random_string($length = 8) {
$alphabets = range('A','Z');
$numbers = range('0','9');
$additional_characters = array('_','=');
$final_array = array_merge($alphabets,$numbers,$additional_characters);
while($length--) {
$key = array_rand($final_array);
$password .= $final_array[$key];
}
if (preg_match('/[A-Za-z0-9]/', $password))
{
return $password;
}else{
return random_string();
}
}
ランダムなパスワードを生成しようとすると、次のことを試みます。
まず、暗号的に安全なランダム バイトのセットを生成します。
2 番目は、ランダムなバイトを印刷可能な文字列に変換することです。
さて、PHP でランダムなバイトを生成するには、次のような方法が複数あります。
$length = 32;
//PHP 7+
$bytes= random_bytes($length);
//PHP < 7
$bytes= openssl_random_pseudo_bytes($length);
次に、これらのランダムなバイトを印刷可能な文字列に変換します。
使用できます bin2hex :
$string = bin2hex($bytes);
または Base64_encode :
$string = base64_encode($bytes);
ただし、base64 を使用する場合は文字列の長さを制御できないことに注意してください。これを行うには bin2hex を使用できます。 32バイト に変わります 64文字の文字列。しかし、それは次のような場合にのみ機能します。 平 弦。
前の例を読んだ後、私はこれを思いつきました:
protected static $nonce_length = 32;
public static function getNonce()
{
$chars = array();
for ($i = 0; $i < 10; $i++)
$chars = array_merge($chars, range(0, 9), range('A', 'Z'));
shuffle($chars);
$start = mt_rand(0, count($chars) - self::$nonce_length);
return substr(join('', $chars), $start, self::$nonce_length);
}
Array [0-9、AZ]を10倍複製し、要素をシャッフルします。SubsTR()がより「クリエイティブ」になるランダム開始点を取得した後、[AZ]およびその他の要素を配列に追加できます。多かれ少なかれ、私よりも創造的であること
私は検証リンクを扱うときにハッシュキーを使用することを好みます。マイクロタイムに基づいてハッシュするため、キーが同じである必要がある理由がないため、マイクロタイムを使用し、MD5 を使用してハッシュすることをお勧めします。
$key = md5(rand());
$key = md5(microtime());
これは、文字と数字 (英数字) を含むランダムな文字列を生成できる単純な関数です。文字列の長さを制限することもできます。これらのランダムな文字列は、次のようなさまざまな目的に使用できます。紹介コード、プロモーションコード、クーポンコード。関数は次の PHP 関数に依存します。Base_convert、sha1、uniqid、mt_rand
function random_code($length)
{
return substr(base_convert(sha1(uniqid(mt_rand())), 16, 36), 0, $length);
}
echo random_code(6);
/*sample output
* a7d9e8
* 3klo93
*/
スコット、はい、あなたは非常に文章が上手で、良い解決策です!ありがとう。
また、各ユーザーに対して一意の API トークンを生成する必要もあります。以下は私のアプローチです。ユーザー情報(ユーザーIDとユーザー名)を使用しました。
public function generateUniqueToken($userid, $username){
$rand = mt_rand(100,999);
$md5 = md5($userid.'!(&^ 532567_465 ///'.$username);
$md53 = substr($md5,0,3);
$md5_remaining = substr($md5,3);
$md5 = $md53. $rand. $userid. $md5_remaining;
return $md5;
}
ぜひご覧いただき、改善できることがあればお知らせください。ありがとう
これは私が自分のプロジェクトの1つで使用しているものです。うまく機能しており、 ユニークなランダムトークン:
$timestampz=time();
function generateRandomString($length = 60) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
$tokenparta = generateRandomString();
$token = $timestampz*3 . $tokenparta;
echo $token;
このトークンがどのように生成されるのか疑問に思っているユーザーを混乱させるために、タイムスタンプを 3 倍にしていることに注意してください ;)
それが役立つことを願っています:)
これが一番使える方法だと思います。
str_shuffle(md5(rand(0,100000)))
<?php
function generateRandomString($length = 11) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
?>
上記の関数は、11 文字の長さのランダムな文字列を生成します。
この 2 行のコードを使用して一意の文字列を生成できます。約 10000000 回の反復テストを行っています。
$sffledStr= str_shuffle('abscdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()_-+');
$uniqueString = md5(time().$sffledStr);
私はいつもこの関数を使用して、カスタムのランダムな英数字文字列を生成します。これがお役に立てば幸いです。
<?php
function random_alphanumeric($length) {
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ12345689';
$my_string = '';
for ($i = 0; $i < $length; $i++) {
$pos = mt_rand(0, strlen($chars) -1);
$my_string .= substr($chars, $pos, 1);
}
return $my_string;
}
echo random_alphanumeric(50); // 50 characters
?>
たとえば、次のものが生成されます。Y1FypdjVbFCFK6Gh9FDJpe6dciwJEfV6MQGpJqAfuijaYSZ86g
別の文字列と比較して、それが一意のシーケンスであることを確認したい場合は、このトリックを使用できます...
$string_1 = random_alphanumeric(50);
$string_2 = random_alphanumeric(50);
while ($string_1 == $string_2) {
$string_1 = random_alphanumeric(50);
$string_2 = random_alphanumeric(50);
if ($string_1 != $string_2) {
break;
}
}
echo $string_1;
echo "<br>\n";
echo $string_2;
2 つの一意の文字列が生成されます。
qsBDs4JOoVRfFxyLAOGECYIsWvpcpMzAO9pypwxsqPKeAmYLOi
Ti3kE1WfGgTNxQVXtbNNbhhvvapnaUfGMVJecHkUjHbuCb85pF
これがあなたが探しているものであることを願っています...
私が思うに、既存のアイデアすべての問題は、 おそらく ユニークだけどそうではない 絶対に ユニークです(loletechへのDariusz Walczakの返信で指摘されているように)。実はユニークな解決策があります。スクリプトに何らかのメモリが必要です。私にとって、これは SQL データベースです。単純にどこかのファイルに書き込むこともできます。次の 2 つの実装があります。
最初の方法:一意性を提供するフィールドが 1 つではなく 2 つあります。最初のフィールドは、ランダムではなく一意の ID 番号です (最初の ID は 1、2 番目の ID は 2...)。SQL を使用している場合は、AUTO_INCREMENT プロパティを使用して ID フィールドを定義するだけです。2 番目のフィールドは一意ではなく、ランダムです。これは、すでに言及されている他のテクニックのいずれかを使用して生成できます。Scott のアイデアは優れていましたが、md5 は便利で、おそらくほとんどの目的には十分です。
$random_token = md5($_SERVER['HTTP_USER_AGENT'] . time());
2 番目の方法:基本的に同じ考え方ですが、最初に生成される文字列の最大数を選択します。これは、1 兆のような非常に大きな数字になる可能性があります。次に、同じことを行って ID を生成しますが、すべての ID が同じ桁数になるようにゼロで埋め込みます。次に、ID をランダムな文字列と連結するだけです。ほとんどの目的には十分ランダムですが、ID セクションによって一意であることも保証されます。
このコードを使用できます。それがあなたに役立つことを願っています。
function rand_code($len)
{
$min_lenght= 0;
$max_lenght = 100;
$bigL = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$smallL = "abcdefghijklmnopqrstuvwxyz";
$number = "0123456789";
$bigB = str_shuffle($bigL);
$smallS = str_shuffle($smallL);
$numberS = str_shuffle($number);
$subA = substr($bigB,0,5);
$subB = substr($bigB,6,5);
$subC = substr($bigB,10,5);
$subD = substr($smallS,0,5);
$subE = substr($smallS,6,5);
$subF = substr($smallS,10,5);
$subG = substr($numberS,0,5);
$subH = substr($numberS,6,5);
$subI = substr($numberS,10,5);
$RandCode1 = str_shuffle($subA.$subD.$subB.$subF.$subC.$subE);
$RandCode2 = str_shuffle($RandCode1);
$RandCode = $RandCode1.$RandCode2;
if ($len>$min_lenght && $len<$max_lenght)
{
$CodeEX = substr($RandCode,0,$len);
}
else
{
$CodeEX = $RandCode;
}
return $CodeEX;
}
についての詳細 PHP のランダム コード ジェネレーター
不要なループを削除して上記の Scotts コードを簡素化すると、速度が大幅に低下し、openssl_random_pseudo_bytes を 1 回だけ呼び出すよりも安全にはなりません。
function crypto_rand_secure($min, $max)
{
$range = $max - $min;
if ($range < 1) return $min; // not so random...
$log = ceil(log($range, 2));
$bytes = (int) ($log / 8) + 1; // length in bytes
$rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
return $min + $rnd%$range;
}
function getToken($length)
{
return bin2hex(openssl_random_pseudo_bytes($length)
}