UUID는 얼마나 독특합니까?


451

UUID를 사용하여 무언가를 고유하게 식별하는 것이 얼마나 안전합니까 (서버에 업로드 된 파일에 사용하고 있습니까)? 내가 이해하는 것처럼 난수를 기반으로합니다. 그러나 충분한 시간이 주어지면 결국 순수한 기회로 스스로 반복 할 것 같습니다. 이 문제를 완화하기 위해 더 나은 시스템이나 어떤 유형의 패턴이 있습니까?


11
"충분한 시간"의 충분히 큰 가치를 위해 :)

91
"UUID는 얼마나 독특합니까?" 보편적으로 유일하다고 생각합니다. ;)
Miles

29
그리고 Venus에서 개발할 계획이 없다면 GUID로 충분할 것입니다.
skaffman

1
자세한 내용 및 생성기 : 온라인 UUID 생성기
Dave

2
"고유 한"은 결코 충돌하지 않음을 의미 합니다. 충돌 가능성이있는 경우 고유하지 않습니다 . 따라서 정의상 UUID는 고유하지 않으며 충돌 가능성에 관계없이 잠재적 인 충돌에 대비 한 경우에만 안전합니다. 그렇지 않으면 프로그램이 잘못되었습니다. UUID를 "거의 독창적"이라고 말할 수 있지만 이것이 "고유하다"는 것은 아닙니다.
Eonil

답변:


446

매우 안전:

주어진 사람이 운석에 부딪히는 연간 위험은 170 억으로 1 번의 확률로 추정되는데, 이는 확률이 약 0.00000000006 (6 × 10-11 )이며 이는 수십 조의 UUID를 생성 할 확률과 같습니다. 1 년 안에 하나의 사본을 가지고 있습니다. 다시 말해, 향후 100 년 동안 초당 10 억 개의 UUID를 생성 한 후에 만 ​​하나의 복제본을 생성 할 확률은 약 50 %입니다.

경고:

그러나 이러한 확률은 충분한 엔트로피를 사용하여 UUID가 생성 된 경우에만 유지됩니다. 그렇지 않으면 통계 분산이 더 낮을 수 있으므로 복제 확률이 상당히 높아질 수 있습니다. 분산 응용 프로그램에 고유 식별자가 필요한 경우 많은 장치의 데이터를 병합하더라도 UUID가 충돌하지 않도록 모든 장치에 사용되는 시드 및 생성기의 임의성은 응용 프로그램 수명 동안 안정적이어야합니다. 이것이 가능하지 않은 경우 RFC4122는 네임 스페이스 변형을 대신 사용할 것을 권장합니다.

출처 : Wikipedia 기사에서 Universally 고유 식별자에 대한 중복 UUID 확률 섹션 (링크는 섹션을 재 작업하기 전에 2016 년 12 월부터 개정으로 이어짐)

또한 동일하게 고유 한 고유 식별자 기사 인 Collisions 의 동일한 주제에 대한 현재 섹션을 참조하십시오 .


22
Wikipedia에서이 부분이 마음에 듭니다. 그러나 이러한 확률은 충분한 엔트로피를 사용하여 UUID가 생성 된 경우에만 유지됩니다. 그렇지 않으면 통계 분산이 더 낮을 수 있으므로 복제 가능성이 상당히 높아질 수 있습니다. 따라서이 문장을 복제 할 수있는 실제 기회는 무엇입니까? 우리는 컴퓨터에서 실제 난수를 만들 수 없습니다.
mans

6
실제로, 많은 수의 엔트로피 ( "실제 임의성", 난 당신이 그것을 호출 할 것입니다)를 난수 API에 도입하는 방법을 찾는 데 많은 노력을 기울였습니다. 참조 en.wikipedia.org/wiki/Entropy_%28computing%29
broofa

4
그것은 실제로 내가 상상했던 것보다 더 높은 충돌 확률입니다. 생일 역설에 나는 추측한다.
Cameron

응용 프로그램 실행간에 UUID 사용이 안전하다는 것을 확인할 수 있습니까? (예 : 파이썬 스크립트)
George Sp

