PostgreSQLのセッションIDに適したランダムな文字列をどのように作成しますか?


101

PostgreSQLを使用したセッション検証で使用するランダムな文字列を作成したいと思います。で乱数を取得できることがわかっているSELECT random()ので、を試しましたSELECT md5(random())が、うまくいきません。これどうやってするの?


別の解決策がこちらにありますstackoverflow.com/a/13675441/398670
Craig Ringer

7
タイトルを編集して、既存の回答が完全に意味をなさないようにしました。エヴァンの回答は、よりモダンなものにも似ています。私は、コンテンツの論争のためにこの古くからある質問をロックしたくないので、すべての回答に対応する追加の編集を行ってみましょう。
Tim Post

1
クール、@ gershがこの質問を明確にできるかどうか見てみましょう。元の意図に関して正当な意見の相違があるためです。彼の当初の意図が私がそうであったと私が想定しているものである場合、これらの回答の多くは調整、反対投票、または撤回する必要があります。そして、おそらく、テスト目的などで文字列を生成することに関する新しい質問が提起されるべきです(random()必要性がない場合)。それが私が想定するものではない場合、私の答えは、代わりに洗練された質問に応える必要があります。
エヴァンキャロル

5
@EvanCarroll - gershは、最後の11月21日、2015年に見られた
BSMP

5
2017年にこの質問をしている人は、質問を最初に尋ねて答えたときに利用できなかった方法を使用しているため、2017年> Evanの回答stackoverflow.com/a/41608000/190234を検討してください。
Marcin Raczkowski、2017年

回答:


83

私はこの簡単な解決策を提案します:

これは、指定された長さのランダムな文字列を返す非常に単純な関数です。

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 2014

を使用する別の解決策を見つけましたORDER BY random()。どちらが速いですか?
ma11hew28 14

1
ランダムではCSPRNGではないerand48が使用される可能性があることに注意してください。おそらくpgcryptoを使用するほうがよいでしょう。
Yaur

2
安全な乱数ジェネレータを使用しないため、セッションIDには適さないことを除いて、良い答えです。参照:stackoverflow.com/questions/9816114/...
sudoの

239

次のように最初の試行を修正できます。

SELECT md5(random()::text);

他のいくつかの提案よりもはるかに単純です。:-)


16
これは、「16進数のアルファベット」{0..9、a..f}の文字列のみを返すことに注意してください。十分ではないかもしれません-それらで何をしたいかによります。
Laryx Decidua

返される文字列の長さは?より長い文字列を返すようにする方法はありますか?
andrewrk 14年

8
16進数で表す場合、MD5文字列の長さは常に32文字です。あなたは長さ64の文字列を望んでいた場合は、2つのMD5文字列を連結できます。 SELECT concat(md5(random()::text), md5(random()::text)); そして、あなたが途中でどこかに(例えば50文字)を望んでいた場合は、そのサブ取ることができる: SELECT substr(concat(md5(random()::text), md5(random()::text)), 0, 50);
ジミー・ティレル

2
セッションIDにはあまり良い解決策ではなく、あまりランダムではありません。答えも6歳です。これを使ってgen_random_uuid()、より速く、よりランダムに、より効率的にデータベースに保存される、まったく異なる方法を確認してください
エヴァンキャロル

@Evan拡張機能なしでより「ランダム性」が必要な場合SELECT md5(random()::text||random()::text);、またはSELECT md5(random()::text||random()::text||random()::text);

31

Marcinのソリューションに基づいて、任意のアルファベット(この場合は62文字のASCII英数字)を使用することができます。

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

遅い、ランダムではない、または保存するのに効率的ではない。セッションIDにはあまり良い解決策ではなく、あまりランダムではありません。答えも6歳です。Check out this for a totally different method using gen_random_uuid():より速く、よりランダムに、より効率的にデータベースに保存されます。
エヴァンキャロル

23

UUIDから128ビットのランダムを取得できます。これは、最新のPostgreSQLで作業を行うための方法です。

CREATE EXTENSION pgcrypto;
SELECT gen_random_uuid();

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

UUIDに関するドキュメントも読む価値あるかもしれません

データ型uuidは、RFC 4122、ISO / IEC 9834-8:2005、および関連する規格で定義されている Universally Unique Identifier(UUID)を格納します。(一部のシステムでは、このデータ型をグローバル一意識別子(GUID)と呼びます。)この識別子は、同じ識別子が他の誰かによって生成される可能性を非常に低くするように選択されたアルゴリズムによって生成される128ビットの数量です。同じアルゴリズムを使用して既知の宇宙で。したがって、分散システムの場合、これらの識別子は、単一のデータベース内でのみ一意であるシーケンスジェネレーターよりも優れた一意性を保証します。

