คุณสร้างสตริงแบบสุ่มที่เหมาะสมกับรหัสเซสชันใน PostgreSQL ได้อย่างไร


101

ฉันต้องการสร้างสตริงแบบสุ่มเพื่อใช้ในการตรวจสอบเซสชันโดยใช้ PostgreSQL ฉันรู้ว่าฉันสามารถสุ่มเลขได้SELECT random()ฉันจึงลองSELECT md5(random())แต่ก็ไม่ได้ผล ฉันจะทำเช่นนี้ได้อย่างไร?


อีกวิธีหนึ่งสามารถพบได้ที่นี่stackoverflow.com/a/13675441/398670
Craig Ringer

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

1
เจ๋งมาดูกันว่า @gersh สามารถชี้แจงคำถามนี้ได้หรือไม่เพราะมีความไม่เห็นด้วยกับความตั้งใจเดิมของเขา หากความตั้งใจเดิมของเขาเป็นอย่างที่ฉันคิดไว้คำตอบเหล่านี้จำนวนมากจำเป็นต้องได้รับการปรับลดลงคะแนนหรือถอนกลับ และอาจเป็นคำถามใหม่เกี่ยวกับการสร้างสตริงเพื่อวัตถุประสงค์ในการทดสอบ (หรือสิ่งที่คล้ายกัน) ควรได้รับการยกขึ้น (โดยที่random()ไม่จำเป็น) หากไม่ใช่สิ่งที่ฉันคิดไว้คำตอบของฉันจะต้องตอบคำถามที่ละเอียดอ่อนแทน
Evan Carroll

5
@EvanCarroll - gersh เห็นครั้งสุดท้าย 21 พ.ย. 2558
BSMP

5
สำหรับใครก็ตามที่ตอบคำถามนี้ในปี> 2017 ให้พิจารณาคำตอบของ Evan stackoverflow.com/a/41608000/190234เนื่องจากใช้วิธีการที่ไม่สามารถใช้ได้เมื่อเริ่มต้น Questio n ถูกถามและตอบ
Marcin Raczkowski

คำตอบ:


84

ฉันขอแนะนำวิธีง่ายๆนี้:

นี่เป็นฟังก์ชั่นที่ค่อนข้างเรียบง่ายที่ส่งกลับสตริงสุ่มตามความยาวที่กำหนด:

Create or replace function random_string(length integer) returns text as
$$
declare
  chars text[] := '{0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z}';
  result text := '';
  i integer := 0;
begin
  if length < 0 then
    raise exception 'Given length cannot be less than 0';
  end if;
  for i in 1..length loop
    result := result || chars[1+random()*(array_length(chars, 1)-1)];
  end loop;
  return result;
end;
$$ language plpgsql;

และการใช้งาน:

select random_string(15);

ตัวอย่างผลลัพธ์:

select random_string(15) from generate_series(1,15);

  random_string
-----------------
 5emZKMYUB9C2vT6
 3i4JfnKraWduR0J
 R5xEfIZEllNynJR
 tMAxfql0iMWMIxM
 aPSYd7pDLcyibl2
 3fPDd54P5llb84Z
 VeywDb53oQfn9GZ
 BJGaXtfaIkN4NV8
 w1mvxzX33NTiBby
 knI1Opt4QDonHCJ
 P9KC5IBcLE0owBQ
 vvEEwc4qfV4VJLg
 ckpwwuG8YbMYQJi
 rFf6TchXTO3XsLs
 axdQvaLBitm6SDP
(15 rows)

6
โซลูชันนี้ใช้ค่าที่ปลายด้านใดด้านหนึ่งของอาร์เรย์ chars - 0 และ z - ครึ่งหนึ่งบ่อยเท่าที่เหลือ สำหรับการกระจายตัวอักษรที่สม่ำเสมอยิ่งขึ้นฉันแทนที่chars[1+random()*(array_length(chars, 1)-1)]ด้วยchars[ceil(61 * random())]
PreciousBodilyFluids

