PHP: จะสร้างสตริงตัวอักษรและตัวเลขแบบสุ่มที่ไม่ซ้ำใครได้อย่างไร


380

เป็นไปได้อย่างไรที่จะสร้างสตริงที่ไม่ซ้ำกันโดยใช้ตัวเลขและตัวอักษรเพื่อใช้ในลิงค์ยืนยัน เช่นเมื่อคุณสร้างบัญชีในเว็บไซต์และมันจะส่งอีเมลพร้อมลิงก์ให้คุณและคุณต้องคลิกลิงค์นั้นเพื่อยืนยันบัญชีของคุณ ... ใช่ ... หนึ่งในนั้น

ฉันจะสร้างหนึ่งในนั้นโดยใช้ PHP ได้อย่างไร

อัปเดต:เพิ่งจำuniqid()ได้ เป็นฟังก์ชัน PHP ที่สร้างตัวระบุที่ไม่ซ้ำกันโดยยึดตามเวลาปัจจุบันเป็นไมโครวินาที ฉันคิดว่าฉันจะใช้มัน


สิ่งที่คุณต้องมีคือสตริงและการสุ่มตัวเลขที่กระจายอย่างสม่ำเสมอ
Artelius

10
เฮ้แอนดรูคุณควรเลือกScottเป็นคำตอบที่ถูกต้อง Scott กำลังใช้ตัวสร้างหมายเลข psudo-random number (CSPRNG) ที่เข้ารหัสลับโดย OpenSSL ซึ่งจะเลือกแหล่งข้อมูลเอนโทรปีที่ปลอดภัยที่สุดบนแพลตฟอร์มของคุณ
rook

3
ใครก็ตามที่อ่านข้อความนี้หลังจากปี 2558 โปรดดูที่นี่: paragonie.com/blog/2015/07/…คำตอบยอดนิยมส่วนใหญ่มีข้อบกพร่องไม่มากก็น้อย ...
rugk

OpenSSL และuniqidไม่ปลอดภัย ใช้สิ่งที่ต้องการหรือRandom::alphanumericString($length) Random::alphanumericHumanString($length)
caw

คำตอบ:


305

ประกาศเกี่ยวกับความปลอดภัย : ไม่ควรใช้โซลูชันนี้ในสถานการณ์ที่คุณภาพของการสุ่มของคุณอาจส่งผลต่อความปลอดภัยของแอปพลิเคชัน โดยเฉพาะอย่างยิ่งrand()และuniqid()ไม่ได้เข้ารหัสรักษาความปลอดภัยเครื่องกำเนิดไฟฟ้าจำนวนสุ่ม ดูคำตอบของสกอตต์สำหรับทางเลือกที่ปลอดภัย

หากคุณไม่ต้องการให้มีความเป็นเอกลักษณ์อย่างแน่นอนเมื่อเวลาผ่านไป:

md5(uniqid(rand(), true))

มิฉะนั้น (เนื่องจากคุณได้กำหนดเข้าสู่ระบบที่ไม่ซ้ำกันสำหรับผู้ใช้ของคุณ):

md5(uniqid($your_user_login, true))

90
ทั้งสองวิธีไม่รับประกันความเป็นเอกลักษณ์ - ความยาวของอินพุตของฟังก์ชัน md5 มากกว่าความยาวของเอาต์พุตและตามen.wikipedia.org/wiki/Pigeonhole_principleการรับประกันการชนกันของข้อมูล ในทางกลับกันยิ่งประชากรอาศัยแฮชมากขึ้นเพื่อให้ได้รหัส "ที่ไม่ซ้ำกัน" ยิ่งมีโอกาสเกิดการปะทะกันอย่างน้อยหนึ่งครั้ง (ดูen.wikipedia.org/wiki/Birthday_problem ) ความน่าจะเป็นอาจจะเล็กมากสำหรับการแก้ปัญหาส่วนใหญ่ แต่ก็ยังมีอยู่
Dariusz Walczak

12
นี่ไม่ใช่วิธีที่ปลอดภัยในการสร้างค่าแบบสุ่ม ดูคำตอบของสกอตต์
rook

