Skip to content
返回博客
安全

Web 开发者安全最佳实践

每个 Web 开发者都应遵循的基本安全实践,涵盖密码哈希、输入验证等关键领域。

12 分钟

Web 开发者安全最佳实践

Web 安全不是可选项。随着网络威胁日益增加,开发者必须在应用程序的每一层都构建安全措施。本指南涵盖了你今天就应该实施的基本安全实践。

密码安全

永远不要存储明文密码

始终使用现代算法(如 bcrypt、Argon2 或 scrypt)来哈希密码。这些算法被设计为运算缓慢,使暴力破解攻击变得不切实际。

// 推荐:使用 bcrypt
const bcrypt = require('bcrypt');
const hash = await bcrypt.hash(password, 12);

哈希算法对比

并非所有哈希算法都一样。选择合适的算法取决于你的威胁模型和使用场景:

算法输出大小速度使用场景安全状态
MD5128 位非常快校验和、非安全哈希安全性已被破解
SHA-256256 位数据完整性、数字签名安全
bcrypt184 位慢(可调)密码哈希安全
Argon2可配置慢(可调)密码哈希(现代方案)新项目推荐使用

bcrypt 和 Argon2 被设计为故意低速——这是特性,不是缺陷。每次哈希操作需要数十到数百毫秒,使大规模暴力破解在经济上变得不可行。

理解密码熵

密码强度可以用熵进行数学量化:entropy = log2(charset_size^length)。使用小写字母(26 个字符)的 8 位密码约有 37.6 位熵。而混合大小写字母、数字和符号(95 个字符)的 16 位密码约有 105 位熵——破解难度呈指数级增长。这就是为什么对大多数用户来说,密码长度比复杂度更重要。想深入了解密码强度背后的数学原理,请阅读我们的密码熵详解

使用密码管理器

建议你的用户使用密码管理器。人类选择的密码往往遵循可预测的模式,攻击者利用字典攻击加以利用。密码管理器生成真正的随机字符串,并消除跨服务的密码重用——这是凭证填充攻击最常见的攻击向量之一。

使用足够的盐值轮次

盐值轮次决定了计算成本。更高的值更安全但速度更慢。对于大多数应用来说,10-12 轮是一个良好的平衡点。

输入验证

在客户端和服务端都进行验证

客户端验证可以改善用户体验,但服务端验证对安全至关重要。永远不要信任客户端输入。

清理所有用户输入

通过清理输入来防止注入攻击:

  • 使用参数化查询防止 SQL 注入
  • 转义 HTML 输出以防止 XSS 攻击
  • 严格验证文件上传

具体的攻击示例

理解真实的攻击有助于你进行防御。假设一个评论表单直接将用户输入渲染到 HTML 中。攻击者提交了:

<script>alert('xss')</script>

如果应用在渲染时没有转义,该脚本会在每个访问者的浏览器中执行——窃取 cookie、重定向用户或注入键盘记录器。解决方案:始终根据上下文对输出进行编码。使用 DOMPurify 等库进行 HTML 清理。

SQL 注入同样危险。在登录表单中,攻击者输入用户名:

' OR 1=1 --

如果查询是通过字符串拼接构建的("SELECT * FROM users WHERE username='" + input + "'"),这将完全绕过身份验证。-- 会注释掉查询的其余部分。解决方案:始终使用参数化查询(也称为预处理语句)。每个主流数据库库都支持:

// 错误:字符串拼接
db.query(`SELECT * FROM users WHERE username='${input}'`);

// 正确:参数化查询
db.query('SELECT * FROM users WHERE username = $1', [input]);

内容安全策略(CSP)

作为纵深防御策略,部署内容安全策略头部。CSP 告诉浏览器哪些内容来源是可信的,有效地阻止内联脚本和未授权的资源加载。即使你的代码中存在 XSS 漏洞,严格的 CSP 也能阻止注入脚本的执行。从 Content-Security-Policy: default-src 'self' 开始,根据需要逐步添加例外。

哈希函数

选择正确的哈希算法

不同的使用场景需要不同的哈希函数:

使用场景推荐算法
密码bcrypt、Argon2
完整性验证SHA-256
校验和SHA-256、MD5(非安全场景)
快速哈希BLAKE3

理解哈希输出与碰撞

MD5 生成 128 位(32 个十六进制字符)哈希值,而 SHA-256 生成 256 位(64 个十六进制字符)哈希值。这个差异很重要:更大的输出空间意味着指数级增长的哈希可能值,碰撞概率大幅降低。碰撞是指两个不同的输入产生相同的哈希值——能够制造碰撞的攻击者可以伪造数字签名或篡改已验证的数据。

MD5 碰撞在现代硬件上可以在几秒内生成。SHA-256 仍然具有抗碰撞性,目前没有已知的实际攻击。这就是为什么为正确的场景选择正确的算法至关重要:

  • 校验和与去重:当安全性不是关注点时,MD5 是可以接受的
  • 数据完整性与签名:SHA-256 提供强抗碰撞性
  • 密码存储:bcrypt 或 Argon2,它们增加了盐值和故意的计算延迟

用于消息认证的 HMAC

当你需要同时验证消息的完整性和真实性时,使用 HMAC(基于哈希的消息认证码)。HMAC 将哈希函数与密钥结合,确保只有知道密钥的各方才能生成或验证标签。这对于 API 认证、Webhook 验证和安全令牌生成至关重要。

永远不要将 MD5 或 SHA-1 用于安全目的

MD5 和 SHA-1 在安全方面已经被破解。请使用 SHA-256 或 SHA-3 进行加密哈希。

全面使用 HTTPS

TLS 的实际作用

