HTML5的input[type="password"]仅遮掩显示,值仍为明文;真正安全依赖HTTPS传输、服务端强哈希(如bcrypt/Argon2)存储及前后端协同校验,前端仅能减少暴露面。

HTML 本身不提供密码安全存储能力,input[type="password"] 只是遮掩显示,值仍是明文;真正安全的“存储”发生在服务端,前端唯一能做的是防止密码在传输和内存中裸露。
为什么不能用 localStorage 存密码哈希值
很多人误以为“把 bcrypt 后的哈希存进 localStorage 就安全了”,其实完全相反:
-
localStorage是纯文本、可被任意 JS 读取(包括 XSS 脚本),存什么都等于公开 - 哈希值一旦泄露,攻击者可直接用于登录(若后端错误地把它当原始密码验证)
- 前端无法安全生成或保管盐值,
PBKDF2派生出的密钥仍暴露在内存中,调试器一断点就全看光 - 浏览器扩展、恶意插件、控制台执行
localStorage.getItem('pwd_hash')都能立刻拿到
密码提交时必须走 HTTPS + POST
这是不可妥协的底线。任何绕过它的“前端加密”都是自我安慰:
- HTTP 下哪怕你用
crypto.subtle.encrypt()加密了密码,密文本身就成了新密码,被截获就能重放登录 - GET 请求带密码(如
/login?pwd=xxx)会泄露到服务器日志、代理缓存、Referer 头里 - Chrome 等现代浏览器对非 HTTPS 页面禁用
crypto.subtle,HTTP 站点调用直接抛SecurityError - 表单
method="POST"+enctype="application/x-www-form-urlencoded"是标准做法,别手贱改成 JSON 然后漏设Content-Type
后端必须用 bcrypt/Argon2 做强哈希
前端没权限也没能力决定密码怎么存,但你可以确认后端是否合规——否则前端做得再“严”,数据库一拖库就全崩:
立即学习“前端免费学习笔记(深入)”;
- 拒绝接受任何含
md5、sha1、sha256(无盐无迭代)的密码字段 - 检查哈希字符串格式:bcrypt 是
$2b$12$...开头,Argon2 是$argon2id$v=19$m=65536,t=3,p=4$...,不是 base64 或 hex 就可疑 - 工作因子(cost factor)至少为 12(bcrypt)或
m=65536(Argon2),太低意味着暴力破解只要几毫秒 - 盐值必须每次随机生成、与哈希一同存储,不能复用全局 salt,更不能硬编码在代码里
前端唯一有效的配合动作
不是“加密”,而是“减少暴露面”和“防误操作”:
- 登录/注册页用
autocomplete="new-password"(不是off),提示浏览器不要填充旧密码 - 密码框失焦时清空临时变量:
input.addEventListener('blur', () => { input.value = ''; });,避免内存残留(仅辅助) - 强度校验用
pattern和前端 JS 提示(如pattern="(?=.*\d)(?=.*[a-z]).{8,}"),但绝不替代后端校验 - 敏感操作(改密、删账号)必须二次验证,不能只靠 session,要重新输密码或走短信/WebAuthn
最常被忽略的一点:所有前端防护都建立在“传输层已 HTTPS”和“服务端已正确哈希”的前提下。这两个条件缺一个,其他动作全是表演。



















