Wie erstellt man eine zufällige Zeichenfolge, die für eine Sitzungs-ID in PostgreSQL geeignet ist?

101

Ich möchte eine zufällige Zeichenfolge für die Sitzungsüberprüfung mit PostgreSQL erstellen. Ich weiß, dass ich eine Zufallszahl mit bekommen kann SELECT random(), also habe ich es versucht SELECT md5(random()), aber das funktioniert nicht. Wie kann ich das machen?

gersh
quelle
Eine andere Lösung finden Sie hier stackoverflow.com/a/13675441/398670
Craig Ringer
7
Ich habe den Titel so bearbeitet, dass vorhandene Antworten immer noch durchaus sinnvoll sind, und Evans Antwort bringt auch etwas modernere Passungen. Ich möchte diese uralte Frage nicht für einen Streit um Inhalte sperren. Nehmen wir also zusätzliche Änderungen vor, die allen Antworten entsprechen.
Tim Post
1
Cool, mal sehen, ob @gersh diese Frage klären kann, weil es legitime Meinungsverschiedenheiten über seine ursprüngliche Absicht gibt. Wenn seine ursprüngliche Absicht so ist, wie ich annehme, müssen viele dieser Antworten angepasst, herabgestimmt oder zurückgezogen werden. Und vielleicht sollte eine neue Frage zum Generieren von Zeichenfolgen zu Testzwecken (oder dergleichen) aufgeworfen werden (wo dies random()nicht erforderlich ist). Wenn es nicht das ist, was ich annehme, muss meine Antwort stattdessen auf die verfeinerte Frage ausgerichtet werden.
Evan Carroll
5
@EvanCarroll - gersh wurde zuletzt gesehen 21. November 2015.
BSMP
5
Wenn Sie im Jahr> 2017 zu dieser Frage kommen, ziehen Sie Evans Antwort stackoverflow.com/a/41608000/190234 in Betracht, da sie die Methoden verwendet, die nicht verfügbar waren, als die Frage ursprünglich gestellt und beantwortet wurde.
Marcin Raczkowski

Antworten:

83

Ich würde diese einfache Lösung vorschlagen:

Dies ist eine recht einfache Funktion, die eine zufällige Zeichenfolge der angegebenen Länge zurückgibt:

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;

Und die Verwendung:

select random_string(15);

Beispielausgabe:

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)
Szymon Lipiński
quelle
6
Diese Lösung verwendet die Werte an beiden Enden des Zeichenarrays - 0 und z - halb so oft wie die übrigen. Für eine gleichmäßige Verteilung der Zeichen, ersetzte ich chars[1+random()*(array_length(chars, 1)-1)]mitchars[ceil(61 * random())]
PreciousBodilyFluids
random()wird lengthmal aufgerufen (wie in vielen anderen Lösungen). Gibt es eine effizientere Möglichkeit, jedes Mal aus 62 Zeichen auszuwählen? Wie funktioniert das im Vergleich zu md5()?
ma11hew28
Ich habe eine andere Lösung gefunden , die verwendet ORDER BY random(). Welche ist schneller?
ma11hew28
1
Es ist erwähnenswert, dass zufällig erand48 verwendet, das kein CSPRNG ist. Sie sind wahrscheinlich besser dran, wenn Sie nur pgcrypto verwenden.
Yaur
2
Gute Antwort, außer dass es keinen sicheren Zufallszahlengenerator verwendet und daher nicht so gut für Sitzungs-IDs ist. Siehe: stackoverflow.com/questions/9816114/...
sudo
237

Sie können Ihren ersten Versuch folgendermaßen korrigieren:

SELECT md5(random()::text);

Viel einfacher als einige der anderen Vorschläge. :-)