4
สำหรับการยืนยันทางอีเมลที่ใกล้จะหมดอายุในไม่ช้าฉันคิดว่าความเป็นไปได้เล็กน้อยที่คน ๆ หนึ่งอาจจะยืนยันว่าอีเมลของคนอื่นในวันนี้มีความเสี่ยงเล็กน้อย แต่คุณสามารถตรวจสอบได้ว่าโทเค็นนั้นมีอยู่แล้วในฐานข้อมูลหลังจากสร้างมันขึ้นมาและเพียงแค่เลือกโทเค็นใหม่ถ้าเป็นกรณีนั้น อย่างไรก็ตามเวลาที่คุณใช้ในการเขียนโค้ดขนาดสั้นนั้นมีแนวโน้มที่จะสูญเปล่าเพราะจะไม่มีการเรียกใช้มากที่สุด
Andrew

โปรดทราบว่า md5 ส่งคืนค่าเลขฐานสิบหกซึ่งหมายถึงชุดอักขระ จำกัด ไว้ที่ [0-9] และ [af]
Thijs Riezebeek

1
md5 สามารถชนกันได้คุณเพิ่งทำลายข้อได้เปรียบของ
uniqid

507

ฉันเพิ่งดูวิธีแก้ปัญหาเดียวกันนี้ แต่ฉันต้องการให้ฟังก์ชันสร้างโทเค็นที่สามารถใช้สำหรับการดึงรหัสผ่านได้เช่นกัน ซึ่งหมายความว่าฉันต้องจำกัดความสามารถของโทเค็นที่จะเดาได้ เนื่องจากuniqidขึ้นอยู่กับเวลาและตาม php.net "ค่าส่งคืนแตกต่างจาก microtime ()" เล็กน้อยuniqidไม่ตรงตามเกณฑ์ PHP แนะนำให้ใช้openssl_random_pseudo_bytes()แทนเพื่อสร้างโทเค็นที่ปลอดภัยด้วยการเข้ารหัส

คำตอบสั้น ๆ และตรงประเด็นคือ:

bin2hex(openssl_random_pseudo_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/th/function.openssl-random-pseudo-bytes.php#104322

แก้ไข (PHP7):ด้วยการเปิดตัว PHP7 ทำให้ไลบรารีมาตรฐานมีสองฟังก์ชันใหม่ที่สามารถแทนที่ / ปรับปรุง / ลดความซับซ้อนของฟังก์ชั่น crypto_rand_secure ด้านบน 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);

    for ($i=0; $i < $length; $i++) {
        $token .= $codeAlphabet[random_int(0, $max-1)];
    }

    return $token;
}

2
ตกลง การจัดเก็บรหัสผ่านแบบธรรมดานั้นแย่มาก! (ฉันใช้ปักเป้า) แต่หลังจากสร้างโทเค็นแล้วมันอนุญาตให้เจ้าของโทเค็นรีเซ็ตรหัสผ่านดังนั้นโทเค็นจึงมีรหัสผ่านที่เทียบเท่าในขณะที่ใช้ได้ (และถือว่าเป็นเช่นนั้น)
Scott

23
เฮ้ฉันรวมโค้ดที่ยอดเยี่ยมของคุณเข้ากับฟังก์ชันการทำงานของ Kohana Text :: random () ที่สะดวกและสร้างส่วนสำคัญนี้: gist.github.com/raveren/5555297
raveren

9
ที่สมบูรณ์แบบ! ฉันทดสอบด้วยความยาว 10 ตัวอักษร โทเค็น 100,000 รายการและยังไม่มีการทำซ้ำ!
Sharpless512

5
ฉันใช้กระบวนการนี้และพบว่ามันช้ามาก ด้วยความยาวของ 36 มันหมดเวลาบนเซิร์ฟเวอร์ dev ด้วยความยาว 24 มันยังคงใช้เวลาประมาณ 30 วินาที ไม่แน่ใจว่าฉันทำอะไรผิด แต่มันช้าเกินไปสำหรับฉันและฉันเลือกวิธีอื่น
เชน

5
นอกจากนี้คุณยังสามารถใช้base64_encodeแทนbin2hexการรับสตริงที่สั้นกว่าด้วยตัวอักษรขนาดใหญ่
erjiang

91

เวอร์ชันเชิงวัตถุของโซลูชันที่ได้รับการโหวตมากที่สุด

ฉันได้สร้างโซลูชันเชิงวัตถุตามคำตอบของScott :

<?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

ฉันหวังว่ามันจะช่วยให้ใครบางคน ไชโย!


