跳转至内容

Web 应用安全指南/检查清单

来自维基教科书,开放世界中的开放书籍

杂项要点

  • 不要依赖 Web 应用防火墙来保证安全(但是,可以考虑使用它们来提高安全级别)
  • 如果使用了外部库(例如,用于数据库访问、XML 解析),请始终使用最新版本
  • 如果需要随机数,请从安全/加密随机数生成器中获取
  • 对于每个数据操作或检索,请始终检查访问权限
  • 在任何情况下都不要尝试自己实现加密算法。请使用高级库进行加密。
  • 确保调试输出和错误消息不会泄露敏感信息
  • 在代码中标记有问题的调试输出(例如 //TODO DEBUG REMOVE),即使您打算在测试后立即将其删除
  • 不要使用“eval()”和类似函数
    • 如果可能,请避免使用“system()”和类似函数
  • 确保数据库服务器无法从外部直接访问
  • 考虑阻止旧版浏览器使用您的应用程序

文件包含与泄露

  • 不要从用户输入中获取包含文件的名称,只能从受信任的列表或常量中获取。
    • 如果要使用用户输入,请根据白名单进行验证。检查文件是否存在或输入是否匹配特定格式是不够的。
  • 如果可能,请避免让脚本读取和传递文件。
  • 如果您使用用户提供的文件名读取和传递文件,请彻底验证文件名以避免目录遍历和类似攻击,并确保用户有权读取该文件。
  • 确保应用程序运行时没有超过所需的权限。

文件上传漏洞

  • 避免不必要的文件上传
  • 确保用户上传的文件不会被 Web 服务器解释为脚本文件,例如,通过检查文件扩展名(或 Web 服务器用于识别脚本文件的任何方法)
  • 确保文件无法上传到意外的目录(目录遍历)
  • 尝试禁用上传目录中的脚本执行
  • 确保文件扩展名与文件内容的实际类型匹配
  • 如果只需要上传图像,请考虑使用安全库重新压缩它们以确保它们有效
  • 确保上传的文件在传递给用户时使用正确的 Content-type 指定
  • 使用允许的文件类型白名单,防止用户上传有问题的文件类型,例如 HTML、CSS、JavaScript、XML、SVG 和可执行文件
  • 防止用户上传特殊文件(例如 .htaccess、web.config、robots.txt、crossdomain.xml、clientaccesspolicy.xml)
  • 防止用户覆盖应用程序文件
  • 考虑使用“Content-disposition: attachment”标头传递上传的文件

SQL 注入

  • 使用预处理语句访问数据库 – 或 –
  • 使用存储过程,并使用适当的语言/库方法或预处理语句进行访问
  • 始终确保应用程序使用的数据库登录名仅具有所需的权限

跨站脚本 (XSS)

  • 在将任何非常量内容包含在响应中之前,尽可能靠近输出位置进行转义(即,在包含“echo”或“print”调用的行中)
  • 如果不可能(例如,在构建较大的 HTML 块时),请在构建时进行转义,并指示变量内容已预先转义以及名称中的预期上下文。
  • 在转义时考虑上下文:转义 HTML 内部的文本与转义 HTML 属性值不同,并且与转义 CSS 或 JavaScript 内部的值或 HTTP 标头内的值非常不同。
    • 这可能意味着您需要针对多个上下文和/或多次进行转义。例如,当将 HTML 片段作为 JS 常量传递以供稍后包含在文档中时,在将常量写入 JavaScript 源代码时,需要针对 HTML 中的 JS 字符串进行转义,然后在脚本将片段写入文档时,再次针对 HTML 进行转义。(请参阅基本原理以获取示例)
    • 攻击者绝不能将任何内容放在不应放置的位置,即使您认为它不可利用(例如,因为尝试利用它会导致 JavaScript 损坏)。
  • 在文档开头(即,尽早)和/或标头中显式设置正确的字符集。
  • 确保用户提供的 URL 以允许的方案开头(白名单),以避免危险的方案(例如 javascript:-URL)
  • 不要忘记重定向脚本中的 URL
  • 可以将内容安全策略用作额外的安全措施,但它本身不足以防止攻击。

XML 和内部数据转义

  • 如果可能,请避免使用 XML。
  • 对于 XML,请使用经过良好测试的高质量库,并密切关注文档。了解您的库 - 一些库具有允许您在不知情的情况下绕过转义的函数。
  • 如果您解析(读取)XML,请确保您的解析器不尝试加载外部引用(例如实体和 DTD)。
  • 对于数据的其他内部表示形式,请确保应用了正确的转义或过滤。即使看起来更困难,也尝试使用经过良好测试的高质量库(如果可用)。
  • 如果手动进行转义,请确保它能够以安全的方式处理空字节、意外字符集、无效的 UTF-8 字符等。

XML、JSON 和通用 API 安全

  • 确保对 API 进行适当的访问控制
  • 不要忘记您需要正确转义所有输出以防止 XSS 攻击,数据格式(如 XML)需要特别考虑,并且在许多情况下都需要防止跨站请求伪造 (CSRF)
  • 使用具有成熟库的标准数据格式(如 JSON),并正确使用它们。这可能会处理您所有的转义需求。
  • 确保浏览器不会误解您的文档或允许跨站点加载
    • 确保您的文档格式正确
    • 发送正确的 content type
    • 使用 X-Content-Type-Options: nosniff 标头
    • 对于 XML,请提供字符集并确保攻击者无法插入任意标签
    • 对于 JSON,请确保顶级数据结构是对象,并且 HTML 中所有具有特殊含义的字符都已转义

(不)受信任的输入

  • 彻底过滤/转义任何不受信任的内容
  • 如果某些输入字段的允许字符集受到限制,请在使用它之前检查输入是否有效
  • 如果您对某种数据(例如服务器变量)有疑问,请将其视为不受信任的
  • 如果您确定,但没有真正需要将其视为受信任的,请将其视为不受信任的
  • 请求 URL(例如,在环境变量中)不受信任
  • 来自 HTTP 标头的数据不受信任
    • Referer
    • X-Forwarded-For
    • Cookie
    • 服务器名称(!)
  • 所有 POST 和 GET 数据都不受信任
    • 包括不可修改的用户输入字段,例如 select
  • 所有内容验证都必须在服务器端完成

跨站请求伪造 (CSRF)

  • 包含一个隐藏的表单字段,其中包含与用户会话(最好是执行的操作)绑定的随机令牌,并在响应中检查此令牌
  • 确保令牌不可预测,攻击者无法获取
    • 不要将其包含在攻击者可以使用 <script> 标签加载到其站点中的文件中
  • Referer 检查不安全,但可以用作附加措施

点击劫持

  • 通过包含 HTTP 响应标头“来防止在当前浏览器中对您的应用程序进行 (i)framingX-Frame-Options: deny
  • 通过包含一个 JavaScript 框架破坏程序来防止在过时的浏览器中进行 (i)framing,该程序检查 (i)framing,如果检测到则拒绝显示页面
  • 对于安全要求高的应用程序,您希望用户使用 JavaScript 禁用的过时浏览器,请考虑要求使用旧版浏览器的用户启用 JavaScript

不安全的数据传输

  • 对任何和所有数据传输使用 SSL/TLS (https)
  • 不要以 http 方式开始通信,只有在“需要”时才重定向到 https
  • 使用“secure”属性标记 Cookie
  • 在可能的情况下使用 Strict-Transport-Security 标头
  • 教育用户直接访问https://URL
  • 如果您的 Web 应用程序执行 HTTPS 请求,请确保它验证证书和主机名
    • 如果连接到内部服务器,请考虑限制受信任的 CA

会话固定

  • 在用户登录后立即重新生成(更改)会话 ID(销毁旧会话)

  • 防止攻击者通过用户会话来利用用户,仅从cookie中获取会话ID,而不是从GET或POST参数中获取(PHP:php.ini设置“session.use_only_cookies”)

会话劫持

  • 为会话cookie设置“HttpOnly”属性
  • 使用安全的随机性和足够的长度生成随机会话ID
  • 不要泄露会话ID

截断攻击,修剪攻击

  • 避免截断输入。将过长的输入视为错误。
  • 如果需要截断,请确保在截断后检查值,并且仅使用截断后的值
  • 确保不会发生修剪或一致地进行检查
  • 引入长度检查
    • 注意由于编码导致的不同长度
  • 通过设置适当的SQL模式,确保SQL将截断的查询视为错误SQL模式

密码安全

  • 不要存储明文密码,只存储哈希值
  • 使用Argon2、scrypt、bcrypt或其他专为安全密码存储而设计的安全哈希算法。[1][2]
  • 使用每个用户的盐值
  • 使用增强(例如多迭代哈希来减缓暴力破解尝试)
  • 限制每个IP的登录尝试次数(而不是每个用户帐户)
  • 实施合理但不要过于严格的密码策略
  • 如果实施了密码重置流程,请确保其具有足够的安全性。“母亲的娘家姓”之类的问题通常可以被攻击者猜测,并且不够安全。

比较问题

  • 了解编程语言中的比较类型并使用正确的类型
  • 如有疑问(尤其是使用PHP时),请使用严格比较(PHP:“===”)
  • 当比较字符串是否相等时,请确保您实际上是在检查字符串是否相等,而不是一个字符串是否包含另一个字符串

PHP特定问题

  • 不要使用简写形式“<?”,始终使用完整形式“<?php
  • 当使用nginx web服务器时,请确保正确遵循**官方**安装说明并注意“陷阱”页面。注意一些教程中经常包含有效的但不安全的配置示例。
  • 在某些情况下,preg_replace可以充当eval()。避免将用户输入传递给它。如果必须这样做,请正确过滤和转义它。
  • 使用Suhosin(如果可能,包括补丁)并使用严格规则进行配置
    • 启用suhosin.executor.disable_emodifier
    • 如果可能,启用suhosin.executor.disable_eval
    • 如果可能,将suhosin.mail.protect设置为2
  • 从旧版本更新PHP到PHP 5.4时,请确保旧版应用程序不依赖于magic quotes来保证安全。

预取和蜘蛛

  • 对于触发任何操作的任何内容,请使用POST请求而不是GET请求

特殊文件

  • 了解这些文件的含义
  • 确保robots.txt不泄露“秘密”路径
  • 除非需要,否则确保crossdomain.xml和clientaccesspolicy.xml不存在
  • 如果使用,请确保crossdomain.xml和clientaccesspolicy.xml仅允许来自受信任域的访问
  • 防止用户上传/更改特殊文件(请参阅文件上传漏洞部分

SSL、TLS和HTTPS基础

  • 遵循SSLLabs最佳实践,包括
    • 确保禁用SSLv2
    • 自己生成证书的私钥,不要让您的CA来做
    • 使用合适的密钥长度(通常在2013年为2048位)
    • 如果可能,禁用客户端发起的重新协商
    • 考虑手动限制/设置密码套件
  1. Patrick Mylund Nielsen。“安全存储密码”
  2. Wikibook密码学/安全密码描述了为密码存储设计哈希算法的历史和理论。
华夏公益教科书