Peter Eisentraut
quelle
16
Beachten Sie, dass dies nur Zeichenfolgen über dem "Hex-Ziffern-Alphabet" {0..9, a..f} zurückgibt. Möglicherweise nicht ausreichend - hängt davon ab, was Sie mit ihnen machen möchten.
Laryx Decidua
Wie lang ist der zurückgegebene String? Gibt es eine Möglichkeit, eine längere Zeichenfolge zurückzugeben?
Andrewrk
8
Bei hexadezimaler Darstellung beträgt die Länge einer MD5-Zeichenfolge immer 32 Zeichen. Wenn Sie eine Zeichenfolge mit der Länge 64 möchten, können Sie 2 MD5-Zeichenfolgen verketten: SELECT concat(md5(random()::text), md5(random()::text)); Und wenn Sie irgendwo in der Mitte möchten (z. B. 50 Zeichen), können Sie eine Teilzeichenfolge davon verwenden: SELECT substr(concat(md5(random()::text), md5(random()::text)), 0, 50);
Jimmie Tyrrell
2
Keine sehr gute Lösung für Sitzungs-IDs, nicht viel Zufälligkeit. Die Antwort ist auch 6 Jahre alt. Schauen Sie sich dies für eine völlig andere Methode angen_random_uuid() : schneller, zufälliger, effizienter in der Datenbank gespeichert.
Evan Carroll
@Evan, wenn Sie mehr 'Zufälligkeit' ohne eine Erweiterung wollen, können Sie SELECT md5(random()::text||random()::text);, oderSELECT md5(random()::text||random()::text||random()::text);
31

Aufbauend auf Marcins Lösung können Sie dazu ein beliebiges Alphabet verwenden (in diesem Fall alle 62 alphanumerischen ASCII-Zeichen):

SELECT array_to_string(array 
       ( 
              select substr('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', trunc(random() * 62)::integer + 1, 1)
              FROM   generate_series(1, 12)), '');
grourk
quelle
Langsam, nicht so zufällig oder so effizient zu speichern. Keine sehr gute Lösung für Sitzungs-IDs, nicht viel Zufälligkeit. Die Antwort ist auch 6 Jahre alt. Check out this for a totally different method using gen_random_uuid(): schneller, zufälliger, effizienter in der Datenbank gespeichert.
Evan Carroll
23

Sie können 128 zufällige Bits von einer UUID erhalten. Dies ist die Methode, um die Arbeit in modernem PostgreSQL zu erledigen.

CREATE EXTENSION pgcrypto;
SELECT gen_random_uuid();

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

Kann sein , auch lesenswert die Dokumentationen zu UUID

Der Datentyp uuid speichert UUID (Universally Unique Identifiers) gemäß RFC 4122, ISO / IEC 9834-8: 2005 und verwandten Standards. (Einige Systeme bezeichnen diesen Datentyp stattdessen als global eindeutige Kennung oder GUID.) Diese Kennung ist eine 128-Bit-Größe , die von einem Algorithmus generiert wird, der so ausgewählt wurde, dass es sehr unwahrscheinlich ist, dass dieselbe Kennung von jemand anderem generiert wird im bekannten Universum mit dem gleichen Algorithmus. Daher bieten diese Kennungen für verteilte Systeme eine bessere Eindeutigkeitsgarantie als Sequenzgeneratoren, die nur innerhalb einer einzelnen Datenbank eindeutig sind.

Wie selten ist eine Kollision mit UUID oder erraten? Angenommen, sie sind zufällig,