ฉันขอโทษ แต่ฉันจะทำสิ่งนี้ได้อย่างไร ฉันทำ$obj = new RandomStringGenerator;แต่ฉันควรใช้วิธีการใด ขอบคุณ.
sg552

@ sg552 คุณควรเรียกgenerateวิธีการตามตัวอย่างด้านบน เพียงแค่ส่งจำนวนเต็มไปที่มันเพื่อระบุความยาวของสตริงผลลัพธ์
Slava Fomin II

ขอบคุณมันใช้งานได้สำหรับฉัน แต่Custom Alphabetไม่ได้ ฉันได้เอาต์พุตที่ว่างเปล่าเมื่อฉันก้อง อย่างไรก็ตามฉันไม่แน่ใจด้วยซ้ำว่าการใช้ตัวอย่างที่ 2 คือCustom Alphabetอะไรดังนั้นฉันคิดว่าฉันแค่อยู่กับตัวอย่างแรก ขอบคุณ
sg552

1
ดี แต่ค่อนข้าง overkill ในสถานการณ์ส่วนใหญ่เว้นแต่โครงการของคุณต้องอาศัยรหัสที่สร้างแบบสุ่ม
dspacejs

1
ใช่ดี แต่ overkill โดยเฉพาะอย่างยิ่งการพิจารณาว่ามันเลิกใช้แล้วตอนนี้สำหรับ php 5.7
Andrew

39

ฉันมาช้า แต่ฉันมาที่นี่พร้อมข้อมูลการวิจัยที่ดีบางส่วนจากฟังก์ชั่นที่ได้รับคำตอบจากScottคำตอบที่สกอตต์ดังนั้นฉันจึงตั้งค่า Digital Ocean droplet สำหรับการทดสอบอัตโนมัตินาน 5 วันและเก็บสตริงที่ไม่ซ้ำกันที่สร้างขึ้นในฐานข้อมูล MySQL

ในช่วงระยะเวลาการทดสอบนี้ฉันใช้ความยาว 5 แบบ (5, 10, 15, 20, 50) และ +/- 0.5 ล้านบันทึกสำหรับแต่ละความยาว ในระหว่างการทดสอบของฉันมีเพียงความยาว 5 เท่านั้นที่สร้าง +/- 3K ซ้ำจาก 0.5 ล้านและความยาวที่เหลือไม่ได้สร้างซ้ำ ดังนั้นเราสามารถพูดได้ว่าถ้าเราใช้ความยาว 15 หรือสูงกว่ากับฟังก์ชั่นของสก็อตเราก็สามารถสร้างสตริงที่มีความน่าเชื่อถือสูงได้ นี่คือตารางแสดงข้อมูลการวิจัยของฉัน:

ป้อนคำอธิบายรูปภาพที่นี่

อัปเดต: ฉันสร้างแอพ Heroku แบบง่ายโดยใช้ฟังก์ชั่นเหล่านี้ซึ่งส่งกลับโทเค็นเป็นการตอบสนองของ JSON แอปสามารถเข้าถึงได้ที่https://uniquestrings.herokuapp.com/api/token?length=15

ฉันหวังว่านี่จะช่วยได้.


33

คุณสามารถใช้ UUID (Universally Unique Identifier) ​​สามารถใช้เพื่อวัตถุประสงค์ใด ๆ จากสตริงการตรวจสอบผู้ใช้ไปยัง id ธุรกรรมการชำระเงิน

UUID คือหมายเลข 16-octet (128- บิต) ในรูปแบบที่เป็นที่ยอมรับ UUID จะถูกแสดงด้วยตัวเลขฐานสิบหก 32 ตัวซึ่งแสดงในห้ากลุ่มที่คั่นด้วยเครื่องหมายขีดคั่นในรูปแบบ 8-4-4-4-12 สำหรับทั้งหมด 36 ตัวอักษร (ตัวอักษรและตัวเลข 32 ตัวและสี่ยัติภังค์)

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 )
    );

}

// การเรียก funtion

$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

หวังว่ามันจะช่วยใครบางคนในอนาคต :)


ดูยอดเยี่ยมเพิ่มไว้ในรายการของฉัน!
Mustafa Erdogan

32

ฟังก์ชั่นนี้จะสร้างคีย์สุ่มโดยใช้ตัวเลขและตัวอักษร:

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

6
หลังจากที่ในขณะที่คุณได้รับหมายเลขเดียวกัน
Sharpless512