UUIDとの衝突はどれくらいまれですか?それらがランダムであると仮定すると、

単一の重複(「衝突」)の可能性が10億分の1になるには、約100兆のバージョン4 UUIDを生成する必要があります。1回の衝突の可能性は、261 UUID(2.3 x 10 ^ 18または2.3 quintillion)が生成された後にのみ50%に上昇します。これらの数値をデータベースに関連付け、バージョン4 UUIDの衝突の可能性が無視できるかどうかの問題を考慮して、50%の確率で1つのUUID衝突が含まれる2.3キロバイトのバージョン4 UUIDを含むファイルを検討してください。他のデータやオーバーヘッドがないと仮定すると、サイズは36エクサバイトになり、現在存在する最大のデータベース(ペタバイト程度)の数千倍になります。1秒あたり10億個のUUIDが生成されるとすると、ファイルのUUIDを生成するのに73年かかります。また、約3が必要です。バックアップや冗長性がないと仮定して、600万個の10テラバイトのハードドライブまたはテープカートリッジを保存します。1ギガビット/秒の一般的な「ディスクからバッファ」への転送速度でファイルを読み取るには、単一のプロセッサで3000年以上必要です。ドライブの回復不可能な読み取りエラー率は、1018ビットの読み取りあたり最大1ビットであるため、ファイルには約1020ビットが含まれますが、ファイルを端から端まで一度だけ読み取ると、少なくとも約100倍多くのミスが発生します。重複よりUUIDを読み取ります。ストレージ、ネットワーク、電源、およびその他のハードウェアとソフトウェアのエラーは、間違いなくUUIDの複製の問題より数千倍も頻繁に発生します。1ギガビット/秒の転送速度では、1つのプロセッサで3000年以上必要です。ドライブの回復不可能な読み取りエラー率は、1018ビットの読み取りあたり最大1ビットであるため、ファイルには約1020ビットが含まれますが、ファイルを端から端まで一度だけ読み取ると、少なくとも約100倍多くのミスが発生します。重複よりUUIDを読み取ります。ストレージ、ネットワーク、電源、およびその他のハードウェアとソフトウェアのエラーは、間違いなくUUIDの複製の問題より数千倍も頻繁に発生します。1ギガビット/秒の転送速度では、1つのプロセッサで3000年以上必要です。ドライブの回復不可能な読み取りエラー率は、1018ビットの読み取りあたり最大1ビットであるため、ファイルには約1020ビットが含まれますが、ファイルを端から端まで一度だけ読み取ると、少なくとも約100倍多くのミスが発生します。重複よりUUIDを読み取ります。ストレージ、ネットワーク、電源、およびその他のハードウェアとソフトウェアのエラーは、間違いなくUUIDの複製の問題より数千倍も頻繁に発生します。

ソース:ウィキペディア

要約すれば、

  • UUIDは標準化されています。
  • gen_random_uuid()128ビット(2 ** 128の組み合わせ)に格納された128ビットのランダムです。0-廃棄物。
  • random() PostgreSQLで52ビットのランダム(2 ** 52の組み合わせ)のみを生成します。
  • md5()UUIDとして保存されるのは128ビットですが、入力と同じくらいランダムにすることができます(を使用する場合は52ビットrandom()
  • md5()テキストとして保存されるのは288ビットですが、入力と同じくらいランダムにしか使用できません(使用している場合は52ビットrandom())-UUIDのサイズの2倍以上、ランダム性の一部)
  • md5() ハッシュとしては、最適化できるため、あまり効果がありません。
  • UUIDはストレージに非常に効率的です。PostgreSQLは、正確に128ビットの型を提供します。文字列の長さのオーバーヘッドを持つとして格納されるtextand とは異なります。varcharvarlena
  • PostgreSQL nifty UUIDには、デフォルトの演算子、キャスティング、機能がいくつか付属しています。

3
:部分的に誤った:4ビットはバリアントのバージョンと2ビットのために使用されるので、A正しく生成されたランダムUUIDは、122ランダムビットを有するen.wikipedia.org/wiki/...
オリヴィエグレゴワール

2
ソースがそこに書かれていることを行わない場合、それはUUIDではなく、PostgreSQLによってそのように呼び出されるべきではありません。
OlivierGrégoire2017年

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メソッドの2番目の引数は、文字列の長さを指定します。