random()ได้รับการเรียกlengthครั้ง (เช่นเดียวกับในโซลูชันอื่น ๆ ) มีวิธีที่มีประสิทธิภาพมากขึ้นในการเลือกจาก 62 ตัวอักษรในแต่ละครั้งหรือไม่? สิ่งนี้มีประสิทธิภาพmd5()อย่างไรเมื่อเทียบกับ?
ma11hew28

ฉันพบวิธีอื่นที่ใช้ORDER BY random(). ไหนเร็วกว่ากัน?
ma11hew28

1
เป็นที่น่าสังเกตว่าการสุ่มอาจใช้ erand48 ซึ่งไม่ใช่ CSPRNG คุณน่าจะดีกว่าแค่ใช้ pgcrypto
Yaur

2
คำตอบที่ดียกเว้นว่าจะไม่ใช้ตัวสร้างตัวเลขสุ่มที่ปลอดภัยดังนั้นจึงไม่ดีสำหรับรหัสเซสชัน ดู: stackoverflow.com/questions/9816114/…
sudo

240

คุณสามารถแก้ไขความพยายามครั้งแรกของคุณได้ดังนี้:

SELECT md5(random()::text);

ง่ายกว่าคำแนะนำอื่น ๆ มาก :-)


17
โปรดทราบว่าสิ่งนี้ส่งคืนสตริงที่อยู่เหนือ "ตัวอักษรเลขฐานสิบหก" {0..9, a..f} เท่านั้น อาจไม่เพียงพอ - ขึ้นอยู่กับว่าคุณต้องการทำอะไรกับพวกเขา
Laryx Decidua

ความยาวของสตริงที่ส่งคืนคืออะไร? มีวิธีทำให้สตริงที่ยาวขึ้นหรือไม่?
andrewrk

8
เมื่อแสดงเป็นเลขฐานสิบหกความยาวของสตริง MD5 จะเป็น 32 อักขระเสมอ หากคุณต้องการสตริงที่มีความยาว 64 คุณสามารถต่อสาย MD5 ได้ 2 สาย: SELECT concat(md5(random()::text), md5(random()::text)); และถ้าคุณต้องการที่ไหนสักแห่งที่อยู่ตรงกลาง (ตัวอย่างเช่น 50 ตัวอักษร) คุณสามารถใช้สตริงย่อยได้: SELECT substr(concat(md5(random()::text), md5(random()::text)), 0, 50);
Jimmie Tyrrell

2
ไม่ใช่ทางออกที่ดีสำหรับรหัสเซสชันไม่ใช่การสุ่มมากนัก คำตอบคือ 6 ปี ลองดูวิธีการที่แตกต่างกันโดยสิ้นเชิงโดยใช้gen_random_uuid() : เร็วขึ้นสุ่มมากขึ้นจัดเก็บอย่างมีประสิทธิภาพมากขึ้นในฐานข้อมูล
Evan Carroll

@Evan ถ้าคุณต้องการ 'สุ่ม' มากขึ้นโดยไม่ต้องขยายคุณสามารถSELECT md5(random()::text||random()::text);หรือSELECT md5(random()::text||random()::text||random()::text);

31

จากโซลูชันของ Marcin คุณสามารถทำได้โดยใช้ตัวอักษรตามอำเภอใจ (ในกรณีนี้คืออักขระตัวเลขและตัวอักษร ASCII ทั้งหมด 62 ตัว):

SELECT array_to_string(array 
       ( 
              select substr('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', trunc(random() * 62)::integer + 1, 1)
              FROM   generate_series(1, 12)), '');

ช้าไม่สุ่มหรือมีประสิทธิภาพในการจัดเก็บ ไม่ใช่ทางออกที่ดีสำหรับรหัสเซสชันไม่ใช่การสุ่มมากนัก คำตอบคือ 6 ปี Check out this for a totally different method using gen_random_uuid(): เร็วขึ้นสุ่มมากขึ้นเก็บไว้ในฐานข้อมูลอย่างมีประสิทธิภาพมากขึ้น
Evan Carroll

23

คุณสามารถสุ่มได้ 128 บิตจาก UUID นี่คือวิธีการทำงานให้เสร็จใน PostgreSQL สมัยใหม่

