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

C++密码加密异常:仅需密码前两位正确即可通过验证

问题排查与解决方案

首先,咱们先拆解一下你遇到的诡异问题:输入密码前两位就能通过验证,这说明你的哈希验证逻辑肯定在某个环节出现了「字符串不匹配但哈希意外相等」的情况,核心问题大概率出在盐的字符串处理和哈希存储/比较的类型转换上,下面一步步分析:

1. 最可能的根源:GUID字符串的不一致与截断

你存储密码时用了 Sysutils::GUIDToString(gSalt),这个函数在C++Builder里默认会生成带大括号的GUID字符串(比如 {12345678-1234-1234-1234-1234567890AB}),长度是38个字符。但你紧接着用了 CAST(...) AS NVARCHAR(36),这会直接把38字符的字符串截断成36位——也就是去掉最后一个大括号,变成 {12345678-1234-1234-1234-1234567890AB(少了结尾的 })。

而验证时,你从数据库取的 PasswordSalt 是 uniqueidentifier 类型,用 AsVariant 会返回不带大括号的36位GUID字符串(比如 12345678-1234-1234-1234-1234567890AB)。这就导致:

  • 存储时拼接的是:正确密码 + 带{且缺}的36位盐
  • 验证时拼接的是:输入密码 + 不带{}的36位盐

理论上这两个拼接字符串完全不同,哈希不该相等,但你遇到了意外匹配——这大概率是因为第二个问题:

2. PasswordHash字段类型错误导致的隐式转换问题

你把 HASHBYTES('SHA2_512') 的结果(varbinary(64) 类型的二进制哈希)直接存入了 nvarchar(MAX) 字段。SQL Server会自动把二进制数据隐式转换成Unicode字符串存储,而验证时又要把这个字符串再转成二进制和新计算的哈希比较。这个双向转换过程中,极有可能出现编码错乱,导致不同的二进制哈希被当成相同的字符串存储/比较,最终出现诡异的匹配结果。

3. 额外风险:SQL字符串拼接的隐患

你直接用字符串拼接生成SQL语句,不仅有严重的SQL注入风险,还可能因为字符串编码(比如是否加 N 前缀)、特殊字符截断等问题,进一步加剧哈希计算的不一致性。


解决步骤

第一步:修复盐的处理逻辑

统一盐的字符串表示,避免截断和格式不一致:

// 存储密码时,直接用数据库里的uniqueidentifier类型转成标准36位字符串
// 建议用参数化查询,避免字符串拼接问题
asSQLCommand = "UPDATE YCR_User_Names SET "
               "PasswordHash = HASHBYTES('SHA2_512', N'" + editPassword->Text + "' + CAST(@Salt AS NVARCHAR(36))), "
               "PasswordSalt = @Salt "
               "WHERE ID = " + sqlGetUser->FieldByName("ID")->AsInteger;
// 给SQLCommand添加参数@Salt,类型设为uniqueidentifier,值传入gSalt

验证时直接用数据库中的 PasswordSalt 字段转换,不要依赖前端的字符串处理:

asSQLCommand = "SELECT * FROM YCR_User_Names "
               "WHERE PasswordHash = HASHBYTES('SHA2_512', N'" + editPassword->Text + "' + CAST(PasswordSalt AS NVARCHAR(36))) "
               "AND ID = " + sqlGetUser->FieldByName("ID")->AsInteger;

注意:所有字符串前加 N 前缀,确保是Unicode字符串,避免varchar/nvarchar的隐式转换。

第二步:修改PasswordHash字段类型

把 PasswordHash 从 nvarchar(MAX) 改成 varbinary(64)——SHA2_512的哈希结果正好是64字节,用二进制类型存储最准确,彻底避免类型转换的坑。

第三步:改用参数化查询(强烈推荐)

彻底放弃字符串拼接生成SQL,用参数传递密码和盐,比如:

// 存储示例
TADOCommand* cmd = new TADOCommand(this);
cmd->Connection = yourDBConnection;
cmd->CommandText = "UPDATE YCR_User_Names SET PasswordHash = HASHBYTES('SHA2_512', N@Password + CAST(@Salt AS NVARCHAR(36))), PasswordSalt = @Salt WHERE ID = @UserID";
cmd->Parameters->CreateParameter("@Password", ftString, pdInput, 255, editPassword->Text);
cmd->Parameters->CreateParameter("@Salt", ftGuid, pdInput, 0, gSalt);
cmd->Parameters->CreateParameter("@UserID", ftInteger, pdInput, 0, sqlGetUser->FieldByName("ID")->AsInteger);
cmd->Execute();

这样既避免了SQL注入,又彻底解决了字符串转换和截断的问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 21:08:01