You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在MySQL中查询哈希后的Discord ID并返回自定义名称

解决方案:带盐哈希可查询的Discord ID存储方案

针对你的需求——既要安全哈希Discord ID防彩虹表,又能快速查询无需全表遍历,结合MySQL 5.7、discord.js v14和Sequelize v6,提供以下三种可行方案,按推荐优先级排序:

方案1:固定盐+迭代哈希(适合小数据集)

你的数据集只有300条,固定盐的安全性风险可控,同时支持直接SELECT WHERE查询。

实现步骤:

  1. 数据库表结构(Sequelize模型定义):
const { Model, DataTypes } = require('sequelize');
const crypto = require('crypto');

class UserMapping extends Model {}
UserMapping.init({
  customName: {
    type: DataTypes.STRING,
    allowNull: false
  },
  hashedDiscordId: {
    type: DataTypes.STRING(64), // SHA-256输出长度
    allowNull: false,
    unique: true
  }
}, {
  sequelize,
  modelName: 'UserMapping'
});
  1. 哈希生成逻辑(固定盐+迭代哈希,增强抗彩虹表能力):
// 固定盐(存于环境变量,不要硬编码)
const FIXED_SALT = process.env.HASH_SALT;
const ITERATIONS = 10000;
const KEY_LENGTH = 32;

function hashDiscordId(discordId) {
  return crypto.pbkdf2Sync(discordId, FIXED_SALT, ITERATIONS, KEY_LENGTH, 'sha256').toString('hex');
}
  1. 查询逻辑(discord.js命令示例):
const { SlashCommandBuilder } = require('discord.js');

module.exports = {
  data: new SlashCommandBuilder()
    .setName('get-custom-name')
    .setDescription('通过Discord ID查询自定义名称')
    .addStringOption(option =>
      option.setName('discord-id')
        .setDescription('目标用户的Discord ID')
        .setRequired(true)),
  async execute(interaction) {
    const discordId = interaction.options.getString('discord-id');
    const hashedId = hashDiscordId(discordId);
    
    const userMapping = await UserMapping.findOne({
      where: { hashedDiscordId: hashedId }
    });

    if (userMapping) {
      await interaction.reply(`该用户的自定义名称:${userMapping.customName}`);
    } else {
      await interaction.reply('未找到对应的自定义名称');
    }
  }
};

优缺点:

  • 优点:查询速度快(直接索引匹配)、实现简单
  • 缺点:若固定盐泄露,攻击者可针对生成彩虹表(但300条数据量极小,实际风险低)

方案2:哈希前缀过滤+随机盐(更高安全性)

保留随机盐的高安全性,同时通过哈希前缀减少需要验证的记录数,避免全表遍历。

实现步骤:

  1. 数据库表结构:
class UserMapping extends Model {}
UserMapping.init({
  customName: {
    type: DataTypes.STRING,
    allowNull: false
  },
  hashPrefix: {
    type: DataTypes.STRING(16), // 取SHA-256哈希的前16位
    allowNull: false
  },
  salt: {
    type: DataTypes.STRING(32),
    allowNull: false
  },
  fullHashedId: {
    type: DataTypes.STRING(64),
    allowNull: false
  }
}, {
  sequelize,
  modelName: 'UserMapping'
});

// 为hashPrefix创建索引,加速查询
UserMapping.addIndex({ fields: ['hashPrefix'] });
  1. 存储逻辑:
function generateRandomSalt() {
  return crypto.randomBytes(16).toString('hex');
}

async function storeUserMapping(discordId, customName) {
  const salt = generateRandomSalt();
  const fullHash = crypto.pbkdf2Sync(discordId, salt, 10000, 32, 'sha256').toString('hex');
  const hashPrefix = fullHash.slice(0, 16);

  await UserMapping.create({
    customName,
    hashPrefix,
    salt,
    fullHashedId: fullHash
  });
}
  1. 查询逻辑:
async function getCustomNameByDiscordId(discordId) {
  // 先计算临时哈希(用任意盐,只取前缀匹配)
  const tempHash = crypto.pbkdf2Sync(discordId, 'dummy-salt', 1, 32, 'sha256').toString('hex');
  const queryPrefix = tempHash.slice(0, 16);

  // 查询所有前缀匹配的记录
  const candidates = await UserMapping.findAll({
    where: { hashPrefix: queryPrefix }
  });

  // 逐一验证记录的盐是否匹配
  for (const candidate of candidates) {
    const computedHash = crypto.pbkdf2Sync(discordId, candidate.salt, 10000, 32, 'sha256').toString('hex');
    if (computedHash === candidate.fullHashedId) {
      return candidate.customName;
    }
  }
  return null;
}

优缺点:

  • 优点:随机盐保证高安全性,前缀过滤大幅减少验证记录数(300条数据下几乎瞬间完成)
  • 缺点:比固定盐方案多一步验证,但数据量小无性能问题

方案3:可逆AES加密(直接匹配查询)

放弃哈希改用加密,可直接对输入ID加密后匹配数据库中的密文,实现快速查询。

实现步骤:

  1. 加密/解密逻辑:
const ENCRYPTION_KEY = Buffer.from(process.env.ENCRYPTION_KEY, 'hex'); // 32字节密钥(AES-256)
const IV_LENGTH = 16;

function encryptDiscordId(discordId) {
  const iv = crypto.randomBytes(IV_LENGTH);
  const cipher = crypto.createCipheriv('aes-256-cbc', ENCRYPTION_KEY, iv);
  let encrypted = cipher.update(discordId);
  encrypted = Buffer.concat([encrypted, cipher.final()]);
  return `${iv.toString('hex')}:${encrypted.toString('hex')}`;
}

function decryptDiscordId(encryptedId) {
  const [ivHex, encryptedHex] = encryptedId.split(':');
  const iv = Buffer.from(ivHex, 'hex');
  const encrypted = Buffer.from(encryptedHex, 'hex');
  const decipher = crypto.createDecipheriv('aes-256-cbc', ENCRYPTION_KEY, iv);
  let decrypted = decipher.update(encrypted);
  decrypted = Buffer.concat([decrypted, decipher.final()]);
  return decrypted.toString();
}
  1. 数据库模型与查询:
class UserMapping extends Model {}
UserMapping.init({
  customName: {
    type: DataTypes.STRING,
    allowNull: false
  },
  encryptedDiscordId: {
    type: DataTypes.STRING,
    allowNull: false,
    unique: true
  }
}, {
  sequelize,
  modelName: 'UserMapping'
});

// 查询示例
const encryptedInputId = encryptDiscordId(inputDiscordId);
const userMapping = await UserMapping.findOne({
  where: { encryptedDiscordId: encryptedInputId }
});

优缺点:

  • 优点:查询速度最快,加密可逆(若需恢复原始ID)
  • 缺点:需严格管理加密密钥,密钥泄露会导致所有ID泄露

选择建议:

  • 若追求最简单实现且可接受固定盐的微小风险,选方案1
  • 若要最高安全性且不介意少量验证开销,选方案2
  • 若需要可逆恢复原始ID,选方案3

内容的提问来源于stack exchange,提问作者LeCarbonator

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 19:20:19