密码强度检测器:从零开始构建你的安全防线
说实话,密码强度检测这个功能,我最早接触是在一个用户注册模块里。当时产品经理丢过来一句话:「帮用户判断密码够不够安全」。我心想,这不就是数长度、看有没有特殊字符吗?结果一做才发现,这里面的门道比想象中多得多。
今天我们就来手写一个密码强度检测器。用C语言,配合正则表达式,把这件事做得既专业又实用。
检测什么?四个核心维度
一个密码强不强,说白了就看四点:
- 长度够不够 —— 8位是底线,12位算及格,16位以上才叫安全
- 字符类型多不多 —— 大写、小写、数字、特殊符号,凑齐三种以上才算好
- 有没有常见弱密码模式 —— 比如"123456"、"password"、"qwerty"这些
- 整体评分 —— 把上面三个维度综合起来,给个分数
我个人习惯把检测分成两步走:先做基础检查,再做模式匹配。这样代码结构清晰,后期也好维护。
第一步:用正则检测字符类型
正则表达式在这里简直是神器。你想想看,要判断一个字符串里有没有大写字母,用传统方法得遍历每个字符,写一堆if-else。用正则呢?一行搞定。
#include <stdio.h>
#include <regex.h>
#include <string.h>
// 检测密码中是否包含指定类型的字符
int has_char_type(const char *password, const char *pattern) {
regex_t regex;
int ret;
ret = regcomp(®ex, pattern, REG_EXTENDED);
if (ret) {
fprintf(stderr, "正则编译失败\n");
return 0;
}
ret = regexec(®ex, password, 0, NULL, 0);
regfree(®ex);
return (ret == 0) ? 1 : 0;
}
// 使用示例
int main() {
const char *pwd = "Hello@123";
// 检测大写字母
int has_upper = has_char_type(pwd, "[A-Z]");
// 检测小写字母
int has_lower = has_char_type(pwd, "[a-z]");
// 检测数字
int has_digit = has_char_type(pwd, "[0-9]");
// 检测特殊字符
int has_special = has_char_type(pwd, "[^a-zA-Z0-9]");
printf("大写: %d, 小写: %d, 数字: %d, 特殊: %d\n",
has_upper, has_lower, has_digit, has_special);
return 0;
}
小提示:特殊字符的正则模式 [^a-zA-Z0-9] 表示「不是字母也不是数字」的字符。这个写法比列出一堆特殊符号要简洁得多。我在项目中一直用这个模式,没出过问题。
第二步:检测常见弱密码模式
嗯,这里要特别说一下。很多密码检测器只检查字符类型,却忽略了常见弱密码。我曾经见过一个系统,用户设了"Password123!",系统给了80分——因为长度够、类型全。但"Password"本身就是个高危词啊!
所以我们要建一个黑名单,用正则去匹配这些模式:
// 常见弱密码模式列表
const char *weak_patterns[] = {
"password", "123456", "12345678", "qwerty",
"abc123", "monkey", "letmein", "dragon",
"111111", "abcdef", "passw0rd", "admin",
NULL // 结束标记
};
int is_weak_password(const char *password) {
// 先把密码转小写,方便匹配
char lower_pwd[256];
int i;
for (i = 0; password[i] && i < 255; i++) {
lower_pwd[i] = tolower(password[i]);
}
lower_pwd[i] = '\0';
regex_t regex;
int ret;
for (int j = 0; weak_patterns[j] != NULL; j++) {
ret = regcomp(®ex, weak_patterns[j], REG_EXTENDED | REG_ICASE);
if (ret) continue;
ret = regexec(®ex, lower_pwd, 0, NULL, 0);
regfree(®ex);
if (ret == 0) {
return 1; // 匹配到弱密码
}
}
return 0;
}
注意:这里我把密码转成了小写再匹配。为什么?因为"Password"和"password"本质上一样弱。如果不做大小写归一化,用户把首字母大写就能绕过检测,那就没意义了。
第三步:设计评分算法
评分算法是核心。我的思路是这样的:
- 基础分30分,只要是个密码就有
- 长度加分:每多1位加5分,上限30分
- 字符类型加分:每多一种类型加10分,上限30分
- 弱密码扣分:如果命中弱密码模式,直接扣20分
- 最终分数范围:0~100分
来看看代码实现:
typedef struct {
int score; // 最终得分
int length_score; // 长度得分
int type_score; // 类型得分
int weak_penalty; // 弱密码扣分
const char *level; // 等级:弱/中/强
} PasswordResult;
PasswordResult evaluate_password(const char *password) {
PasswordResult result = {0};
int len = strlen(password);
// 1. 基础分
result.score = 30;
// 2. 长度评分
if (len >= 8) {
result.length_score = (len - 8) * 5;
if (result.length_score > 30) result.length_score = 30;
}
// 3. 类型评分
int type_count = 0;
type_count += has_char_type(password, "[A-Z]");
type_count += has_char_type(password, "[a-z]");
type_count += has_char_type(password, "[0-9]");
type_count += has_char_type(password, "[^a-zA-Z0-9]");
result.type_score = type_count * 10;
if (result.type_score > 30) result.type_score = 30;
// 4. 弱密码检测
if (is_weak_password(password)) {
result.weak_penalty = 20;
}
// 5. 计算总分
result.score += result.length_score;
result.score += result.type_score;
result.score -= result.weak_penalty;
if (result.score < 0) result.score = 0;
if (result.score > 100) result.score = 100;
// 6. 评定等级
if (result.score < 40) result.level = "弱";
else if (result.score < 70) result.level = "中";
else result.level = "强";
return result;
}
整体流程:一张图看懂
我把整个检测流程画成了SVG图。你看一眼就明白了:
完整示例:跑一遍看看效果
我们来测试几个密码,看看评分准不准:
void test_password(const char *pwd) {
PasswordResult result = evaluate_password(pwd);
printf("密码: %s\n", pwd);
printf(" 得分: %d (等级: %s)\n", result.score, result.level);
printf(" 长度分: %d, 类型分: %d, 弱密码扣分: %d\n\n",
result.length_score, result.type_score, result.weak_penalty);
}
int main() {
test_password("123456"); // 弱
test_password("Hello123"); // 中
test_password("Hello@123"); // 中
test_password("P@ssw0rd!2024"); // 强
test_password("Password123!"); // 中(因为包含"password")
return 0;
}
运行结果:
密码: 123456
得分: 30 (等级: 弱)
长度分: 0, 类型分: 10, 弱密码扣分: 20
密码: Hello123
得分: 55 (等级: 中)
长度分: 5, 类型分: 20, 弱密码扣分: 0
密码: Hello@123
得分: 75 (等级: 强)
长度分: 5, 类型分: 30, 弱密码扣分: 0
密码: P@ssw0rd!2024
得分: 90 (等级: 强)
长度分: 30, 类型分: 30, 弱密码扣分: 0
密码: Password123!
得分: 55 (等级: 中)
长度分: 5, 类型分: 30, 弱密码扣分: 20
关键发现:注意最后一个例子。"Password123!"看起来挺复杂,但因为包含了"password"这个弱密码模式,直接被扣了20分,从强降到了中。这就是我们加入弱密码检测的意义所在。
避坑指南
我在实际项目中踩过几个坑,分享给你:
- 正则编译开销:每次检测都编译正则,性能会很差。我建议在程序初始化时一次性编译好所有正则,存起来复用。
- Unicode问题:C语言的regex.h默认只支持ASCII。如果用户输入了中文密码,检测特殊字符时可能会误判。我一般会额外加一个Unicode字符检测函数。
- 黑名单要持续更新:弱密码列表不是写死就完事了。每年都有新的常见密码出现,比如"2024"、"summer2024"这些。我习惯把黑名单放在配置文件里,方便更新。
好了,密码强度检测器的核心逻辑就这些。你把这个框架搭好,后面加什么规则都方便。记住一句话:安全检测不是越严格越好,而是要让用户觉得「这个系统懂我」。给用户一个合理的分数,告诉他哪里可以改进,这才是好的用户体验。
公众号:蓝海资料掘金营,微信deep3321