CREATE EXTENSION pgcrypto;
SELECT gen_random_uuid();

           gen_random_uuid            
--------------------------------------
 202ed325-b8b1-477f-8494-02475973a28f

อาจคุ้มค่ากับการอ่านเอกสารใน UUID ด้วย

ประเภทข้อมูล uuid จะจัดเก็บ Universally Unique Identifiers (UUID) ตามที่กำหนดโดยRFC 4122, ISO / IEC 9834-8: 2005และมาตรฐานที่เกี่ยวข้อง (ระบบบางระบบอ้างถึงชนิดข้อมูลนี้ว่าเป็นตัวระบุที่ไม่ซ้ำกันทั่วโลกหรือ GUID แทน) ตัวระบุนี้เป็นปริมาณ 128 บิตที่สร้างขึ้นโดยอัลกอริทึมที่เลือกเพื่อให้เป็นไปได้ยากมากที่บุคคลอื่นจะสร้างตัวระบุเดียวกัน ในจักรวาลที่รู้จักโดยใช้อัลกอริทึมเดียวกัน ดังนั้นสำหรับระบบแบบกระจายตัวระบุเหล่านี้จึงให้การรับประกันความเป็นเอกลักษณ์ที่ดีกว่าตัวสร้างลำดับซึ่งจะไม่ซ้ำกันภายในฐานข้อมูลเดียวเท่านั้น

การชนกับ UUID เกิดน้อยเพียงใดหรือเดาได้? สมมติว่าเป็นแบบสุ่ม

จะต้องมีการสร้าง UUID เวอร์ชัน 4 ประมาณ 100 ล้านล้านเพื่อให้มีโอกาส 1 ในพันล้านที่จะเกิดรายการซ้ำกัน ("การชนกัน") โอกาสที่จะเกิดการชนกันจะเพิ่มขึ้นเป็น 50% หลังจากสร้าง UUID 261 (2.3 x 10 ^ 18 หรือ 2.3 quintillion) แล้วเท่านั้น การเชื่อมโยงตัวเลขเหล่านี้กับฐานข้อมูลและเมื่อพิจารณาถึงปัญหาที่ว่าความน่าจะเป็นของการชนกันของ UUID เวอร์ชัน 4 นั้นเล็กน้อยหรือไม่ให้พิจารณาไฟล์ที่มี UUID เวอร์ชัน 4 จำนวน 2.3 quintillion ซึ่งมีโอกาส 50% ที่จะมีการชนกันของ UUID หนึ่งรายการ มันจะมีขนาด 36 เอ็กซาไบต์โดยสมมติว่าไม่มีข้อมูลหรือค่าใช้จ่ายอื่นใดที่ใหญ่กว่าฐานข้อมูลที่ใหญ่ที่สุดที่มีอยู่ในปัจจุบันหลายพันเท่าซึ่งเรียงตามลำดับเพตาไบต์ ในอัตรา 1 พันล้าน UUID ที่สร้างขึ้นต่อวินาทีจะใช้เวลา 73 ปีในการสร้าง UUID สำหรับไฟล์ นอกจากนี้ยังต้องใช้ประมาณ 3 ฮาร์ดไดรฟ์ขนาด 10 เทราไบต์ 6 ล้านตลับหรือตลับเทปเพื่อจัดเก็บโดยสมมติว่าไม่มีการสำรองข้อมูลหรือซ้ำซ้อน การอ่านไฟล์ที่อัตราการถ่ายโอน "disk-to-buffer" โดยทั่วไปคือ 1 กิกะบิตต่อวินาทีจะต้องใช้เวลามากกว่า 3000 ปีสำหรับโปรเซสเซอร์ตัวเดียว เนื่องจากอัตราความผิดพลาดในการอ่านที่ไม่สามารถกู้คืนได้ของไดรฟ์คือ 1 บิตต่อการอ่าน 1018 บิตอย่างดีที่สุดในขณะที่ไฟล์จะมีประมาณ 1,020 บิตการอ่านไฟล์เพียงครั้งเดียวจากต้นทางถึงปลายทางจะทำให้เกิดความผิดพลาดอย่างน้อยประมาณ 100 เท่า อ่าน UUID มากกว่ารายการที่ซ้ำกัน ข้อผิดพลาดในการจัดเก็บเครือข่ายพลังงานและฮาร์ดแวร์และซอฟต์แวร์อื่น ๆ จะเกิดขึ้นบ่อยกว่าปัญหาการทำสำเนา UUID หลายพันเท่า อัตราการถ่ายโอน 1 กิกะบิตต่อวินาทีจะต้องใช้เวลามากกว่า 3000 ปีสำหรับโปรเซสเซอร์ตัวเดียว เนื่องจากอัตราความผิดพลาดในการอ่านที่ไม่สามารถกู้คืนได้ของไดรฟ์คือ 1 บิตต่อการอ่าน 1018 บิตอย่างดีที่สุดในขณะที่ไฟล์จะมีประมาณ 1,020 บิตการอ่านไฟล์เพียงครั้งเดียวจากต้นทางถึงปลายทางจะทำให้เกิดความผิดพลาดอย่างน้อยประมาณ 100 เท่า อ่าน UUID มากกว่ารายการที่ซ้ำกัน ข้อผิดพลาดในการจัดเก็บเครือข่ายพลังงานและฮาร์ดแวร์และซอฟต์แวร์อื่น ๆ จะเกิดขึ้นบ่อยกว่าปัญหาการทำสำเนา UUID หลายพันเท่า อัตราการถ่ายโอน 1 กิกะบิตต่อวินาทีจะต้องใช้เวลามากกว่า 3000 ปีสำหรับโปรเซสเซอร์ตัวเดียว เนื่องจากอัตราความผิดพลาดในการอ่านที่ไม่สามารถกู้คืนได้ของไดรฟ์คือ 1 บิตต่อการอ่าน 1018 บิตอย่างดีที่สุดในขณะที่ไฟล์จะมีประมาณ 1,020 บิตการอ่านไฟล์เพียงครั้งเดียวจากต้นทางถึงปลายทางจะทำให้เกิดความผิดพลาดอย่างน้อยประมาณ 100 เท่า อ่าน UUID มากกว่ารายการที่ซ้ำกัน ข้อผิดพลาดในการจัดเก็บเครือข่ายพลังงานและฮาร์ดแวร์และซอฟต์แวร์อื่น ๆ จะเกิดขึ้นบ่อยกว่าปัญหาการทำสำเนา UUID หลายพันเท่า