훌륭한 예 ... : D
NuttLoose

151

"충분히 주어진 시간"이 100 년을 의미하고 초당 10 억의 비율로 생성한다면 100 년 후에 50 %의 확률로 충돌 할 가능성이 있습니다.


185
그러나 해당 ID에 256 엑사 바이트의 스토리지를 사용한 후에 만 ​​가능합니다.
밥 아만

16
재밌는 것은, 물론, 우연히 일치하는 우연의 일치, 운 및 신의 개입 수준에서 동일한 2 행을 생성 할 수 있지만, 헤아릴 수없는 확률에도 불구하고 여전히 가능합니다! : D 그렇습니다. 복제본을 만들 때의 순간에 대한 생각의 즐거움을 말하는 것입니다! 스크린 샷 비디오!
scalabl3

4
독창성은 순전히 무작위성 때문입니까? 아니면 다른 요인이 있습니까? (예 : 타임 스탬프, IP 등)
Weishi Zeng

15
@TheTahaan 그것은 임의의 의미가 아닙니다. "전적으로 예측할 수 없음"을 의미하지는 않습니다. 일반적으로 어떤 종류의 분포를 따릅니다. 동전 10 개를 뒤집 으면 머리 2 개, 꼬리 3 개, 머리 5 개가 나올 확률이 매우 낮습니다 (2 ^ -10, 약 0.001). 실제로는 무작위이지만 특정 결과를 얻을 가능성 을 절대적으로 알 수 있습니다 . 우리는 그냥 여부를 사전에 말할 수없는 일어난다.
Richard Rast

5
이 구현이 무엇을 잘못했는지 설명하기 위해 타임 스탬프와 mac 주소의 조합에 의존하는 버전 1 UUID를 사용하고 있습니다. 그러나 UUID를 충분히 빠르게 생성하면 타임 스탬프가 아직 증가하지 않습니다. 이 시나리오에서 UUID 생성 알고리즘은 마지막으로 사용 된 타임 스탬프를 추적하여 1 씩 증가시켜야합니다. 그러나 단기간에 동일한 시스템에서 올바르게 생성 된 모든 버전 1 UUID는 명백한 유사성을 나타내지 만 여전히 고유해야합니다.
밥 아만

103

여러 유형의 UUID가 있으므로 "안전"은 사용중인 유형 (UUID 사양에서 "버전"이라고 함)에 따라 다릅니다.

  • 버전 1은 시간 기반 MAC 주소 UUID입니다. 128 비트에는 네트워크 카드의 MAC 주소 (제조업체에서 고유하게 지정)에 대한 48 비트와 100 나노초의 해상도를 가진 60 비트 클록이 포함됩니다. 이 시계 는 3603 AD로 포장 되므로 이러한 UUID는 적어도 그때까지 안전합니다 (초당 1,000 만 개 이상의 새로운 UUID가 필요하거나 누군가가 네트워크 카드를 복제하지 않는 한). 나는 시계가 1582 년 10 월 15 일에 시작하기 때문에 "적어도"라고 말한다. 그래서 당신은 작은 복제의 가능성이 있기 전에 당신이 시계가 감겨 진 후 약 400 년을 보낸다.

  • 버전 4는 난수 UUID입니다. 6 개의 고정 비트가 있으며 나머지 UUID는 122 비트의 임의성입니다. 복제 가능성이 매우 적은 위키 백과 또는 기타 분석을 참조하십시오 .

  • 버전 3은 MD5를 사용하고 버전 5는 임의 또는 의사 난수 생성기 대신 SHA-1을 사용하여 122 비트를 만듭니다. 따라서 안전성 측면에서 다이제스트 알고리즘이 처리하는 것이 항상 고유 한 한 버전 4는 통계 문제와 같습니다.

  • 버전 2는 버전 1과 유사하지만 시계가 더 작으므로 훨씬 빨리 랩핑됩니다. 그러나 버전 2 UUID는 DCE 용이므로이를 사용하지 않아야합니다.

