PostgreSQL에서 세션 ID에 적합한 임의의 문자열을 어떻게 생성합니까?


101

PostgreSQL을 사용하여 세션 확인에 사용할 임의의 문자열을 만들고 싶습니다. 를 사용하여 난수를 얻을 수 있다는 것을 알고 SELECT random()있으므로을 시도 SELECT md5(random())했지만 작동하지 않습니다. 어떻게 할 수 있습니까?


다른 해결책은 여기에서 찾을 수 있습니다. stackoverflow.com/a/13675441/398670
Craig Ringer

7
나는 기존 답변이 여전히 완벽하게 이해되도록 제목을 편집했으며 Evan의 답변도 좀 더 현대적으로 적합합니다. 콘텐츠 분쟁에 대해이 오래된 질문을 잠그고 싶지 않습니다 . 모든 답변을 수용 할 수있는 추가 수정을하겠습니다 .
Tim Post

1
멋지다. @gersh가 원래 의도에 대해 정당한 의견이 일치하지 않기 때문에이 질문을 명확히 할 수 있는지 살펴 보자. 그의 원래 의도가 내가 생각했던 것이라면 이러한 답변 중 많은 부분을 조정하거나, 반대 투표하거나 철회해야합니다. 그리고 테스트 목적 (또는 이와 유사한 것)을위한 문자열 생성에 대한 새로운 질문이 제기되어야합니다 ( random()ness가 필요하지 않은 경우). 내가 가정하는 것이 아니라면, 내 대답은 정제 된 질문에 맞춰져야합니다.
Evan Carroll

5
@EvanCarroll - gersh 지난 11월 21일 2015 년 보였다
BSMP

5
2017 년 에이 질문에 답하는 사람은 질문이 원래 요청되고 답변되었을 때 사용할 수 없었던 방법을 사용하므로 Evan의 답변 stackoverflow.com/a/41608000/190234 를 고려 하십시오.
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 2014

사용하는 다른 솔루션 을 찾았습니다 ORDER BY random(). 어느 것이 더 빠릅니까?
ma11hew28 2014

1
random이 CSPRNG가 아닌 erand48을 사용할 수 있다는 점에 주목할 가치가 있습니다. pgcrypto를 사용하는 것이 더 좋습니다.
Yaur

2
보안 난수 생성기를 사용하지 않으므로 세션 ID에 적합하지 않다는 점을 제외하면 좋은 대답입니다. 참조 : stackoverflow.com/questions/9816114/…
sudo

240

다음과 같이 초기 시도를 수정할 수 있습니다.

SELECT md5(random()::text);

다른 제안보다 훨씬 간단합니다. :-)


16
이렇게하면 '16 진수 알파벳 '{0..9, a..f} 이상의 문자열 만 반환됩니다. 충분하지 않을 수 있습니다-당신이 그들과 함께하고 싶은 것에 달려 있습니다.
Laryx Decidua 2012

반환 된 문자열의 길이는 얼마입니까? 더 긴 문자열을 반환하는 방법이 있습니까?
andrewrk 2014-06-26

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);
Jimmie Tyrrell 2014 년

2
세션 ID에 대한 그다지 좋은 솔루션은 아니며 무작위성이 아닙니다. 답도 6 살입니다. 더 빠르고 더 무작위 적이며 더 효율적으로 데이터베이스에 저장 하는 완전히 다른 방법을 확인하십시오gen_random_uuid() .
Evan Carroll

@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(): 더 빠르고, 더 무작위 적이며, 더 효율적으로 데이터베이스에 저장됩니다.
Evan Carroll

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 및 관련 표준에 정의 된 대로 UUID (Universally Unique Identifier)를 저장 합니다. (일부 시스템에서는이 데이터 유형을 전역 고유 식별자 또는 GUID라고합니다.)이 식별자는 다른 사람이 동일한 식별자를 생성 할 가능성이 거의 없도록 선택한 알고리즘에 의해 생성되는 128 비트 수량 입니다. 동일한 알고리즘을 사용하여 알려진 우주에서. 따라서 분산 시스템의 경우 이러한 식별자는 단일 데이터베이스 내에서만 고유 한 시퀀스 생성기보다 더 나은 고유성을 보장합니다.

UUID와의 충돌은 얼마나 드물거나 추측 할 수 있습니까? 무작위라고 가정하면