8
私はこれが好きですが、UPDATEステートメントを使用すると、すべての行が一意のパスワードではなく同じランダムパスワードに設定されていました。これを解決するには、数式に主キーIDを追加します。私はそれをランダムな値に追加し、再度減算します。ランダム性は変更されませんが、PostgreSQLはだまされて各行の値を再計算します。:ここでは「my_id」の主キー名を使用して、例を示します array_to_string(ARRAY(SELECT chr((65 + round((random()+my_id-my) * 25)) :: integer) FROM generate_series(1,8)), '')
マーク・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
いいえ。「ランダムな文字列を生成する方法」ではなく、「ランダムなセッションIDを生成する方法」に回答しています。説明の2つの単語に基づいて、質問(およびタイトル)の意味を変更しました。別の質問に答えています。そして、質問の意味を変えるために節度を乱用し続けます。
Marcin Raczkowski、2017年

13

ぜひご利用くださいstring_agg

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

これをMD5で使用してUUIDも生成しています。random ()整数よりもビット数の多いランダムな値が必要です。


random()必要なビット数が得られるまで連結できると思います。しかたがない。
Andrew Wolfe

11

デフォルトではアクティブではありませんが、コア拡張の1つをアクティブにすることができます。

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 2013年

1
を参照してい$1$ますか?これはハッシュ型の識別子(md5 == 1)で、残りはランダム化された値です。
Jefferey Cave 2013

はい、それは私の正確な解釈のおかげで私の間違った解釈でした。
Le Droid 2013年

6

それ自体がランダムな文字列を探しているとは思いません。セッション検証に必要なのは、一意であることが保証されている文字列です。監査用のセッション検証情報を保存していますか?その場合、セッション間で一意の文字列が必要です。私は2つのかなり単純なアプローチを知っています。

  1. シーケンスを使用します。単一のデータベースでの使用に適しています。
  2. UUIDを使用します。普遍的にユニークなので、分散環境にも適しています。

UUIDは、生成アルゴリズムによって一意であることが保証されています。事実上非常にあなたは、任意の時点で、これまでに(これはUUIDに比べてはるかに小さい周期性を持つランダムな文字列、上よりもはるかに強力であることに注意)を任意のマシン上に2つの同一の番号を生成する可能性は低いです。

UUIDを使用するには、uuid-ossp拡張機能をロードする必要があります。インストールしたら、SELECT、INSERT、またはUPDATE呼び出しで使用可能なuuid_generate_vXXX()関数のいずれかを呼び出します。uuidタイプは16バイトの数値ですが、文字列表現も持っています。


これは潜在的に危険なアドバイスのようです。セッションキーに関しては、それを推測する合理的な可能性を排除するために、暗号的にランダムな一意性ランダム性が必要です。UUIDが使用するアルゴリズムは、ランダムでない(主に)メカニズムによる一意性を保証します。これは、セキュリティ上の脅威となります。
jmar777 2015

6
@ jmar777 UUIDの全体的な目的は、推測が難しく、非常にランダムであることです。v1バージョンを除いて、非常に高い周期性があります。v4は完全に128ビットのランダムです。彼らはあなたが行うすべてのオンラインバンキングトランザクションで使用されています。それらがそれで十分なら、それらは他のほとんどすべてのもので十分です。
Patrick

1
さて、あなたは何を知っていますか?それがバージョン4で対処されていることに気づきませんでした。私を訂正してくれてありがとう!
jmar777 2015

@Patrick Small nit、V4 UUIDは128ビットではなく122ビットのランダムです。;)
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;

遅い、ランダムではない、または保存するのに効率的ではない。セッションIDにはあまり良い解決策ではなく、あまりランダムではありません。答えも6歳です。Check out this for a totally different method using gen_random_uuid():より速く、よりランダムに、より効率的にデータベースに保存されます。
エヴァンキャロル

3
@EvanCarroll:公平にgen_random_uuid()言えば、私が知る限り、バージョン9.4 に登場しました。これは2014-12-18にリリースされたもので、あなたが反対票を投じてから1年以上経過しています。追加のnitpick:答えはほんの3 1/2歳です:-)しかし、あなたが正しい、これが私たちが持っているのでgen_random_uuid()、これが使用されるべきものです。したがって、私はあなたの答えを賛成します。
Laryx Decidua 2017年

5

@Kaviusはの使用を推奨しましたpgcryptoが、の代わりにgen_saltどうgen_random_bytesですか?そして、sha512代わりにmd5どうですか?

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

ドキュメント:

F.25.5。ランダムデータ関数

gen_random_bytes(count integer)はbyteaを返します

暗号的に強力なランダムバイトのカウントを返します。一度に最大1024バイトを抽出できます。これは、乱数発生器プールのドレインを回避するためです。



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

結果に時々現れるスラッシュとプラス記号を削除し、大文字の結果を生成するように修正します。select upper(replace(replace(substring(encode(decode(md5(random():: text)) ')|| decode(md5(random():: text)、' hex ')、' base64 ')、0、10)、' / '、' A ')、' + '、' Z '));
Seun Matt
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.