大多数师傅对认证应该很熟悉,常规的弱密码问题、认证类漏洞层出不穷,今天我主要是站在研发视角,去看待如何针对认证进行安全开发,或者说如何选取合适的认证场景来确保应用系统的安全认证。
认证是指验证个体、实体或网站是否与其声称的身份一致的过程。常见的情况是通过提交用户名或ID及密码进行验证。认证通过后通过会话进行交互管理,会话管理是服务器用来维持与它交互的实体状态的过程。这对于服务器记住如何在事务过程中对后续请求作出反应是必需的。会话通过服务器上的会话标识符来维护,该标识符可以在客户端和服务器在传输和接收请求时来回传递。会话应针对每个用户是唯一的,并且难以预测。
认证指引——开发视角
用户ID
确保用户名/用户ID不区分大小写。用户 'smith' 和用户 'Smith' 应视为同一用户。用户名也应该是唯一的。对于高安全需求的应用,用户名应该由系统进行分配并且保密+唯一性的,而不是用户自定义的公开数据。
认证解决方案和敏感账户
- 不允许使用敏感账户(即内部账户,如用于后端/中间件/数据库的账户)登录到任何前端用户界面。
实施密码强度控制
弱密码问题层出不穷,需要在应用层强制制定密码规则,降低用户设置弱密码的风险,具体特征如下:
密码长度
- 应用应强制执行密码的最小长度。不低于8位。
- 应用应强制执行密码的最大长度。不高于64位,超过64位可能会存在长密码拒绝服务攻击
不要静默截断密码
允许使用所有字符,包括Unicode和空格
- 不应该有密码组成规则限制允许的字符类型。
实施安全的密码恢复机制
应用程序通常具有一种机制,为用户提供一种在忘记密码时访问其账户的方式。
安全地存储密码
应用程序使用正确的加密技术存储密码,这个比较复杂,后续会出一个专题进行讲解。
使用安全函数比较密码哈希值
尽可能使用语言或框架提供的安全的密码比较函数,将用户输入的密码与存储的密码哈希进行比较,例如PHP中的`password_verify()`函数。请确保比较函数:
- 具有最大输入长度,以防止使用非常长的输入进行造成拒绝服务攻击。
- 显式设置两个变量的类型,以防止PHP中的魔术哈希等类型混淆攻击。
- 以恒定时间返回,以防止时序攻击。
仅通过TLS等加密传输方式传输密码
敏感功能需要重新认证
为了防止CSRF攻击和会话劫持,在更新敏感账户信息(例如用户密码、用户电子邮件)或进行敏感交易之前,需要重新输入当前账户的凭据进行二次验证,常见的修改密码时需验证旧密码。如果没有这项措施,攻击者可能能够在不知道用户当前凭据的情况下,通过CSRF或XSS攻击执行敏感交易。此外,攻击者可能通过临时物理访问用户的浏览器或窃取他们的会话ID来接管用户的会话。
双因素认证
应用程序应对敏感操作使用双因素进行认证。如在新设备或非可信设备登录时,需要进行二次校验
认证和错误消息
在认证功能的情况下,如果错误消息实现不正确,可以被用于用户ID和密码枚举。应用程序应对以下情况使用通用错误消息进行响应(包括HTTP和HTML):
- 用户ID或密码不正确。
- 账户不存在。
- 账户被锁定或禁用。
账户注册功能也应该考虑在内,对于用户已存在的情况,也可以应用相同的通用错误消息方法。防止攻击者对应用程序进行用户枚举操作。
但是业务逻辑本身可能会带来与处理时间相关的差异因素。实际上,根据实现情况,处理时间可能会根据不同情况(成功与失败)有显著差异,如攻击者进行基于时间的攻击(例如几秒钟的差值)。
使用伪代码的登录功能的示例:
第一种实现使用“快速退出”方法
```plaintext
IF USER_EXISTS(username) THEN
password_hash=HASH(password)
IS_VALID=LOOKUP_