Etwa 100 Billionen UUIDs der Version 4 müssten generiert werden, um eine 1: 1-Milliarde-Chance auf ein einzelnes Duplikat zu haben ("Kollision"). Die Wahrscheinlichkeit einer Kollision steigt erst auf 50%, nachdem 261 UUIDs (2,3 x 10 ^ 18 oder 2,3 Billionen) generiert wurden. Wenn Sie diese Zahlen mit Datenbanken verknüpfen und die Frage prüfen, ob die Wahrscheinlichkeit einer UUID-Kollision der Version 4 vernachlässigbar ist, betrachten Sie eine Datei mit 2,3 Billionen UUIDs der Version 4 mit einer Wahrscheinlichkeit von 50%, eine UUID-Kollision zu enthalten. Es wäre 36 Exabyte groß, vorausgesetzt, es gibt keine anderen Daten oder Overheads, die tausendmal größer sind als die größten derzeit existierenden Datenbanken, die in der Größenordnung von Petabyte liegen. Bei einer Rate von 1 Milliarde UUIDs pro Sekunde würde es 73 Jahre dauern, die UUIDs für die Datei zu generieren. Es würde auch ungefähr 3 erfordern. 6 Millionen 10-Terabyte-Festplatten oder Bandkassetten zum Speichern, vorausgesetzt, es werden keine Backups oder Redundanzen erstellt. Das Lesen der Datei mit einer typischen "Disk-to-Buffer" -Übertragungsrate von 1 Gigabit pro Sekunde würde für einen einzelnen Prozessor über 3000 Jahre erfordern. Da die nicht behebbare Lesefehlerrate von Laufwerken bestenfalls 1 Bit pro 1018 Bit beträgt, während die Datei etwa 1020 Bit enthalten würde, würde ein einmaliges Lesen der Datei von Ende zu Ende zumindest zu etwa 100-mal mehr Fehlern führen. Lesen Sie UUIDs als Duplikate. Speicher-, Netzwerk-, Strom- und andere Hardware- und Softwarefehler sind zweifellos tausende Male häufiger als Probleme bei der UUID-Duplizierung. Eine Übertragungsrate von 1 Gigabit pro Sekunde würde für einen einzelnen Prozessor über 3000 Jahre erfordern. Da die nicht behebbare Lesefehlerrate von Laufwerken bestenfalls 1 Bit pro 1018 Bit beträgt, während die Datei etwa 1020 Bit enthalten würde, würde ein einmaliges Lesen der Datei von Ende zu Ende zumindest zu etwa 100-mal mehr Fehlern führen. Lesen Sie UUIDs als Duplikate. Speicher-, Netzwerk-, Strom- und andere Hardware- und Softwarefehler sind zweifellos tausende Male häufiger als Probleme bei der UUID-Duplizierung. Eine Übertragungsrate von 1 Gigabit pro Sekunde würde für einen einzelnen Prozessor über 3000 Jahre erfordern. Da die nicht behebbare Lesefehlerrate von Laufwerken bestenfalls 1 Bit pro 1018 Bit beträgt, während die Datei etwa 1020 Bit enthalten würde, würde ein einmaliges Lesen der Datei von Ende zu Ende zumindest zu etwa 100-mal mehr Fehlern führen. Lesen Sie UUIDs als Duplikate. Speicher-, Netzwerk-, Strom- und andere Hardware- und Softwarefehler sind zweifellos tausende Male häufiger als Probleme bei der UUID-Duplizierung.

Quelle: Wikipedia

Zusammenfassend,

  • UUID ist standardisiert.
  • gen_random_uuid()ist 128 zufällige Bits, die in 128 Bits gespeichert sind (2 ** 128 Kombinationen). 0-Abfall.
  • random() generiert in PostgreSQL nur 52 zufällige Bits (2 ** 52 Kombinationen).
  • md5()als UUID gespeichert ist 128 Bit, kann aber nur so zufällig sein wie seine Eingabe ( 52 Bit bei Verwendungrandom() )
  • md5()Als Text gespeichert sind 288 Bit, aber es kann nur so zufällig sein wie seine Eingabe ( 52 Bit bei Verwendungrandom() ) - mehr als doppelt so groß wie eine UUID und ein Bruchteil der Zufälligkeit)
  • md5() kann als Hash so optimiert werden, dass es nicht effektiv viel bewirkt.
  • UUID ist sehr effizient für die Speicherung: PostgreSQL bietet einen Typ mit genau 128 Bit. Im Gegensatz zu textund varcharusw., die als Over gespeichert werden und varlenafür die Länge der Zeichenfolge Overhead haben.
  • PostgreSQL nifty UUID enthält einige Standardoperatoren, Castings und Funktionen.
Evan Carroll
quelle
3
Teilweise falsch: Eine richtig generierte zufällige UUID hat nur 122 zufällige Bits, da 4 Bits für die Version und 2 Bits für die Variante verwendet werden: en.wikipedia.org/wiki/…
Olivier Grégoire
2
Wenn die Quelle nicht das tut, was dort geschrieben steht, ist sie keine UUID und sollte von PostgreSQL nicht als solche aufgerufen werden.
Olivier Grégoire
16

Ich habe kürzlich mit PostgreSQL gespielt und ich denke, ich habe eine etwas bessere Lösung gefunden, indem ich nur integrierte PostgreSQL-Methoden verwendet habe - kein pl / pgsql. Die einzige Einschränkung besteht darin, dass derzeit nur UPCASE-Zeichenfolgen oder -Zahlen oder Kleinbuchstaben generiert werden.

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

Das zweite Argument der generate_seriesMethode bestimmt die Länge der Zeichenfolge.

