教开发人员有关跨站点脚本 (XSS) 的一些重要概念是什么?

信息安全 xss 安全编码
2021-08-09 17:45:18

我正在帮助为开发人员(其中约 100 人)提供一小时的跨站点脚本培训。您认为哪些概念对于理解它们是必不可少的?现在我们有:

  • 反射和存储的区别
  • 防御层(WAF、浏览器防御、服务器标头和安全编码)
  • 安全编码和编码参数的上下文

最重要的是,漏洞的潜在影响。

4个回答

从开发人员的角度来看,您拥有的前两点并不是很相关。存储型和反射型 XSS 具有相同的缓解措施:根据上下文正确转义您输出的内容。层层防御可能只会被视为实施不力的缓解措施的借口:“WAF 会为我捕获它。”

相反,请关注以下代码卫生实践:

  • 在输入时验证,而不是转义。输入验证应仅用于确保输入有意义,而不是“安全”。目前不可能知道它是否安全,因为你不知道它会被使用的所有地方。
  • 假设所有数据都是不安全的。永远不要假设某些数据已被转义,或者不包含标签、引号或实体等。了解不安全的输入可能来自任何地方:HTTP 标头、cookie、URL 参数、批量导入数据等。
  • 在使用点逃生。在使用数据时,为使用它的上下文转义数据。只想逃一次以提高性能?根据可以安全使用的位置命名目标变量:jsstringsafeEmailhtmlattrsafeColorhtmltextsafeFullName等。

您认为哪些概念对于理解它们是必不可少的?

  • 反射和存储之间的区别——我真的不在乎。
  • 防御层——是的。许多开发人员不了解“纵深防御”。假设所有其他缓解措施都失败了,并且您的代码是攻击者和易受攻击资源之间的最后一件事。你能做些什么来减轻攻击?
  • 安全编码和编码参数的上下文——绝对如此。
  • 最重要的是,漏洞的潜在影响。 - 绝对地

还有什么?您的清单中缺少的一件大事是:有很多工具和技术都可以用来减轻这些潜在的漏洞;深度防御建议我们尽可能多地使用它们。

  • 设计安全,默认安全。考虑设计和实施所有阶段的漏洞。确保您在必要时选择退出安全,而不是必要时选择加入
  • 制作正式的威胁模型。恶意数据可以从哪里进入系统,又可以从哪里离开?哪些子系统相互信任,哪些不相互信任?如果你不知道边界在哪里,就很难强化它们以抵御攻击。
  • 字符串是敌人。数据应该在可以智能组合的智能对象中流动,而不是可以连接和拆分的字符串。
  • 如果您使用的是具有类型系统的语言,请捕获数据在类型系统中是否受到污染。让编译器告诉您您正在将受污染的数据分配到需要未受污染数据的上下文中。
  • 如果您没有编译器为您找到错误,请在您的命名规则中模拟类型系统。如果变量包含受污染的数据,则在其名称中的某处“受污染”。如果你将一个被污染的值分配给一个名称中没有“污染”的变量,那么你只是在代码审查时发现了一个问题,而不是在攻击者成功时。
  • 使用安全专家设计的静态分析工具来发现这类问题;仔细注意输出,甚至是误报误报表明代码无法被分析器看到是正确的;这意味着它也可能无法被人类认为是正确的。修复代码,使工具不再发现误报。
  • 像攻击者一样测试一切,而不是用户。如果有清理输入的代码,请让您的团队成员对其进行攻击。消毒剂错误是难以发现的漏洞的常见来源。
  • 对生产中的错误配置进行测试。XSS 漏洞可能是由于出于调试或测试目的而意外关闭了某些验证或清理,并且忘记重新打开它,将错误的配置推送到生产环境等等。
  • 等等。

您涵盖了大部分基础知识。扩展一点你的观点(其中一些对这里的大多数读者来说似乎很明显,但对所有开发人员来说可能不一定很明显):

  • XSS 可以通过 GET 和 POST 发生。
  • XSS 也是管理后端的一个问题。
  • 存在基于 DOM 的 XSS。
  • 浏览器防御存在,但不应依赖WAF 和服务器标头也是如此。
  • 编码应该在打印时进行,而不是在接收输入时进行。
  • 上下文确实很重要。在某些情况下,HTML 编码是不够的。
  • JavaScript 和 CSS 转义/编码很困难。建议使用现有库。
  • 所有参数都应该被编码。仅对看似不安全的参数进行编码并不是一个好方法。
  • 强烈建议进行额外的输入过滤(例如,如果您需要一个整数,请检查它是否真的是一个整数,最好在某些 Input 类中进行本地化),但绝不应该是唯一的防线。

但我认为最重要的一点是:HTML 编码应该默认发生,通过使用默认编码所有参数的模板引擎。

如果编码发生在所有地方,很容易忘记一次。当然,处理 HTML 编码不够用的所有情况仍然很重要,但大多数 XSS 漏洞将通过默认编码来阻止。

我认为要教的两个最重要的教训是:

  1. 如果您正在通过将字符串连接在一起以临时方式生成标记或任何其他结构化文本表示(例如,JSON、SQL、URL 查询字符串),那么立即停止您正在做的事情,并找到或构建一个集中的和用于标记或 DOM 生成的安全库。
  2. 不要相信需要您执行以下任何操作的 XSS 预防策略:
    • 猜猜攻击者会尝试什么或以其他方式试图智取攻击者。特别是,输入端过滤器有被攻击者智取的历史,应该被怀疑。
    • 在不同的上下文中一次又一次地识别哪些变量是“不受信任的”输入,然后您必须例外处理。(相反,默认情况下所有输入都应该是不受信任的——你应该选择退出XSS 保护,而不是选择加入它。)

关于我的第一点:几乎所有的注入问题都源于在错误的抽象级别上工作:程序员在他们应该使用从正确的做事方式抽象出来的高级操作的情况下连接字符串。这意味着:

  1. 程序员最终会一遍又一遍地处理完全相同的棘手问题(在插入字符串的上下文中正确转义字符串值)。即使他们每次都做对了(不太可能!),解决方案也永远不会被重用。
  2. 由于正确转义字符串值的责任分散在整个代码库中,而不是集中在一个地方,因此审核代码的安全性和解决问题变得困难了几个数量级。

关于第二点:关于依赖输入端过滤的危险的一个很好的教训是浏览 OWASP 的XSS Filter Evasion Cheat Sheet 将该页面视为记录一次又一次失败的尝试,以及通过输入端过滤解决 XSS 的一次又一次尝试,以及攻击者能够用来绕过它的聪明才智。

您还经常看到很多关于“不可信”输入以及如何处理它们的建议,这充满了危险,因为:

  • 弄清楚哪些输入是可信的,需要做很多工作,尤其是当你必须一遍又一遍地做的时候;
  • 您可能会错误地判断哪些输入是值得信赖的;
  • 今天可以信任的输入明天可能不值得信任。

OWASP 的XSS 预防备忘单是另一个非常出色的文档,它遵循我的两点。它侧重于如何安全地任意字符串插入到动态生成的 HTML、CSS 和 Javascript 文档中。如果您在一个地方(在第三方库或您的代码库中)解决该问题并始终一致地应用该解决方案,那么您将做得很好——您将“切断”XSS 漏洞的“颈部”。