如何缩短UUID V4而不使其变得不唯一/不可猜测

问题描述 投票:0回答:6

我必须生成唯一的 URL 部分,该部分将“不可猜测”并且“抵抗”暴力攻击。它还必须尽可能短:)并且所有生成的值必须具有相同的长度。我正在考虑使用 UUID V4,它可以由 32 个(没有连字符)十六进制字符

de305d5475b4431badb2eb6b9e546014
表示,但它有点太长了。所以我的问题是如何生成一些unqiue,可以用url字符表示,每个生成的值具有相同的length并且短于32个字符。 (在node.js或pgpsql中)

node.js postgresql cryptography probability uuid
6个回答
12
投票

v4()
将生成一个大数字,并将其转换为十六进制字符串。在 Node.js 中,您可以使用
Buffer
将字符串转换为更小的 Base64 编码:

import { v4 } from 'uuid';

function getRandomName() {
    const hexString = v4();
    console.log("hex:   ", hexString);
    
    // remove decoration
    const hexStringUndecorated = hexString.replace(/-/g, "");
    
    const base64String = Buffer
      .from(hexStringUndecorated, 'hex')
      .toString('base64')
    console.log("base64:", base64String);
    
    return base64String;
}

产生:

hex:    6fa1ca99-a92b-4d2a-aac2-7c7977119ebc
base64: b6HKmakr

hex:    bd23c8fd-0f62-49f4-9e51-8b5c97601a16
base64: vSPI/Q9i

5
投票

UUID v4 本身实际上并不能保证唯一性。两个随机生成的 UUID 发生冲突的可能性非常非常小。这就是为什么它们需要这么长——这减少了冲突的机会。 所以你可以让它更短,但你做得越短,它实际上就越有可能不是唯一的。 UUID v4 的长度为 128 位,因为这通常被认为“足够唯一”。


2
投票

short-uuid模块就是这样做的。

“生成标准 UUID 并将其转换为更短的或只是不同的格式,然后再转换回来。”

它接受自定义字符集(并提供一些)来来回转换 UUID。

您还可以对 uuid 进行 Base64 处理,将其缩短为 22。这是一个 playground


1
投票

这完全取决于它的可猜测性/独特性。

我的建议是生成 128 个随机位,然后使用 base36 对其进行编码。这会给你一个“好”的 URL,而且它是独一无二的,并且可能足够难以猜测。

如果你想要更短,可以使用base64,但base64需要包含两个非字母数字字符。


1
投票

这是一个相当古老的线程,但我想指出最重要的答案并没有产生它声称的结果。它实际上会生成约 32 个字符长的字符串,但示例声称有 8 个字符。如果您想要更多压缩,请使用此函数将 uuid 转换为基数 90。

使用Base64每3个字节需要4个字符,而Hex(Base16)每个字节需要2个字符。这意味着 Base64 的存储大小比十六进制好约 67%,但如果我们可以增加字符/字节比率,我们可以获得更好的压缩。因此,Base90 提供了稍微更多的压缩。

const hex = "0123456789abcdef";
const base90 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$%&'()*+-./:<=>?@[]^_`{|}~";

/**
 * Convers a Base16 (Hex) represented string to a Base 90 String.
 * @param {String} number Hex represented string
 * @returns A Base90 representation of the hex string
 */
function convertToBase90(number) {
    var i,
        divide,
        newlen,
        numberMap = {},
        fromBase = hex.length,
        toBase = base90.length,
        length = number.length,
        result = typeof number === "string" ? "" : [];

    for (i = 0; i < length; i++) {
        numberMap[i] = hex.indexOf(number[i]);
    }
    do {
        divide = 0;
        newlen = 0;
        for (i = 0; i < length; i++) {
            divide = divide * fromBase + numberMap[i];
            if (divide >= toBase) {
                numberMap[newlen++] = parseInt(divide / toBase, 10);
                divide = divide % toBase;
            } else if (newlen > 0) {
                numberMap[newlen++] = 0;
            }
        }
        length = newlen;
        result = base90.slice(divide, divide + 1).concat(result);
    } while (newlen !== 0);

    return result;
}

/**
 * Compresses a UUID String to base 90 resulting in a shorter UUID String
 * @param {String} uuid The UUID string to compress
 * @returns A compressed UUID String.
 */
function compressUUID(uuid) {
    uuid = uuid.replace(/-/g, "");
    return convertToBase90(uuid);
}

超过几百万个随机 uuid,这不会生成重复项和以下输出:

Lengths:
Avg: 19.959995 Max:  20 Min:  17

Examples:
Hex:      68f75ee7-deb6-4c5c-b315-3cc6bd7ca0fd
Base 90:  atP!.AcGJh1(eW]1LfAh

Hex:      91fb8316-f033-40d1-974d-20751b831c4e
Base 90:  ew-}Kv&nK?y@~xip5/0e

Hex:      4cb167ee-eb4b-4a76-90f2-6ced439d5ca5
Base 90:  7Ng]V/:0$PeS-K?!uTed

1
投票

UUID 长度为 36 个字符,如果您希望保存将其转换回来的功能并确保 URL 安全,则可以将其缩短为 22 个字符(约 30%)。

这里是base64 url安全字符串的纯节点解决方案:

type UUID = string;
type Base64UUID = string;

/**
 * Convert uuid to base64url
 *
 * @example in: `f32a91da-c799-4e13-aa17-8c4d9e0323c9` out: `8yqR2seZThOqF4xNngMjyQ`
 */
export function uuidToBase64(uuid: UUID): Base64UUID {
  return Buffer.from(uuid.replace(/-/g, ''), 'hex').toString('base64url');
}

/**
 * Convert base64url to uuid
 *
 * @example in: `8yqR2seZThOqF4xNngMjyQ` out: `f32a91da-c799-4e13-aa17-8c4d9e0323c9`
 */
export function base64toUUID(base64: Base64UUID): UUID {
  const hex = Buffer.from(base64, 'base64url').toString('hex');

  return `${hex.substring(0, 8)}-${hex.substring(8, 12)}-${hex.substring(
    12,
    16,
  )}-${hex.substring(16, 20)}-${hex.substring(20)}`;
}

测试:

import { randomUUID } from "crypto";

// f32a91da-c799-4e13-aa17-8c4d9e0323c9
const uuid = randomUUID();

// 8yqR2seZThOqF4xNngMjyQ
const base64 = uuidToBase64(uuid);

// f32a91da-c799-4e13-aa17-8c4d9e0323c9
const uuidFromBase64 = base64toUUID(base64); 
© www.soinside.com 2019 - 2024. All rights reserved.