Marcin Raczkowski
quelle
8
Ich mag das, aber als ich es als UPDATE-Anweisung verwendete, wurden alle Zeilen auf dasselbe zufällige Passwort anstatt auf eindeutige Passwörter gesetzt. Ich habe dieses Problem gelöst, indem ich der Formel die Primärschlüssel-ID hinzugefügt habe. Ich addiere es zum Zufallswert und subtrahiere es erneut. Die Zufälligkeit wird nicht geändert, aber PostgreSQL wird dazu verleitet, die Werte für jede Zeile neu zu berechnen. Hier ist ein Beispiel mit dem Primärschlüsselnamen "my_id": array_to_string(ARRAY(SELECT chr((65 + round((random()+my_id-my) * 25)) :: integer) FROM generate_series(1,8)), '')
Mark Stosberg
Die von @MarkStosberg vorgestellte Lösung funktionierte wie gesagt, aber nicht wie erwartet. Die erzeugten Daten stimmten nicht mit dem vorgetäuschten Muster überein (nur Groß- und Kleinschreibung oder nur Ziffern). Ich habe durch arithmetische Modulation das zufällige Ergebnis behoben: array_to_string(ARRAY(SELECT chr((65 + round((random() * 25 + id) :: integer % 25 )) :: integer) FROM generate_series(1, 60)), '');
Nuno Rafael Figueiredo
4
Nein. Sie antworten auf die Frage "Wie generiere ich eine zufällige Sitzungs-ID ?" Und nicht auf "Wie generiere ich eine zufällige Zeichenfolge ? ". Sie haben die Bedeutung der Frage (und des Titels) basierend auf zwei Wörtern in der Beschreibung geändert. Sie beantworten eine andere Frage. und missbrauchen Sie weiterhin Ihre Moderationskraft, um die Bedeutung der Frage zu ändern.
Marcin Raczkowski
13

Bitte benutzen string_agg!

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

Ich verwende dies mit MD5, um auch eine UUID zu generieren. Ich möchte nur einen zufälligen Wert mit mehr Bits als einer random ()ganzen Zahl.

Andrew Wolfe
quelle
Ich nehme an, ich könnte einfach verketten, random()bis ich die gewünschte Anzahl von Bits erhalte. Naja.
Andrew Wolfe
11

Obwohl standardmäßig nicht aktiv, können Sie eine der Kernerweiterungen aktivieren:

CREATE EXTENSION IF NOT EXISTS pgcrypto;

Dann wird Ihre Anweisung zu einem einfachen Aufruf von gen_salt (), der eine zufällige Zeichenfolge generiert:

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

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

Die führende Nummer ist eine Hash-ID. Es stehen mehrere Algorithmen mit jeweils eigener Kennung zur Verfügung:

  • md5: $ 1 $
  • bf: $ 2a $ 06 $
  • des: keine Kennung
  • xdes: _J9 ..

Weitere Informationen zu Erweiterungen:


BEARBEITEN

Wie von Evan Carrol angegeben, können Sie ab Version 9.4 verwenden gen_random_uuid()

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

Jefferey Höhle
quelle
Die erzeugten Salze scheinen zu sequentiell zu sein, um wirklich zufällig zu sein, nicht wahr?
Le Droid
1
Beziehen Sie sich auf die $1$? Das ist eine Hash-Typ-ID (md5 == 1), der Rest ist der zufällige Wert.
Jefferey Cave
Ja, das war meine falsche Interpretation, danke für die Präzision.
Le Droid
6

Ich glaube nicht, dass Sie per se nach einer zufälligen Zeichenfolge suchen. Was Sie für die Sitzungsüberprüfung benötigen, ist eine Zeichenfolge, die garantiert eindeutig ist. Speichern Sie Informationen zur Sitzungsüberprüfung für die Überwachung? In diesem Fall muss die Zeichenfolge zwischen den Sitzungen eindeutig sein. Ich kenne zwei ziemlich einfache Ansätze:

  1. Verwenden Sie eine Sequenz. Gut für die Verwendung in einer einzelnen Datenbank.
  2. Verwenden Sie eine UUID. Universell einzigartig, also auch in verteilten Umgebungen gut.