ที่มา: wikipedia

สรุป,

  • UUID เป็นมาตรฐาน
  • gen_random_uuid()คือ 128 บิตสุ่มเก็บไว้ใน 128 บิต (2 ** 128 ชุดค่าผสม) 0- เสีย.
  • random() สร้างเฉพาะ 52 บิตของการสุ่มใน PostgreSQL (2 ** 52 ชุดค่าผสม)
  • md5()จัดเก็บเป็น UUID คือ 128 บิต แต่สามารถสุ่มได้เท่ากับอินพุตเท่านั้น ( 52 บิตหากใช้random() )
  • md5()จัดเก็บเป็นข้อความคือ 288 บิต แต่สามารถสุ่มได้เฉพาะอินพุตเท่านั้น ( 52 บิตหากใช้random() ) - มากกว่าสองเท่าของขนาด UUID และเศษส่วนของการสุ่ม)
  • md5() ในฐานะแฮชสามารถปรับให้เหมาะสมจนไม่สามารถทำอะไรได้มากนัก
  • UUID มีประสิทธิภาพสูงสำหรับการจัดเก็บ: PostgreSQL จัดเตรียมประเภทที่มีขนาด 128 บิต ซึ่งแตกต่างจากtextและvarcharอื่น ๆ ที่จัดเก็บvarlenaซึ่งมีค่าโสหุ้ยสำหรับความยาวของสตริง
  • PostgreSQL UUID ที่ดีมาพร้อมกับตัวดำเนินการการหล่อและคุณสมบัติเริ่มต้นบางอย่าง