1
นี่ไม่ได้สุ่มอย่างเต็มที่เพราะมันจะไม่มี chacater เดียวกันมากกว่าหนึ่งครั้ง
EricP

16

ฉันใช้หนึ่งซับนี้:

base64_encode(openssl_random_pseudo_bytes(3 * ($length >> 2)));

โดยที่ length คือความยาวของสตริงที่ต้องการ (หารด้วย 4 มิฉะนั้นจะถูกปัดเศษลงเป็นจำนวนที่ใกล้ที่สุดหารด้วย 4)


6
มันจะไม่ส่งกลับเฉพาะตัวอักษรและตัวเลขเท่านั้น สามารถมีอักขระเช่น==
user3459110

1
หากใช้ใน URL base64url_encodeสามารถใช้แทนได้ดูที่นี่: php.net/manual/en/function.base64-encode.php
Paul

9

ใช้รหัสด้านล่างเพื่อสร้างตัวเลขสุ่ม 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);

3
สิ่งนี้ไม่สามารถสร้างสตริง "aaaaaaaaaaa" หรือ "aaaaabbbbbb" มันเลือกสตริงย่อยของการเรียงสับเปลี่ยนแบบสุ่มของตัวอักษร สตริงที่มีความยาว 11 ตัวอักษรมีเพียง V (11,35) = 288 ค่าที่เป็นไปได้ อย่าใช้สิ่งนี้หากคุณคาดหวังว่าสตริงจะไม่ซ้ำกัน!
kdojeteri

@ การปิงทำแล้วsubstr(str_shuffle(str_repeat('0123456789abcdefghijklmnopqrstvwxyz', 36)), 0, 11);
ทิมฮอลแมน

7
  1. สร้างหมายเลขสุ่มโดยใช้ตัวสร้างหมายเลขสุ่มที่คุณชื่นชอบ
  2. คูณและหารมันเพื่อให้ได้ตัวเลขที่ตรงกับจำนวนตัวอักษรในตัวอักษรรหัสของคุณ
  3. รับรายการที่ดัชนีนั้นในตัวอักษรรหัสของคุณ
  4. ทำซ้ำตั้งแต่ 1) จนกว่าคุณจะมีความยาวตามที่คุณต้องการ

เช่น (ในรหัสหลอก)

int myInt = random(0, numcharacters)
char[] codealphabet = 'ABCDEF12345'
char random = codealphabet[i]
repeat until long enough

ในทางทฤษฎีการพูดสิ่งนี้ไม่สามารถสร้างสตริงเดียวกันมากกว่าหนึ่งครั้งได้หรือไม่?
frezq

4

นี่คือตัวสร้างรหัสสุดยอดที่ไม่ซ้ำใครสำหรับคุณ ฉันทำเอง.

<?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;
?>

คุณสามารถสะท้อน 'var' สำหรับรหัสของคุณตามที่คุณต้องการ แต่ $ mdun นั้นดีกว่าคุณสามารถแทนที่ md5 เป็น sha1 เพื่อรหัสที่ดีขึ้น แต่นั่นจะใช้เวลานานมากซึ่งคุณอาจไม่ต้องการ

ขอบคุณ.


7
ในกรณีนี้การสุ่มนั้นรับประกันด้วยการเขียนโค้ดแบบสุ่มใช่ไหม?
Daniel Kucal

3
ใช่นี่คือ "สุ่ม" เพราะมันไม่ได้มาตรฐานรหัสคลุมเครือ สิ่งนี้ทำให้ยากต่อการคาดเดา ... แต่ไม่จริงมันใช้ชิ้นส่วนที่ไม่ปลอดภัยเพื่อสร้างตัวเลขสุ่ม ดังนั้นผลลัพธ์จะไม่สุ่มหรือปลอดภัยในทางเทคนิค
ฟิลิปป์

4

สำหรับสตริงสุ่มคุณสามารถใช้

<?php

echo md5(microtime(true).mt_Rand());

เอาท์พุท:

40a29479ec808ad4bcff288a48a25d5c

ดังนั้นแม้ว่าคุณจะพยายามสร้างสตริงหลายครั้งในเวลาเดียวกันคุณจะได้ผลลัพธ์ที่แตกต่างกัน


4