단일 복제 ( "충돌")의 10 억분의 1의 기회를 가지려면 약 100 조 개의 버전 4 UUID를 생성해야합니다. 한 번의 충돌 가능성은 261 개의 UUID (2.3 x 10 ^ 18 또는 2.3 quintillion)가 생성 된 후에 만 ​​50 %로 증가합니다. 이 숫자를 데이터베이스와 연결하고 버전 4 UUID 충돌 가능성이 무시할 수 있는지 여부를 고려할 때, UUID 충돌을 포함 할 확률이 50 % 인 2.3 quintillion 버전 4 UUID가 포함 된 파일을 고려하십시오. 다른 데이터 나 오버 헤드가 없다고 가정하면 크기가 36 엑사 바이트가되며, 현재 존재하는 가장 큰 데이터베이스 (약 페타 바이트)보다 수천 배 더 큽니다. 초당 10 억 UUID가 생성되는 속도로 파일에 대한 UUID를 생성하는 데 73 년이 걸립니다. 또한 약 3 개가 필요합니다. 6 백만 개의 10 테라 바이트 하드 드라이브 또는 테이프 카트리지로 백업이나 중복성이 없다고 가정합니다. 초당 1 기가비트의 일반적인 "디스크 대 버퍼"전송 속도로 파일을 읽으려면 단일 프로세서에 3000 년 이상이 필요합니다. 드라이브의 복구 할 수없는 읽기 오류율은 읽기 1018 비트 당 1 비트이고 파일에는 약 1020 비트가 포함되어 있기 때문에 파일을 끝에서 끝까지 한 번 읽는 것만으로도 적어도 약 100 배 더 많은 오류가 발생합니다. 중복보다 UUID를 읽습니다. 스토리지, 네트워크, 전원 및 기타 하드웨어 및 소프트웨어 오류는 의심 할 여지없이 UUID 복제 문제보다 수천 배 더 자주 발생합니다. 초당 1 기가비트의 전송 속도는 단일 프로세서에 3000 년 이상이 필요합니다. 드라이브의 복구 할 수없는 읽기 오류율은 읽기 1018 비트 당 1 비트이고 파일에는 약 1020 비트가 포함되어 있기 때문에 파일을 끝에서 끝까지 한 번 읽는 것만으로도 적어도 약 100 배 더 많은 오류가 발생합니다. 중복보다 UUID를 읽습니다. 스토리지, 네트워크, 전원 및 기타 하드웨어 및 소프트웨어 오류는 의심 할 여지없이 UUID 복제 문제보다 수천 배 더 자주 발생합니다. 초당 1 기가비트의 전송 속도는 단일 프로세서에 3000 년 이상이 필요합니다. 드라이브의 복구 할 수없는 읽기 오류율은 읽기 1018 비트 당 1 비트이고 파일에는 약 1020 비트가 포함되어 있기 때문에 파일을 끝에서 끝까지 한 번 읽는 것만으로도 적어도 약 100 배 더 많은 오류가 발생합니다. 중복보다 UUID를 읽습니다. 스토리지, 네트워크, 전원 및 기타 하드웨어 및 소프트웨어 오류는 의심 할 여지없이 UUID 복제 문제보다 수천 배 더 자주 발생합니다.

출처 : wikipedia

요약해서 말하자면,

  • UUID가 표준화되었습니다.
  • gen_random_uuid()128 비트 (2 ** 128 개 조합)에 저장된 128 비트 랜덤입니다. 0- 폐기물.
  • random() PostgreSQL에서는 52 비트 만 무작위로 생성됩니다 (2 ** 52 조합).
  • md5()UUID로 저장되는 것은 128 비트이지만 입력만큼만 임의적 일 수 있습니다 ( 사용하는 경우 52 비트random() ).
  • md5()텍스트로 저장되는 것은 288 비트이지만 입력만큼만 임의적 일 수 있습니다 ( 사용하는 경우 52 비트random() )-UUID 크기의 두 배와 임의성의 일부 이상)
  • md5() 해시로 최적화되어 효과적으로 많은 작업을 수행하지 않습니다.
  • UUID는 저장소에 매우 효율적입니다. PostgreSQL은 정확히 128 비트 유형을 제공합니다. 달리 textvarchar등, A와 저장되는 varlena문자열의 길이의 오버 헤드 갖는다.
  • PostgreSQL 멋진 UUID에는 기본 연산자, 캐스팅 및 기능이 함께 제공됩니다.

3
: 구름 잘못된 : 4 비트는 상기 변이체에 대한 버전 2 개 비트를 위해 사용되기 때문에 적절하게 생성 된 랜덤 UUID은 랜덤 122 비트가 en.wikipedia.org/wiki/...
올리비에 그레