3
ไม่ถูกต้องบางส่วน: UUID แบบสุ่มที่สร้างขึ้นอย่างถูกต้องมีเพียง 122 บิตแบบสุ่มเนื่องจากใช้ 4 บิตสำหรับเวอร์ชันและ 2 บิตสำหรับตัวแปร: en.wikipedia.org/wiki/…
Olivier Grégoireเมื่อ

2
ถ้าซอร์สไม่ทำตามที่เขียนไว้นั่นแสดงว่าไม่ใช่ UUID และไม่ควรถูกเรียกโดย PostgreSQL
Olivier Grégoire

16

เมื่อเร็ว ๆ นี้ฉันเล่นกับ PostgreSQL และฉันคิดว่าฉันได้พบวิธีแก้ปัญหาที่ดีกว่าเล็กน้อยโดยใช้วิธี PostgreSQL ในตัวเท่านั้น - ไม่มี pl / pgsql ข้อ จำกัด เพียงอย่างเดียวคือปัจจุบันสร้างเฉพาะสตริง UPCASE หรือตัวเลขหรือสตริงตัวพิมพ์เล็ก

template1=> SELECT array_to_string(ARRAY(SELECT chr((65 + round(random() * 25)) :: integer) FROM generate_series(1,12)), '');
 array_to_string
-----------------
 TFBEGODDVTDM

template1=> SELECT array_to_string(ARRAY(SELECT chr((48 + round(random() * 9)) :: integer) FROM generate_series(1,12)), '');
 array_to_string
-----------------
 868778103681

อาร์กิวเมนต์ที่สองของgenerate_seriesวิธีการกำหนดความยาวของสตริง


8
ฉันชอบสิ่งนี้ แต่พบว่าเมื่อฉันใช้คำสั่ง UPDATE ทุกแถวถูกตั้งค่าเป็นรหัสผ่านแบบสุ่มเดียวกันแทนที่จะเป็นรหัสผ่านที่ไม่ซ้ำกัน ฉันแก้ไขสิ่งนี้โดยการเพิ่มรหัสคีย์หลักลงในสูตร ฉันเพิ่มลงในค่าสุ่มและลบอีกครั้ง การสุ่มจะไม่เปลี่ยนแปลง แต่ PostgreSQL ถูกหลอกให้คำนวณค่าสำหรับแต่ละแถวอีกครั้ง นี่คือตัวอย่างโดยใช้ชื่อคีย์หลักของ "my_id": array_to_string(ARRAY(SELECT chr((65 + round((random()+my_id-my) * 25)) :: integer) FROM generate_series(1,8)), '')
Mark Stosberg

วิธีแก้ปัญหาที่ @MarkStosberg นำเสนอทำงานตามที่เขาพูด แต่ไม่เป็นไปตามที่ฉันคาดไว้ ข้อมูลที่ผลิตไม่ตรงกับรูปแบบที่แสร้งทำเป็น (เป็นตัวอักษรหรือตัวเลขเพียงอย่างเดียว) ฉันแก้ไขโดยการปรับเลขคณิตผลลัพธ์แบบสุ่ม: array_to_string(ARRAY(SELECT chr((65 + round((random() * 25 + id) :: integer % 25 )) :: integer) FROM generate_series(1, 60)), '');
Nuno Rafael Figueiredo

4
ไม่คุณกำลังตอบคำถาม 'ฉันจะสร้างรหัสเซสชันแบบสุ่มได้อย่างไร' ไม่ใช่ 'ฉันจะสร้างสตริงแบบสุ่มได้อย่างไร' คุณได้เปลี่ยนความหมายของ quesiton (และชื่อเรื่อง) ตามคำสองคำในคำอธิบาย คุณกำลังตอบคำถามอื่น และใช้อำนาจการกลั่นกรองของคุณในทางมิชอบเพื่อเปลี่ยนความหมายของคำถาม
Marcin Raczkowski

14

กรุณาใช้string_agg!

SELECT string_agg (substr('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', ceil (random() * 62)::integer, 1), '')
FROM   generate_series(1, 45);

ฉันใช้สิ่งนี้กับ MD5 เพื่อสร้าง UUID ด้วย ฉันแค่ต้องการค่าสุ่มที่มีบิตมากกว่าrandom ()จำนวนเต็ม