เมื่อพยายามสร้างรหัสผ่านแบบสุ่มคุณพยายามที่จะ:

  • ขั้นแรกให้สร้างชุดสุ่มไบต์ที่ปลอดภัยโดยการเข้ารหัส

  • สองคือการเปลี่ยนไบต์สุ่มเหล่านั้นเป็นสตริงที่พิมพ์ได้

ตอนนี้มีหลายวิธีในการสร้างไบต์สุ่มใน 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 ถ่าน แต่มันจะใช้งานได้เช่นในสตริงEVEN

โดยพื้นฐานแล้วคุณสามารถทำได้ :

$length = 32;

if(PHP_VERSION>=7){
    $bytes= random_bytes($length);
}else{
    $bytes= openssl_random_pseudo_bytes($length);
} 

$string = bin2hex($bytes);


2
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 ดังต่อไปนี้: 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
*/

1

หลังจากอ่านตัวอย่างก่อนหน้านี้ฉันพบสิ่งนี้:

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);
}

ฉันทำซ้ำ 10 ครั้งอาร์เรย์ [0-9, AZ] และสับเปลี่ยนองค์ประกอบหลังจากที่ฉันได้รับจุดเริ่มต้นแบบสุ่มสำหรับ substr () ให้เป็น 'สร้างสรรค์' มากขึ้น :) คุณสามารถเพิ่ม [az] และองค์ประกอบอื่น ๆ ลงในอาร์เรย์ซ้ำกัน มากหรือน้อยสร้างสรรค์มากกว่าฉัน


1

ฉันชอบใช้แฮชคีย์เมื่อจัดการลิงก์การยืนยัน ฉันอยากจะแนะนำให้ใช้ microtime และ hashing ที่ใช้ MD5 เนื่องจากไม่มีเหตุผลว่าทำไมปุ่มควรจะเหมือนกันเพราะมันแฮชจากพื้นฐานของ microtime

  1. $key = md5(rand());
  2. $key = md5(microtime());

1
<?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 ตัวอักษร


1

หากคุณต้องการสร้างสตริงที่ไม่ซ้ำกันใน PHP ลองต่อไปนี้

md5(uniqid().mt_rand());

ในเรื่องนี้,

uniqid()- มันจะสร้างสตริงที่ไม่ซ้ำกัน ฟังก์ชั่นนี้จะส่งกลับตัวระบุที่ไม่ซ้ำกันตามเวลาประทับเป็นสตริง

mt_rand() - สร้างตัวเลขสุ่ม

md5() - มันจะสร้างสตริงแฮช


0

สกอตต์ใช่คุณเขียนมากและเป็นทางออกที่ดี! ขอบคุณ

ฉันต้องสร้างโทเค็น API เฉพาะสำหรับผู้ใช้ของฉันด้วย ต่อไปนี้เป็นวิธีการของฉันฉันใช้ข้อมูลผู้ใช้ (ชื่อผู้ใช้และชื่อผู้ใช้):

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;
}

โปรดดูและแจ้งให้เราทราบหากการปรับปรุงใด ๆ ที่ฉันสามารถทำได้ ขอบคุณ


0

นี่คือสิ่งที่ฉันใช้กับหนึ่งในโครงการของฉันมันใช้งานได้ดีและสร้างโทเค็นสุ่มที่ไม่ซ้ำใคร :

$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;

โปรดทราบว่าฉันได้เพิ่มการประทับเวลาสามครั้งเพื่อสร้างความสับสนสำหรับผู้ใช้ที่อาจสงสัยว่าโทเค็นนี้สร้างได้อย่างไร)

ฉันหวังว่ามันจะช่วย :)


1
"โปรดทราบว่าฉันเพิ่มเวลาลงสามเท่าเพื่อสร้างความสับสนสำหรับผู้ใช้ทุกคนที่อาจสงสัยว่าโทเค็นนี้สร้างได้อย่างไร" เสียงเหมือนความปลอดภัยผ่าน obfuscation stackoverflow.com/questions/533965/…
แฮร์รี่

นี่ไม่ใช่ "ความปลอดภัยผ่านการทำให้งง" ขณะที่เพิ่มประทับเวลาลงในสตริงสุ่มที่สร้างขึ้นและนั่นคือสตริงสุดท้ายจริงที่เราส่งคืน)
Kaszoni Ferencz


0

เราสามารถใช้โค้ดสองบรรทัดนี้เพื่อสร้างสตริงที่ไม่ซ้ำกันซึ่งมีการทดสอบซ้ำประมาณ 10,000,000 ครั้ง

  $sffledStr= str_shuffle('abscdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()_-+');
    $uniqueString = md5(time().$sffledStr);