TLS(传输层安全协议)提供三个关键保护:传输加密(防止窃听)、服务器认证(证明你正在与真实的服务器通信,而非冒充者)以及数据完整性(检测传输中的任何篡改)。没有 TLS,用户和服务器之间的所有数据——密码、令牌、个人信息——都以明文形式传输。

始终使用 TLS

  • 从受信任的 CA 获取证书(Let’s Encrypt 是免费且完全自动化的)
  • 将 HTTP 重定向到 HTTPS
  • 使用 HSTS 头部
  • 保持 TLS 版本更新

HSTS 与混合内容

HTTP 严格传输安全(HSTS)头部告诉浏览器只能通过 HTTPS 连接,即使用户输入了 http://。设置 Strict-Transport-Security: max-age=31536000; includeSubDomains 可以在所有子域上强制执行一整年。这能防止 SSL 剥离攻击,即攻击者将连接降级为 HTTP。

注意混合内容警告:如果你的 HTTPS 页面通过 HTTP 加载图片、脚本或样式表,浏览器会阻止或警告。审查你的页面中硬编码的 http:// URL,使用协议相对路径或对所有资源强制使用 HTTPS。

身份认证

实施速率限制

通过速率限制防止暴力破解攻击:

  • 限制每个 IP 的登录尝试次数
  • 在失败尝试后添加延迟
  • 对可疑活动使用验证码

JWT 认证基础

JSON Web Token(JWT)提供了一种无状态的认证机制,结构为 header.payload.signature。服务器使用密钥签署令牌,客户端在后续请求中携带它。由于令牌包含用户的声明信息,服务器不需要在每次请求时查找会话状态——这使得 JWT 非常适合分布式系统和微服务架构。

始终为访问令牌设置较短的过期时间(例如 15 分钟),并使用刷新令牌来获取新的访问令牌。安全地存储刷新令牌(httpOnly cookie,而非 localStorage),并实施令牌轮换,使每个刷新令牌只能使用一次。

多因素认证(MFA)

对于任何严肃的应用程序,MFA 不再是可选项。要求第二因素——TOTP 代码(Google Authenticator)、硬件密钥(YubiKey)或推送通知——能够显著降低密码泄露的影响。即使攻击者获得了有效的凭证,没有第二因素也无法完成认证。

会话固定攻击防护

会话固定攻击发生在攻击者在用户认证之前设置一个已知的会话 ID。登录后,攻击者使用该会话 ID 劫持已认证的会话。防止此攻击的方法是在认证成功后始终重新生成会话 ID并使旧 ID 失效。

使用安全的会话管理

  • 生成加密随机的会话 ID
  • 在 cookie 上设置 secure 和 httpOnly 标志
  • 实施会话超时
  • 在登出时使会话失效

安全头部清单

部署正确的 HTTP 响应头是加固应用程序最有效且最低成本的方式之一。以下是基本安全头部的快速参考表:

头部用途示例值
Content-Security-Policy防止 XSS 和数据注入default-src 'self'
Strict-Transport-Security强制 HTTPS 连接max-age=31536000; includeSubDomains
X-Content-Type-Options防止 MIME 类型嗅探nosniff
X-Frame-Options防止点击劫持DENY
Referrer-Policy控制引用来源信息strict-origin-when-cross-origin

这些头部可以在 Web 服务器层(Nginx、Apache)、CDN/边缘层(Cloudflare、Vercel)或应用框架中设置。使用 securityheaders.com 等工具测试你的头部配置。目标是达到 A+ 评级——大多数头部只需一行配置,部署零成本。

使用我们的安全工具

探索我们的安全工具来帮助你的开发工作:

如需更全面地了解编码、哈希和转换工具如何融入你的开发工作流程,请参阅我们的开发者工具必备指南

常见问题

最常见的 Web 安全漏洞是什么?

跨站脚本攻击(XSS)是 OWASP 报告中最普遍的 Web 安全漏洞。当应用程序在未经适当验证的情况下将不可信数据嵌入网页时,就会发生 XSS。通过对所有用户输入进行清理、使用内容安全策略(CSP)头部,以及根据上下文(HTML、JavaScript、URL 或 CSS)对输出进行编码来防止 XSS。

MD5 还能用于密码哈希吗?

不能 — MD5 绝不应用于密码哈希。MD5 计算速度极快,这使其容易受到暴力破解和彩虹表攻击。现代 GPU 每秒可计算数十亿次 MD5 哈希。请改用 bcrypt、scrypt 或 Argon2,这些算法被设计为故意低速,并内置加盐机制以抵御攻击。

2026 年安全密码应该有多长?

建议至少 12 个字符,但 16 个字符以上能提供显著更强的保护。长度比复杂度更重要 — 一个 20 字符的密码短语(如 “correct-horse-battery-staple”)比一个简短的复杂密码(如 “P@ss1!”)更安全。对于关键账户,无论密码长度如何,都应启用多因素认证(MFA)。

加密和哈希有什么区别?

加密是可逆的 — 你可以使用密钥将数据解密还原为原始形式。哈希是单向的 — 你无法从哈希值恢复原始数据。对需要检索的数据(如存储的用户数据)使用加密,对只需验证的数据(如密码和校验和)使用哈希。

我应该自己实现认证系统吗?

不建议 — 从零构建认证系统风险大且容易出错。请使用经过实战验证的框架和服务,如 Auth0、Firebase Auth 或 Supabase Auth。它们处理密码哈希、会话管理、令牌轮换、多因素认证和暴力破解防护。将开发时间集中在应用程序的独特功能上。

总结

安全是一个持续的过程,而不是一次性的任务。及时了解最新的漏洞信息,定期审计你的代码,并遵循最小权限原则。用户将数据托付给你 - 请用强大的安全实践来回报这份信任。