需求
域约束
事实证明,姓名很复杂。
试图确定姓名的有效性是徒劳的。请记住,我们选择收集姓名是为了将其用于电子邮件的开头——我们不需要它与一个人的真实身份相匹配,无论这在其地理位置上意味着什么。完全没有必要给我们的用户带来错误或过于规范的验证带来的痛苦。
因此,我们可以简单地要求姓名字段非空(即,它必须至少包含一个非空格字符)。
安全约束
不幸的是,互联网上并非所有人都是好人。
如果有足够的时间,尤其是如果我们的简报获得关注并取得成功,我们必然会吸引恶意访问者的注意。
表单和用户输入是主要的攻击目标——如果它们没有得到适当的清理,它们可能会让攻击者破坏我们的数据库(SQL注入)、在我们的服务器上执行代码、导致我们的服务崩溃,以及执行其他恶意操作。
谢谢,但不用了。
在我们的情况下可能会发生什么?在各种可能的攻击中,我们应该做好哪些准备?
我们正在构建一份电子邮件简报,因此我们将重点关注以下方面:
- 拒绝服务 - 例如,试图关闭我们的服务以阻止其他人注册。 这几乎是任何在线服务的常见威胁;
- 数据盗窃 - 例如,窃取大量的电子邮件地址;
- 网络钓鱼 - 例如,使用我们的服务向受害者发送看似合法的电子邮件,诱骗他们点击某些链接或执行其他操作。
我们是否应该尝试在验证逻辑中应对所有这些威胁?
绝对不是!
但采用分层安全方法是一种很好的做法:通过采取缓解措施来降低堆栈中多个层级的这些威胁的风险(例如,输入验证、避免SQL注入的参数化查询、电子邮件中参数化输入的转义等),即使任何一项检查失败或日后被移除,我们也能降低受到攻击的可能性。
我们应该始终牢记,软件是一个活生生的产物:对系统的整体理解是时间流逝的第一个受害者。
当你第一次写下整个系统时,你的脑海里已经有了它,但下一个接触它的开发人员却不会——至少从一开始就不会。因此,应用程序某个不起眼角落的负载检查可能会消失(例如HTML转义),从而使你暴露于一类攻击(例如网络钓鱼)。
冗余可降低风险。
让我们直奔主题——鉴于我们识别出的威胁类别,我们应该对名称进行哪些验证才能提升我们的安全态势?
我的建议是:
- 强制设置最大长度。我们在 Postgres 中的电子邮件类型为 TEXT,这实际上不受限制——嗯,直到磁盘存储空间开始耗尽为止。名称的形式多种多样,但 256 个字符应该足以满足我们绝大多数用户的需求48——如果不够,我们会礼貌地要求他们输入昵称。
- 拒绝包含麻烦字符的名称。
/()"<>\{}
在 URL、SQL 查询和 HTML 片段中相当常见,但在名称49中则不那么常见。禁止使用它们会提高 SQL 注入和网络钓鱼攻击的复杂性。