UUIDs sind aufgrund ihres Generierungsalgorithmus garantiert eindeutig. Tatsächlich ist es äußerst unwahrscheinlich, dass Sie zu irgendeinem Zeitpunkt auf einem Computer zwei identische Zahlen generieren (beachten Sie, dass dies viel stärker ist als bei zufälligen Zeichenfolgen, die eine weitaus geringere Periodizität als UUIDs aufweisen).

Sie müssen die Erweiterung uuid-ossp laden, um UUIDs verwenden zu können. Rufen Sie nach der Installation eine der verfügbaren Funktionen von uuid_generate_vXXX () in Ihren SELECT-, INSERT- oder UPDATE-Aufrufen auf. Der UUID-Typ ist eine 16-Byte-Zahl, hat aber auch eine Zeichenfolgendarstellung.

Patrick
quelle
Dies scheint ein potenziell gefährlicher Rat zu sein. Wenn es um Sitzungsschlüssel geht, möchten Sie eine Eindeutigkeit und Zufälligkeit, die kryptografisch zufällig genug ist, um jede vernünftige Möglichkeit auszuschließen, sie zu erraten. Die von UUIDs verwendeten Algorithmen garantieren die Eindeutigkeit durch nicht zufällige (meistens) Mechanismen, die eine Sicherheitsbedrohung darstellen.
jmar777
6
@ jmar777 Der ganze Zweck von UUIDs ist, dass sie schwer zu erraten und sehr zufällig sind. Mit Ausnahme der Version v1 weisen sie eine sehr hohe Periodizität auf. v4 ist vollständig 128-Bit zufällig. Sie werden in jeder Online-Banking-Transaktion verwendet, die Sie durchführen. Wenn sie dafür gut genug sind, sind sie für so ziemlich alles andere gut genug.
Patrick
1
Nun, was weißt du? Ich wusste nicht, dass dies in Version 4 behoben wurde . Danke, dass du mich verbessert hast!
jmar777
@Patrick Small nit, V4-UUIDs sind 122 zufällige Bits, nicht 128 .;)
Jesse
5

Der Parameter INTEGER definiert die Länge der Zeichenfolge. Garantiert, dass alle 62 Alphanum-Zeichen mit gleicher Wahrscheinlichkeit abgedeckt werden (im Gegensatz zu einigen anderen Lösungen, die im Internet herumschwirren).

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;
Kehlkopf Decidua
quelle
Langsam, nicht so zufällig oder so effizient zu speichern. Keine sehr gute Lösung für Sitzungs-IDs, nicht viel Zufälligkeit. Die Antwort ist auch 6 Jahre alt. Check out this for a totally different method using gen_random_uuid(): schneller, zufälliger, effizienter in der Datenbank gespeichert.
Evan Carroll
3
@EvanCarroll: Um ehrlich zu sein, gen_random_uuid()erschien in Version 9.4, soweit ich das beurteilen kann, die am 18.12.2014 veröffentlicht wurde, mehr als ein Jahr nach der Antwort, die Sie abgelehnt haben. Zusätzlicher Nitpick: Die Antwort ist erst 3 1/2 Jahre alt :-) Aber Sie haben Recht, jetzt wo wir haben gen_random_uuid(), sollte dies verwendet werden. Daher werde ich Ihre Antwort positiv bewerten.
Laryx Decidua
5

@ Kavius ​​empfahl die Verwendung pgcrypto, aber stattdessen gen_salt, was ist mit gen_random_bytes? Und wie wäre es sha512statt md5?

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

Docs:

F.25.5. Zufallsdatenfunktionen

gen_random_bytes (count integer) gibt bytea zurück

Gibt die Anzahl der kryptografisch starken zufälligen Bytes zurück. Es können maximal 1024 Bytes gleichzeitig extrahiert werden. Dies soll verhindern, dass der Zufallsgeneratorpool entleert wird.

Jared Beck
quelle
4

select * from md5(to_char(random(), '0.9999999999999999'));

user516487
quelle
2
select encode(decode(md5(random()::text), 'hex')||decode(md5(random()::text), 'hex'), 'base64')
user457226
quelle
Ich ändere es, um den Schrägstrich und das Pluszeichen zu entfernen, die manchmal im Ergebnis erscheinen, und um auch ein Ergebnis in Großbuchstaben zu generieren ') || decodieren (md5 (random () :: text),' hex '),' base64 '), 0, 10),' / ',' A '),' + ',' Z '));
Seun Matt