0

ฉันใช้ฟังก์ชันนี้เพื่อสร้างสตริงตัวอักษรและตัวเลขแบบสุ่มที่กำหนดเองเสมอ ... หวังว่าจะช่วยได้

<?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;

มันสร้างสองสตริงที่ไม่ซ้ำกัน:

qsBDs4JOoVRfFxyLAOGECYIsWvpcpMzAO9pypwxsqPKeAmYLOi

Ti3kE1WfGgTNxQVXtbNNbhhvvapnaUfGMVJecHkUjHbuCb85pF

หวังว่านี่คือสิ่งที่คุณกำลังมองหา ...


0

วิธีง่ายๆคือการแปลงฐาน 64 เป็นตัวอักษรและตัวเลขโดยการละทิ้งอักขระที่ไม่ใช่ตัวอักษรและตัวเลข

อันนี้ใช้ random_bytes () สำหรับผลลัพธ์ที่ปลอดภัยแบบเข้ารหัส

function random_alphanumeric(int $length): string
{
    $result='';
    do
    {
        //Base 64 produces 4 characters for each 3 bytes, so most times this will give enough bytes in a single pass
        $bytes=random_bytes(($length+3-strlen($result))*2);
        //Discard non-alhpanumeric characters
        $result.=str_replace(['/','+','='],['','',''],base64_encode($bytes));
        //Keep adding characters until the string is long enough
        //Add a few extra because the last 2 or 3 characters of a base 64 string tend to be less diverse
    }while(strlen($result)<$length+3);
    return substr($result,0,$length);
}


-1

ฉันเชื่อว่าปัญหาที่เกิดขึ้นกับความคิดที่มีอยู่ทั้งหมดคือพวกเขาอาจจะไม่ซ้ำกัน แต่ไม่ซ้ำกันแน่นอน (ตามที่ระบุไว้ในคำตอบของ Dariusz Walczak เพื่อ loletech) ฉันมีทางออกที่เป็นเอกลักษณ์จริง ๆ มันต้องการให้สคริปต์ของคุณมีหน่วยความจำบางประเภท สำหรับฉันนี่คือฐานข้อมูล SQL คุณสามารถเขียนไฟล์ได้ง่ายๆ มีการนำไปใช้งานสองแบบ:

วิธีแรก: มีสองเขตข้อมูลมากกว่า 1 ที่ให้เอกลักษณ์ ฟิลด์แรกคือหมายเลข ID ที่ไม่ได้สุ่ม แต่เป็นค่าที่ไม่ซ้ำกัน (ID แรกคือ 1, 2 ที่สอง ... ) หากคุณใช้ SQL เพียงกำหนดฟิลด์ ID ด้วยคุณสมบัติ AUTO_INCREMENT ฟิลด์ที่สองไม่ซ้ำกัน แต่เป็นแบบสุ่ม สิ่งนี้สามารถสร้างขึ้นได้ด้วยเทคนิคอื่น ๆ ที่ผู้คนได้กล่าวถึงไปแล้ว ความคิดของสกอตต์นั้นดี แต่ md5 สะดวกและอาจดีพอสำหรับวัตถุประสงค์ส่วนใหญ่:

$random_token = md5($_SERVER['HTTP_USER_AGENT'] . time());

วิธีที่สอง: โดยพื้นฐานแล้วแนวคิดเดียวกัน แต่เริ่มแรกเลือกจำนวนสูงสุดของสตริงที่จะถูกสร้างขึ้น นี่อาจเป็นจำนวนมหาศาลเช่นล้านล้าน จากนั้นทำสิ่งเดียวกันสร้าง ID แต่ให้ศูนย์เป็นศูนย์เพื่อให้ ID ทั้งหมดมีจำนวนหลักเท่ากัน จากนั้นเพียงแค่เชื่อม ID กับสตริงสุ่ม มันจะสุ่มพอสำหรับจุดประสงค์ส่วนใหญ่ แต่ส่วน ID จะทำให้แน่ใจว่ามันจะไม่ซ้ำกัน


-1

คุณสามารถใช้รหัสนี้ฉันหวังว่ามันจะเป็นประโยชน์สำหรับคุณ

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


-2