따라서 모든 실제 문제에 대해 안전합니다. 확률에 도달하는 것이 불편한 경우 (예 : 당신의 지구에서 큰 소행성에 의해 지구가 파괴되는 것에 대해 걱정하는 사람의 유형 인 경우) 버전 1 UUID를 사용하고 고유해야합니다 ( 당신이 3603 AD를 지나서 살 계획이 없다면, 당신의 일생 동안.

그렇다면 왜 모두가 단순히 버전 1 UUID를 사용하지 않습니까? 버전 1 UUID는 생성 된 머신의 MAC 주소를 공개하고 예측할 수 있기 때문에 UUID를 사용하는 응용 프로그램에 보안에 영향을 줄 수있는 두 가지입니다.


1
버전 1로 기본 UUID를 설정하면 많은 사람들이 동일한 서버에서 생성 할 때 심각한 문제가 발생합니다. 버전 4 UUID는 모든 언어 또는 플랫폼 (자바 스크립트 포함)으로 빠르게 생성 할 수있는 무언가를 신속하게 작성할 수 있기 때문에 기본값입니다.
저스틴 Bozonier

1
@Hoylen 잘 설명했다! 그러나이 과장이 필요합니까?
Dinoop paloli

1
이론적으로 제조업체에서 고유하게 지정합니다.
OrangeDog

4
복제본을 만나기 위해 1 천만 개의 버전 1 UUID를 생성 할 필요는 없습니다. 시퀀스 번호를 오버플로하기 위해 단일 "틱"범위 내에서 16,384 개의 UUID 배치를 생성해야합니다. 나는 이것이 (1) μs 레벨의 입도를 가지며 (2) 단조로운 것으로 보장되지 않은 (시스템 클럭은 아닙니다) 클럭 소스에 의존하는 구현에서 이런 일이 발생하는 것을 보았습니다. 누구의 UUID 생성 코드를 사용하는지 주의 하고 특히 시간 기반 UUID 생성기에주의하십시오. 제대로 이해하기 어려우므로 사용하기 전에 테스트받아야 합니다.
Mike Strobel

생성 된 v4 UUID 간의 시간 범위가 더 많은 충돌 확률을 초래할 수 있습니까? 트래픽이 많은 애플리케이션에서는 수천 개의 UUID가 동시에 생성되고 동일한 수량의 UUID가 상대적으로 더 긴 시간 동안 생성 된 경우보다 충돌 가능성이 더 높다고 가정합니다.
Jonathan

18

이에 대한 대답은 UUID 버전에 크게 좌우 될 수 있습니다.

많은 UUID 생성기는 버전 4 난수를 사용합니다. 그러나 이들 중 다수는 Pseudo 난수 생성기를 사용하여 생성합니다.

UUID를 생성하는 데 짧은 기간의 시드가 잘못된 PRNG를 사용하면 전혀 안전하지 않다고 말할 수 있습니다.

따라서 알고리즘을 생성하는 데 사용 된 알고리즘만큼 안전합니다.

반대로,이 질문에 대한 답을 알고 있다면 버전 4 uuid가 사용하기에 안전해야한다고 생각합니다. 사실 나는 네트워크 블록 파일 시스템에서 블록을 식별하기 위해 그것을 사용하고 있으며 지금까지 충돌이 없었습니다.

필자의 경우, 사용중인 PRNG는 메르 센 트위스터이며 / dev / urandom을 포함한 여러 소스에서 나온 씨앗이 뿌리 내리는 방식에주의를 기울이고 있습니다. Mersenne twister의주기는 2 ^ 19937-1입니다. 반복되는 UUID를보기까지는 매우 오랜 시간이 걸릴 것입니다.


14

Wikipedia 에서 인용 :

따라서 누구나 UUID를 생성하고이를 사용하여 다른 사람이 의도하지 않게 식별자를 사용하지 않을 것이라는 확실한 확신을 가지고 무언가를 식별 할 수 있습니다.

실제로 실제로 얼마나 안전한지에 대해 아주 자세하게 설명합니다. 따라서 귀하의 질문에 대답하십시오 : 예, 충분히 안전합니다.


9

나는 다른 답변과 동의합니다. UUID는 거의 모든 실제적인 목적 1 , 그리고 확실히 당신의 목적을 위해 충분히 안전 합니다.

그러나 (가설 적으로) 그렇지 않다고 가정하십시오.

이 문제를 완화하기 위해 더 나은 시스템이나 어떤 유형의 패턴이 있습니까?

다음은 몇 가지 접근 방식입니다.

  1. 더 큰 UUID를 사용하십시오. 예를 들어, 대신 128 난수 비트의 사용 256 또는 512 또는 ... 당신은 유형 4 스타일에 추가하는 각 비트 UUID는 엔트로피의 신뢰할 수있는 원본이 있다고 가정, 반에 의해 충돌의 가능성을 줄일 수 2 .

  2. UUID를 생성하고 발행 한 각각의 UUID를 기록하는 중앙 집중식 또는 분산 서비스를 구축하십시오. 새로운 것을 생성 할 때마다 UUID가 이전에 발행 된 적이 없는지 확인합니다. 우리가 서비스를 운영하는 사람들이 절대적으로 신뢰할 수 있고 부패 할 수 없다고 가정한다면 그러한 서비스는 기술적으로 간단합니다. 불행히도, 특히 정부의 보안 조직이 간섭 할 가능성이있는 경우에는 그렇지 않습니다. 그래서,이 방법은 아마 비현실적이며, 할 수있다 3 현실 세계에서 불가능합니다.


1-UUID의 독창성이 자국의 수도에서 핵 미사일을 발사했는지 여부를 결정하면 많은 동료 시민들이 "확률이 매우 낮다"고 확신하지 못할 것입니다. 따라서 나의 "거의 거의"자격.

2-여기 당신에게 철학적 인 질문이 있습니다. 진정으로 임의의 것이 있습니까? 그렇지 않은 경우 어떻게 알 수 있습니까? 우리가 알고있는 우주는 시뮬레이션입니까? 물리 법칙을 수정하여 결과를 바꿀 수있는 신이 있습니까?

3-이 문제에 대한 연구 논문을 아는 사람이 있으면 의견을 말하십시오.


메소드 번호 2가 기본적으로 UUID 사용의 주요 목적을 무효화하고 그 시점에서 클래식 번호 ID를 사용할 수도 있음을 지적하고 싶습니다.
Petr Vnenk

동의하지 않습니다. 순차적으로 번호가 매겨진 ID의 결함은 너무 추측하기 쉽다는 것입니다. UUID를 추측하기 어려운 방식으로 방법 2를 구현할 수 있어야합니다.
Stephen C

8

UUID 체계는 일반적으로 의사 난수 요소뿐만 아니라 현재 시스템 시간과 네트워크 MAC 주소와 같은 사용 가능한 경우 종종 고유 한 하드웨어 ID를 사용합니다.

UUID를 사용하는 요점은 자신이 할 수있는 것보다 고유 한 ID를 제공하는 더 나은 작업을 수행 할 수 있다는 점입니다. 이것은 자신의 롤링이 아닌 타사 암호화 라이브러리를 사용하는 것과 동일한 근거입니다. 스스로하는 것이 더 재미있을 수 있지만, 그렇게하는 것은 일반적으로 덜 책임이 있습니다.


6

몇 년 동안 해왔습니다. 문제가 발생하지 마십시오.

나는 보통 모든 키와 수정 날짜 등을 포함하는 하나의 테이블을 갖도록 DB를 설정했습니다. 중복 키 문제가 발생하지 않았습니다.

유일한 단점은 일부 정보를 빨리 찾기 위해 쿼리를 작성할 때 키를 많이 복사하고 붙여 넣는 것입니다. 더 이상 ID를 기억하기가 쉽지 않습니다.


5

다음은 고유성을 테스트하기위한 테스트 스 니펫입니다. @ scalabl3 님의 댓글에서 영감을 받음

재밌는 것은, 물론, 우연히 일치하는 우연의 일치, 운 및 신의 개입 수준에서 동일한 2 행을 생성 할 수 있지만, 헤아릴 수없는 확률에도 불구하고 여전히 가능합니다! : D 그렇습니다. 복제본을 만들 때의 순간에 대한 생각의 즐거움을 위해 말하십시오! 스크린 샷 비디오! – scalabl3 10 월 20 일 15:19:11

운이 좋으면 확인란을 선택하면 현재 생성 된 ID 만 확인합니다. 히스토리 점검을 원하면 선택하지 않은 상태로 두십시오. 체크하지 않은 상태에서 어느 시점에서 램이 소진 될 수 있습니다. CPU를 친숙하게 만들어서 필요할 때 빨리 중단 할 수 있습니다. 스 니펫 실행 버튼을 다시 누르거나 페이지를 나가십시오.

Math.log2 = Math.log2 || function(n){ return Math.log(n) / Math.log(2); }
  Math.trueRandom = (function() {
  var crypt = window.crypto || window.msCrypto;

  if (crypt && crypt.getRandomValues) {
      // if we have a crypto library, use it
      var random = function(min, max) {
          var rval = 0;
          var range = max - min;
          if (range < 2) {
              return min;
          }

          var bits_needed = Math.ceil(Math.log2(range));
          if (bits_needed > 53) {
            throw new Exception("We cannot generate numbers larger than 53 bits.");
          }
          var bytes_needed = Math.ceil(bits_needed / 8);
          var mask = Math.pow(2, bits_needed) - 1;
          // 7776 -> (2^13 = 8192) -1 == 8191 or 0x00001111 11111111

          // Create byte array and fill with N random numbers
          var byteArray = new Uint8Array(bytes_needed);
          crypt.getRandomValues(byteArray);

          var p = (bytes_needed - 1) * 8;
          for(var i = 0; i < bytes_needed; i++ ) {
              rval += byteArray[i] * Math.pow(2, p);
              p -= 8;
          }

          // Use & to apply the mask and reduce the number of recursive lookups
          rval = rval & mask;

          if (rval >= range) {
              // Integer out of acceptable range
              return random(min, max);
          }
          // Return an integer that falls within the range
          return min + rval;
      }
      return function() {
          var r = random(0, 1000000000) / 1000000000;
          return r;
      };
  } else {
      // From http://baagoe.com/en/RandomMusings/javascript/
      // Johannes Baagøe <baagoe@baagoe.com>, 2010
      function Mash() {
          var n = 0xefc8249d;

          var mash = function(data) {
              data = data.toString();
              for (var i = 0; i < data.length; i++) {
                  n += data.charCodeAt(i);
                  var h = 0.02519603282416938 * n;
                  n = h >>> 0;
                  h -= n;
                  h *= n;
                  n = h >>> 0;
                  h -= n;
                  n += h * 0x100000000; // 2^32
              }
              return (n >>> 0) * 2.3283064365386963e-10; // 2^-32
          };

          mash.version = 'Mash 0.9';
          return mash;
      }

      // From http://baagoe.com/en/RandomMusings/javascript/
      function Alea() {
          return (function(args) {
              // Johannes Baagøe <baagoe@baagoe.com>, 2010
              var s0 = 0;
              var s1 = 0;
              var s2 = 0;
              var c = 1;

              if (args.length == 0) {
                  args = [+new Date()];
              }
              var mash = Mash();
              s0 = mash(' ');
              s1 = mash(' ');
              s2 = mash(' ');

              for (var i = 0; i < args.length; i++) {
                  s0 -= mash(args[i]);
                  if (s0 < 0) {
                      s0 += 1;
                  }
                  s1 -= mash(args[i]);
                  if (s1 < 0) {
                      s1 += 1;
                  }
                  s2 -= mash(args[i]);
                  if (s2 < 0) {
                      s2 += 1;
                  }
              }
              mash = null;

              var random = function() {
                  var t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32
                  s0 = s1;
                  s1 = s2;
                  return s2 = t - (c = t | 0);
              };
              random.uint32 = function() {
                  return random() * 0x100000000; // 2^32
              };
              random.fract53 = function() {
                  return random() +
                      (random() * 0x200000 | 0) * 1.1102230246251565e-16; // 2^-53
              };
              random.version = 'Alea 0.9';
              random.args = args;
              return random;

          }(Array.prototype.slice.call(arguments)));
      };
      return Alea();
  }
}());

Math.guid = function() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c)    {
      var r = Math.trueRandom() * 16 | 0,
          v = c == 'x' ? r : (r & 0x3 | 0x8);
      return v.toString(16);
  });
};
function logit(item1, item2) {
    console.log("Do "+item1+" and "+item2+" equal? "+(item1 == item2 ? "OMG! take a screenshot and you'll be epic on the world of cryptography, buy a lottery ticket now!":"No they do not. shame. no fame")+ ", runs: "+window.numberofRuns);
}
numberofRuns = 0;
function test() {
   window.numberofRuns++;
   var x = Math.guid();
   var y = Math.guid();
   var test = x == y || historyTest(x,y);

   logit(x,y);
   return test;

}
historyArr = [];
historyCount = 0;
function historyTest(item1, item2) {
    if(window.luckyDog) {
       return false;
    }
    for(var i = historyCount; i > -1; i--) {
        logit(item1,window.historyArr[i]);
        if(item1 == history[i]) {
            
            return true;
        }
        logit(item2,window.historyArr[i]);
        if(item2 == history[i]) {
            
            return true;
        }

    }
    window.historyArr.push(item1);
    window.historyArr.push(item2);
    window.historyCount+=2;
    return false;
}
luckyDog = false;
document.body.onload = function() {
document.getElementById('runit').onclick  = function() {
window.luckyDog = document.getElementById('lucky').checked;
var val = document.getElementById('input').value
if(val.trim() == '0') {
    var intervaltimer = window.setInterval(function() {
         var test = window.test();
         if(test) {
            window.clearInterval(intervaltimer);
         }
    },0);
}
else {
   var num = parseInt(val);
   if(num > 0) {
        var intervaltimer = window.setInterval(function() {
         var test = window.test();
         num--;
         if(num < 0 || test) {
    
         window.clearInterval(intervaltimer);
         }
    },0);
   }
}
};
};
Please input how often the calulation should run. set to 0 for forever. Check the checkbox if you feel lucky.<BR/>
<input type="text" value="0" id="input"><input type="checkbox" id="lucky"><button id="runit">Run</button><BR/>