ฉันคิดว่าฉันสามารถเชื่อมต่อได้random()จนกว่าฉันจะได้จำนวนบิตที่ฉันต้องการ โอ้ดี.
Andrew Wolfe

11

แม้ว่าจะไม่ได้ใช้งานตามค่าเริ่มต้นคุณสามารถเปิดใช้งานส่วนขยายหลักอย่างใดอย่างหนึ่ง:

CREATE EXTENSION IF NOT EXISTS pgcrypto;

จากนั้นคำสั่งของคุณจะกลายเป็นคำเรียกง่ายๆไปยัง gen_salt () ซึ่งสร้างสตริงแบบสุ่ม:

select gen_salt('md5') from generate_series(1,4);

 gen_salt
-----------
$1$M.QRlF4U
$1$cv7bNJDM
$1$av34779p
$1$ZQkrCXHD

หมายเลขนำหน้าคือตัวระบุแฮช มีหลายอัลกอริทึมพร้อมตัวระบุของตัวเอง:

  • md5: $ 1 $
  • bf: $ 2a $ 06 $
  • des: ไม่มีตัวระบุ
  • xdes: _J9 ..

ข้อมูลเพิ่มเติมเกี่ยวกับส่วนขยาย:


แก้ไข

ตามที่ระบุโดย Evan Carrol ณ v9.4 คุณสามารถใช้ได้ gen_random_uuid()

http://www.postgresql.org/docs/9.4/static/pgcrypto.html


เกลือที่สร้างขึ้นดูเหมือนจะเรียงตามลำดับเกินไปที่จะสุ่มจริงๆใช่มั้ย?
Le Droid

1
คุณหมายถึง$1$? นั่นคือตัวระบุประเภทแฮช (md5 == 1) ส่วนที่เหลือเป็นค่าแบบสุ่ม
Jefferey Cave

ใช่นั่นเป็นการตีความที่ผิดพลาดของฉันขอบคุณสำหรับความแม่นยำ
Le Droid

6

ฉันไม่คิดว่าคุณกำลังมองหาสตริงสุ่มต่อ se สิ่งที่คุณต้องการสำหรับการตรวจสอบเซสชันคือสตริงที่รับประกันว่าไม่ซ้ำกัน คุณจัดเก็บข้อมูลการตรวจสอบเซสชันสำหรับการตรวจสอบหรือไม่? ในกรณีนี้คุณต้องให้สตริงไม่ซ้ำกันระหว่างเซสชัน ฉันรู้สองวิธีที่ค่อนข้างง่าย:

  1. ใช้ลำดับ เหมาะสำหรับใช้กับฐานข้อมูลเดียว
  2. ใช้ UUID ไม่เหมือนใครในระดับสากลเหมาะสำหรับสภาพแวดล้อมแบบกระจายเช่นกัน

UUIDs ได้รับการรับรองว่าไม่เหมือนใครโดยอาศัยอัลกอริทึมในการสร้าง อย่างมีประสิทธิภาพเป็นอย่างยิ่งไปได้ยากที่คุณจะสร้างตัวเลขที่เหมือนกันสองตัวบนเครื่องใดก็ได้ตลอดเวลา (โปรดทราบว่าสิ่งนี้แข็งแกร่งกว่าสตริงแบบสุ่มซึ่งมีระยะเวลาน้อยกว่า UUID มาก)

คุณต้องโหลดส่วนขยาย uuid-ossp เพื่อใช้ UUID เมื่อติดตั้งแล้วให้เรียกใช้ฟังก์ชัน uuid_generate_vXXX () ที่มีอยู่ในการโทร SELECT, INSERT หรือ UPDATE ประเภท uuid เป็นตัวเลข 16 ไบต์ แต่ยังมีการแทนค่าสตริงด้วย


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

6
@ jmar777 จุดประสงค์ทั้งหมดของ UUIDs คือพวกเขาคาดเดาได้ยากและมีความสุ่มสูง ยกเว้นเวอร์ชัน v1 จะมีระยะเวลาที่สูงมาก v4 เป็นแบบสุ่ม 128 บิตอย่างสมบูรณ์ พวกเขาถูกใช้ในทุกธุรกรรมธนาคารออนไลน์ที่คุณทำ หากพวกเขาดีพอสำหรับสิ่งนั้นพวกเขาก็ดีพอสำหรับสิ่งอื่น ๆ
Patrick