ลดความซับซ้อนของโค้ด Scotts ด้านบนโดยการลบลูปที่ไม่จำเป็นซึ่งทำให้ช้าลงอย่างมากและไม่ทำให้ปลอดภัยกว่าการเรียก openssl_random_pseudo_bytes เพียงครั้งเดียว

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)
}

น่าเสียดายที่ฉันไม่สามารถเห็นด้วยกับคุณได้ว่านี่เป็นฟังก์ชั่นการทำให้เข้าใจง่ายของ crypto_rand_secure การวนซ้ำนั้นจำเป็นเพื่อหลีกเลี่ยงการอคติแบบโมดูโลที่เกิดขึ้นเมื่อคุณรับผลลัพธ์ของตัวเลขที่สร้างแบบสุ่มแล้วทำการแก้ไขด้วยตัวเลขที่แบ่งช่วง en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#Modulo_bias ด้วยการใช้การวนซ้ำเพื่อโยนตัวเลขใด ๆ ที่อยู่นอกช่วงและเลือกหมายเลขใหม่มันจะหลีกเลี่ยงอคตินี้ stackoverflow.com/questions/10984974/…
สกอตต์

@Scott โพสต์นั้นเกี่ยวกับ rand () อย่างไรก็ตามในกรณีนี้มันได้รับการดูแลโดย openssl_random_pseudo_bytes ดังนั้นจึงไม่จำเป็นต้องวนซ้ำ
mabel

1
gist.github.com/anonymous/87e3ae6fd3320447f46b973e75c2ea85 - โปรดเรียกใช้สคริปต์นี้ คุณจะเห็นว่าการกระจายตัวของผลลัพธ์ไม่ได้สุ่ม ตัวเลขระหว่าง 0-55 มีการเกิดขึ้นสูงกว่าตัวเลขระหว่าง 56-100 opensslไม่ได้ทำให้สิ่งนี้เป็นเพราะความเอนเอียงโมดูโลในคำสั่งการส่งคืนสินค้าของคุณ 100 (ช่วง) ไม่แบ่ง 255 (1 ไบต์) อย่างสม่ำเสมอและทำให้ผลลัพธ์เหล่านี้ ปัญหาที่ใหญ่ที่สุดของฉันกับคำตอบของคุณคือคุณระบุว่าฟังก์ชั่นนั้นปลอดภัยเท่ากับการใช้ลูปซึ่งเป็นเท็จ การติดตั้ง crypto_rand_secure ของคุณนั้นไม่ปลอดภัยและเข้ารหัสลับทำให้เข้าใจผิด
สกอตต์

@Scott เรากำลังพูดถึงที่นี่มันไม่เกี่ยวกับคำสั่งการส่งคืนล่าสุดมันยังสามารถคำนวณได้อย่างก้าวหน้าจากมูลค่าที่ส่งคืนของ openssl_random_pseudo_bytes โดยไม่ต้องใช้โมดูโล่ จุดที่ฉันทำก็คือการวนซ้ำนั้นไม่จำเป็นเมื่อ openssl_random_pseudo_bytes ส่งคืนไบต์แบบสุ่ม ด้วยการวนซ้ำคุณจะไม่ทำให้ปลอดภัยมากขึ้นเพียงทำให้ช้าลง
mabel

2
คำตอบนี้เผยแพร่ข้อมูลที่ไม่ดี คุณถูกต้องว่าการวนซ้ำนั้นไม่ได้ทำให้opensslปลอดภัยมากขึ้น แต่ที่สำคัญกว่านั้นคือมันไม่ทำให้มีความปลอดภัยน้อยลงคำตอบนี้ทำ โค้ดด้านบนใช้หมายเลขสุ่มที่ดีอย่างสมบูรณ์และ mangles โดย biasing ไปยังหมายเลขที่ต่ำกว่า หากคุณใส่คำปฏิเสธในคำตอบของคุณระบุว่ามันเร็วกว่า (โดยการลบลูป) แต่ไม่ใช่ CSPRNG มันจะแก้ไขปัญหานี้ได้ กรุณาเพื่อความรักของการเข้ารหัสลับให้ผู้ใช้ทราบว่าผลลัพธ์ของฟังก์ชั่นนี้อาจลำเอียงและไม่ได้แก้ปัญหาที่เทียบเท่า ลูปไม่ได้อยู่ที่นั่นเพื่อรบกวนคุณ
สกอตต์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.