2
소스가 거기에 쓰여진 것을 수행하지 않으면 UUID가 아니며 PostgreSQL에서 그렇게 호출해서는 안됩니다.
Olivier Grégoire

16

나는 최근에 PostgreSQL을 가지고 놀았고 pl / pgsql이 아닌 내장 PostgreSQL 메서드 만 사용하여 조금 더 나은 솔루션을 찾은 것 같습니다. 유일한 제한은 현재 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 문을 사용했을 때 모든 행이 고유 암호 대신 동일한 임의의 암호로 설정되었음을 발견했습니다. 수식에 기본 키 ID를 추가하여이 문제를 해결했습니다. 나는 그것을 임의의 값에 더하고 다시 뺍니다. 임의성은 변경되지 않지만 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
아니요 . ' 무작위 문자열을 생성하는 방법'이 아니라 ' 무작위 세션 ID를 생성하는 방법'에 대답하고 있습니다. 설명의 두 단어를 기반으로 질문 (및 제목)의 의미를 변경했습니다. 당신은 다른 질문에 대답하고 있습니다. 계속해서 중재 권한을 남용하여 질문의 의미를 바꾸십시오.
Marcin Raczkowski

13

이용하십시오 string_agg!

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

MD5와 함께 이것을 사용하여 UUID도 생성하고 있습니다. random ()정수 보다 비트가 더 많은 임의의 값을 원합니다 .


random()원하는 비트 수를 얻을 때까지 연결 하면됩니다. 오 잘.
앤드류 울프

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

나는 당신이 그 자체로 임의의 문자열을 찾고 있다고 생각하지 않습니다. 세션 확인에 필요한 것은 고유함이 보장되는 문자열입니다. 감사를 위해 세션 확인 정보를 저장합니까? 이 경우 세션간에 고유 한 문자열이 필요합니다. 나는 두 가지 간단한 접근 방식을 알고 있습니다.

  1. 시퀀스를 사용하십시오. 단일 데이터베이스에서 사용하기에 좋습니다.
  2. UUID를 사용하십시오. 보편적으로 고유하므로 분산 환경에서도 좋습니다.

UUID는 생성 알고리즘 덕분에 고유 한 것으로 보장 됩니다. 사실상 어떤 컴퓨터에서든 항상 두 개의 동일한 숫자를 생성 할 가능성 은 극히 낮습니다 (UUID보다 주기성이 훨씬 작은 임의 문자열보다 훨씬 강력합니다).

UUID를 사용하려면 uuid-ossp 확장을로드해야합니다. 설치가 끝나면 SELECT, INSERT 또는 UPDATE 호출에서 사용 가능한 uuid_generate_vXXX () 함수를 호출하십시오. uuid 유형은 16 바이트 숫자이지만 문자열 표현도 있습니다.


이것은 잠재적으로 위험한 조언처럼 보입니다. 세션 키 와 관련하여 합리적인 추측 가능성을 배제 할 수있을만큼 충분히 암호 학적으로 임의적 인 고유성 임의성을 원합니다 . UUID에서 사용하는 알고리즘은 보안 위협을 야기하는 비 무작위 (대부분) 메커니즘으로 고유성을 보장합니다.
jmar777

6
@ jmar777 UUID의 전체 목적은 추측하기 어렵고 매우 무작위 적이라는 것입니다. v1 버전을 제외하고는 주기성이 매우 높습니다. v4는 완전 128 비트 랜덤입니다. 그들은 당신이하는 모든 온라인 뱅킹 거래에서 사용되고 있습니다. 그것들이 그것에 대해 충분히 좋다면 그들은 거의 다른 것에 대해 충분히 좋다.
Patrick

1
글쎄, 당신은 무엇을 알고 있습니다. 나는 그것이 버전 4 에서 다루어 졌다는 것을 몰랐다 . 수정 해 주셔서 감사합니다!
jmar777

@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(): 더 빠르고, 더 무작위 적이며, 더 효율적으로 데이터베이스에 저장됩니다.
Evan Carroll

3
@EvanCarroll : 공정하게 gen_random_uuid()말하면, 제가 아는 한 버전 9.4에 나타났습니다 . 2014 년 12 월 18 일에 릴리스 된 것은 귀하가 반대표를 던진 지 1 년이 지난 후입니다. 추가 nitpick : 대답은 겨우 3 년 반입니다. :-)하지만 당신이 맞아요, 이제 우리는 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');

문서 :

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), 'hex) ') || decode (md5 (random () :: text),'hex '),'base64 '), 0, 10),'/ ','A '),'+ ','Z '));
세운 매트
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.