密码加盐

为什么需要加盐

  1. 降低彩虹表攻击的风险。
  2. 增加密码复杂度。
  3. 保护用户隐私。
  4. 提高数据完整性。
  5. 避免碰撞攻击。
  6. 增加密码存储的安全性。

应用场景

用户身份验证

  1. 用户注册和密码设置
  • 当用户注册或设置密码时,客户端生成一个随机盐(或从服务器请求一个盐)。
  • 客户端将盐与用户提供的密码组合,并将组合后的密码哈希。
  • 客户端将生成的哈希值与盐一起发送到服务器以完成注册或密码设置。
  1. 服务器端存储:
  • 服务器接收客户端发送的哈希值和盐,并将它们存储在用户的账户记录中。
  • 服务器保持盐的唯一性并确保在数据库中存储盐的唯一副本。
  1. 密码验证:
    当用户尝试登录时,客户端向服务器发送用户名和密码(未加盐)。
  2. 服务器检索存储的盐和哈希值。
  • 服务器将接收到的密码与存储的盐组合,并进行哈希,以获得客户端生成的哈希值。
  • 服务器将生成的哈希值与存储的哈希值进行比较,如果匹配,用户密码验证成功。

C语言实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <openssl/sha.h>

#define SALT_SIZE 16 // 盐的大小(字节)
#define HASH_SIZE SHA256_DIGEST_LENGTH // 哈希结果的大小(字节)

// 生成随机盐
void generate_salt(uint8_t salt[SALT_SIZE]) {
srand(time(NULL));
for (int i = 0; i < SALT_SIZE; i++) {
salt[i] = rand() % 256;
}
}

// 使用盐加密密码
void hash_password(const char *password, const uint8_t salt[SALT_SIZE], uint8_t hashed_password[HASH_SIZE]) {
SHA256_CTX sha256;
SHA256_Init(&sha256);

// 将盐与密码组合
SHA256_Update(&sha256, salt, SALT_SIZE);
SHA256_Update(&sha256, password, strlen(password));

// 计算哈希值
SHA256_Final(hashed_password, &sha256);
}

// 验证密码
int verify_password(const char *password, const uint8_t salt[SALT_SIZE], const uint8_t stored_password[HASH_SIZE]) {
uint8_t test_password[HASH_SIZE];
hash_password(password, salt, test_password);

// 比较哈希后的密码是否与存储的密码一致
return memcmp(test_password, stored_password, HASH_SIZE) == 0;
}

int main() {
char password[] = "my_secure_password";
uint8_t salt[SALT_SIZE];
uint8_t hashed_password[HASH_SIZE];

generate_salt(salt);
hash_password(password, salt, hashed_password);

// 存储盐和哈希后的密码

// 验证密码
if (verify_password("incorrect_password", salt, hashed_password)) {
printf("密码正确\n");
} else {
printf("密码错误\n");
}

return 0;
}

上述代码有如下问题:

  • 不够安全:使用rand()函数生成的随机数并不是真正的随机数,而是伪随机数。这意味着生成的盐不够安全,因为攻击者可能能够破解生成算法,从而获得盐的值。

  • 可预测性:如果多个用户在同一秒内注册或更改密码,它们可能会获得相同的随机盐,因为time(NULL)返回的时间戳粒度是一秒。这会降低盐的唯一性。

可以使用C标准库的rand()函数来生成随机种子,然后使用此种子初始化一个密码学安全的伪随机数生成器,如OpenSSL的RAND_bytes函数。这将提高生成随机盐的质量和安全性。