1
คุณรู้อะไรบ้าง ผมไม่ทราบว่าได้รับการแก้ไขในรุ่นที่ 4 ขอบคุณสำหรับการแก้ไข!
jmar777

@Patrick Small nit, V4 UUIDs เป็นแบบสุ่ม 122 บิตไม่ใช่ 128)
Jesse

5

พารามิเตอร์ INTEGER กำหนดความยาวของสตริง รับประกันว่าจะครอบคลุมอักขระที่เป็นตัวอักษรและตัวเลขทั้งหมด 62 ตัวโดยมีความน่าจะเป็นเท่ากัน (ไม่เหมือนกับโซลูชันอื่น ๆ ที่ลอยอยู่บนอินเทอร์เน็ต)

CREATE OR REPLACE FUNCTION random_string(INTEGER)
RETURNS TEXT AS
$BODY$
SELECT array_to_string(
    ARRAY (
        SELECT substring(
            '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
            FROM (ceil(random()*62))::int FOR 1
        )
        FROM generate_series(1, $1)
    ), 
    ''
)
$BODY$
LANGUAGE sql VOLATILE;

ช้าไม่สุ่มหรือมีประสิทธิภาพในการจัดเก็บ ไม่ใช่ทางออกที่ดีสำหรับรหัสเซสชันไม่ใช่การสุ่มมากนัก คำตอบคือ 6 ปี Check out this for a totally different method using gen_random_uuid(): เร็วขึ้นสุ่มมากขึ้นเก็บไว้ในฐานข้อมูลอย่างมีประสิทธิภาพมากขึ้น
Evan Carroll

3
@EvanCarroll: ในความเป็นธรรมทั้งหมดgen_random_uuid()ปรากฏในเวอร์ชัน 9.4 เท่าที่ฉันสามารถบอกได้ซึ่งเปิดตัวในปี 2014-12-18 มากกว่าหนึ่งปีหลังจากคำตอบที่คุณลงคะแนน nitpick เพิ่มเติม: คำตอบคืออายุเพียง 3 1/2 ปี :-) แต่คุณพูดถูกตอนนี้เรามีgen_random_uuid()แล้วนี่คือสิ่งที่ควรใช้ ดังนั้นฉันจะโหวตคำตอบของคุณ
Laryx Decidua

5

@Kavius แนะนำการใช้pgcryptoแต่แทนที่จะgen_saltสิ่งที่เกี่ยวกับgen_random_bytes? แล้วsha512แทนที่จะเป็นmd5อย่างไร?

create extension if not exists pgcrypto;
select digest(gen_random_bytes(1024), 'sha512');

เอกสาร:

ฉ. 25.5. ฟังก์ชันสุ่มข้อมูล

gen_random_bytes (นับจำนวนเต็ม) ส่งคืนไบต์

ส่งคืนจำนวนไบต์สุ่มที่คาดเดายากที่เข้ารหัส สามารถแยกได้สูงสุด 1024 ไบต์ต่อครั้ง เพื่อหลีกเลี่ยงการระบายพูลตัวสร้างการสุ่ม



2
select encode(decode(md5(random()::text), 'hex')||decode(md5(random()::text), 'hex'), 'base64')

ฉันแก้ไขเพื่อลบเครื่องหมายทับและเครื่องหมายบวกที่บางครั้งปรากฏในผลลัพธ์และเพื่อสร้างผลลัพธ์ตัวพิมพ์ใหญ่ให้เลือกส่วนบน (แทนที่ (แทนที่ (สตริงย่อย (เข้ารหัส (ถอดรหัส (md5 (สุ่ม ()) :: ข้อความ)))) 'hex ') || ถอดรหัส (md5 (สุ่ม () :: text),' hex '),' base64 '), 0, 10),' / ',' A '),' + ',' Z '));
Seun Matt
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.