RFC 4122 버전 1 (날짜-시간 및 MAC 주소) UUID를 사용해보십시오.
zaph

최근 크롬이 업데이트 될 때까지 매우 빨랐습니다. 나는 4 주 전에 같은 것을 알아 차렸다.
Tschallacka

3

1
여기에 링크 된 참조는 버전 1 UUID (컴퓨터 생성 등에 대한 정보를 id로 가져옴)에 대해 설명합니다. 대부분의 다른 답변은 버전 4에 대해 이야기합니다 (완전히 임의 임). 위에 링크 된 Wikipedia 기사 en.wikipedia.org/wiki/Universally_unique_identifier 는 다양한 종류의 UUID를 설명합니다.
kratenko

3

UUID4의 경우 길이가 360,000km 인 큐브 모양 상자에 모래 알갱이가있는만큼 ID가 거의 있도록합니다. 그것은 목성의 직경보다 ~ 2 1/2 배 긴 변을 가진 상자입니다.

내가 유닛을 엉망으로 만들 었는지 누군가가 말해 줄 수 있도록 노력하십시오.

  • 모래 알갱이의 부피 0.00947mm ^ 3 ( Guardian )
  • UUID4에는 122 개의 랜덤 비트가 있습니다-> 5.3e36 가능한 값 ( wikipedia )
  • 그 많은 모래 알갱이의 부피 = 5.0191e34 mm ^ 3 또는 5.0191e + 25m ^ 3
  • 해당 부피 = 3.69E8m 또는 369,000km 인 입방 상자의 측면 길이
  • 목성의 직경 : 139,820km (구글)

실제로 나는 이것이 100 % 포장이라고 가정합니다. 그래서 아마도 그 요소를 추가해야합니다!
분실
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.