Web 应用程序安全检查表

概述

对于开发人员来说,这很可怕!代码中的一个错误,依赖项中的一个漏洞,一个受感染的开发人员工作站,你的数据库在 Pastebin 中,你就上了新闻。

那么,到哪里寻求指导呢?OWASP 的前 10 名列表太短了,并且更多地关注列出漏洞而不是防御。相比之下,ASVS是一个很好的列表,但对于实际用途来说仍然有些神秘和模糊。

该清单是对黄金分割的尝试。我们将介绍 68 个实用步骤,您可以采取这些步骤从各个角度保护您的 Web 应用程序。让我们开始!

在浏览器端防御威胁

作为开发人员,您可以帮助减轻最终用户方面的一些威胁。他们包括:

  • 通过用户浏览器中的恶意网站/链接进行攻击。
  • 对用户本地网络的攻击。
  • 有人在用户之前或之后访问共享设备的攻击。例如,如果用户数据仍然存储在浏览器缓存中,其他计算机用户可以稍后检索它。

让我们从针对这些威胁的对策开始我们的清单。

使用 HTTPS 并且仅使用 HTTPS 来保护您的用户免受网络攻击

这个你可能已经知道了。加密用户的网络浏览器和网络服务器之间的所有连接。禁用一些较旧的密码套件和协议也没有什么坏处。

对网站的“敏感”部分进行加密是不够的。攻击者可以拦截单个未加密的 HTTP 请求,然后伪造来自服务器的响应,其中包含恶意内容。

幸运的是,现在 HTTPS 很容易。您可以免费获得证书 (LetsEncrypt) 和自动证书创建/管理 (CertBot)。

进一步阅读

继续我们的清单,接下来是 HSTS,它与 HTTPS 密切相关。

使用 HSTS 和预加载来保护您的用户免受 SSL 剥离攻击

HSTSStrict-Transport-Security是您的服务器可用于强制加密连接的标头。它说,从这里开始,始终使用加密连接 (HTTPS) 连接到我的域

HSTS 将阻止所谓的SSL 剥离攻击,即网络上的攻击者拦截浏览器发出的第一个 HTTP 请求(通常是未加密的)并立即伪造对该未加密 HTTP 请求的回复,假装是服务器并降级从那时起与截获的明文 HTTP 的连接。

一个警告是,HSTS 只会在用户之前已经成功访问​​过应用程序的情况下保护应用程序。为了克服这个限制,您应该将您的站点提交到[https://hstspreload.org] ( https://hstspreload.org ),以便浏览器供应商可以将您的域硬编码到 HSTS 列表中。

例子

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

警告

实施 HSTS 时要注意。它将强制加密流量到的网站,如果您仍然有纯文本,您的网站可能会崩溃。因此,从一个小的开始,max-age一旦你确信一切仍然正常运行,就可以增加它。并将预加载作为最后一步,因为取消很痛苦。

进一步阅读

提供具有“安全”属性的 cookie,以保护您的用户免受网络攻击

Secure使用属性配置您的 cookie 。此属性将防止它们通过(意外或强制)未加密的连接泄露。

Set-Cookie: foo=bar; ...other options... Secure

进一步阅读

安全生成 HTML 以避免 XSS 漏洞

要避免 XSS(跨站点脚本)漏洞,请使用以下方法之一:

  1. 完全静态的网站(例如,JavaScript SPA + 后端 API)。避免生成 HTML 问题的最有效方法是根本不生成 HTML。也许试试 NexJS。它太酷了。
  2. 一个模板引擎。假设您有一个传统的 Web 应用程序,其中 HTML 在后端服务器上生成和参数化。在这种情况下,不要通过字符串连接来制作 HTML。相反,请使用模板引擎,例如TwigPHP、ThymeleafJava、Jinja2Python 等。

如果您使用模板引擎,请确保其配置正确以正确自动编码参数,并且不要使用任何绕过自动编码的“不安全”功能。并且不要将 HTML 放在危险的地方,例如事件处理程序代码、未引用的属性或 href/src。

进一步阅读

安全使用 JavaScript 避免 XSS 漏洞

为避免 JavaScript 端的 XSS(跨站点脚本)漏洞,请勿将任何不受信任的数据传递到可能最终执行代码的函数或属性中。您必须在这里使用常识,但一些常见的嫌疑人是:

  • evalsetTimeout,setInterval
  • innerHTML, 反应的dangerouslySetInnerHTML等。
  • onClickonMouseEnter,onError
  • href,src等。
  • location,location.href等。

进一步阅读

清理和沙箱不受信任的内容,以避免 XSS 和其他漏洞

最好避免不受信任的内容。但有时,您必须从例如远程源检索原始 HTML,然后将其呈现在您的网站上。或者,也许您必须允许您的用户使用所见即所得的编辑器来撰写帖子。有很多用例。

为避免这些场景中的 XSS(跨站点脚本)漏洞,请先使用内容清理内容DOMPurify,然后将其呈现在沙盒框架中。

即使您的 WYSIWYG 库声称要从 HTML 中删除邪恶,您仍然可以通过重新净化和沙箱化内容来打破这种信任关系(“我相信我的 WYSIWYG 库来清理内容”)。您破坏的信任关系越多,您的应用程序就越安全。

还有另一个常见的用例,您想在页面上显示例如广告。在这种情况下,使用 anIFRAME不够的,因为同源策略出于某种原因允许跨域框架将父框架(您的网站)的 URL 更改为例如网络钓鱼站点。在这些情况下,请始终使用sandboxiframe 属性来防止这种情况。

进一步阅读

实施有效的内容安全策略以保护您的用户免受 XSS、xsleak 和其他漏洞的侵害

内容安全策略 (CSP)可作为 XSS(跨站点脚本)攻击的出色保护。除其他外,它还可以防止点击劫持攻击。

所以请务必使用它!默认情况下,CSP 会阻止几乎所有内容,因此放入其中的东西越少越好。例如,以下是一个很好的开始策略:

Content-Security-Policy: default-src 'self'; form-action 'self'; object-src 'none'

它允许从 Web 应用程序的来源加载脚本、样式、图像、字体等,但仅此而已。最值得注意的是,它将阻止内联脚本 ( <script>...</script>),这使得利用 XSS 漏洞变得困难。

此外,该form-action: 'self'指令可防止在网站上创建恶意 HTML 表单(认为“您的会话已过期,请在此处输入您的密码”)并将其提交给攻击者的服务器。

无论您做什么,都不要指定script-src: unsafe-inline,因为这样您的 CSP 就会失去它的魅力。

最后,如果您担心 CSP 在生产中破坏某些东西,您可以首先在Report-Only模式下部署:

Content-Security-Policy-Report-Only: default-src 'self'; form-action 'self'

进一步阅读

使用 HttpOnly 属性提供 cookie 以保护它们免受 XSS 攻击

HttpOnly使用属性配置您的 cookie 。此属性将防止它们被 JavaScript 代码访问。这使得攻击者在成功进行 XSS(跨站点脚本)攻击时窃取它们更具挑战性。当然,您不能对需要通过 JavaScript 访问的 cookie 执行此操作。

Set-Cookie: foo=bar; ...other options... HttpOnly

进一步阅读

使用适当的 Content-Disposition 标头提供下载,以避免 XSS 漏洞

为了避免在向用户提供下载内容时出现 XSS(跨站点脚本)漏洞,请使用指示附件的Content-Disposition标头发送它们。这样,文件将不会直接在最终用户的浏览器中呈现,从而在 HTML 或 SVG 文件的情况下导致 XSS 漏洞。

Content-Disposition: attachment; filename="document.pdf"

假设您希望在浏览器中打开某些特定文件(出于可用性原因可能是 PDF 文档),并且您知道这样做是安全的。在这种情况下,您可以省略标题或更改attachment为该inline特定文件扩展名/扩展名。

进一步阅读

使用适当的 Content-Disposition 标头提供 API 响应,以避免反射下载漏洞

一种称为反射文件下载 (RFD) 的攻击通过制作一个 URL,该 URL 作为恶意文件扩展名从您的 API 下载,反映其中的恶意负载。

您可以通过在 API HTTP 响应中返回Content-Disposition带有保险箱的标头来防止这种攻击。filename

Content-Disposition: attachment; filename="api.json"

进一步阅读

使用您平台的反 CSRF 机制来避免 CSRF 漏洞

为了防止跨站点请求伪造(CSRF) 漏洞,请确保您平台的反 CSRF 机制已启用并按预期工作。

进一步阅读

验证 OAuth/OIDC 状态参数以避免 CSRF 漏洞

存在与 OAuth/OIDC 相关的 CSRF 攻击,攻击者在不知不觉中使用攻击者的帐户登录用户。如果您使用的是 OAuth/OIDC,请确保您的库正在验证state参数。

进一步阅读

正确使用 HTTP 动词以避免 CSRF 漏洞

切勿使用除POST,PUT或进行任何更改之外的PATCH任何内容。例如,请求通常不包含在反 CSRF 机制中。DELETEGET

进一步阅读

提供具有 SameSite 属性的 cookie,以保护您的用户免受 CSRF 漏洞、xsleaks 和有时 XSS 的侵害

使用SameSite属性配置您的 cookie 。SameSite 将阻止大多数CSRF(跨站点请求伪造)攻击,其中恶意网站代表您不知情的用户提交表单。

它还可以防止许多XS-Leaks

有两种模式,LaxStrict

Lax模式非常适合防止大多数跨站点计时和 CSRF 攻击,除了基于 GET 的 CSRF 漏洞,您会在 GET 请求处理程序中错误地进行更改(例如,修改某些数据库记录)。该Strict模式也可以防止这种错误被利用。

但是,该Strict模式还有另一个强大的副作用。它也使得反射 XSS(跨站点脚本)漏洞实际上也无法被利用。

Strict模式不太适合大多数应用程序,因为它会破坏经过身份验证的链接。如果您的用户已登录并在另一个网站上打开指向该应用程序的链接,则打开的选项卡/窗口将不会为用户登录。由于严格模式,会话 cookie 不会与请求一起标记。

但至少SameSiteLax模式下实现,这样做并没有什么坏处,而且它可以很好地防御 CSRF 和跨站点定时攻击。

Set-Cookie: foo=bar; ...other options... SameSite=Lax

...或者:

Set-Cookie: foo=bar; ...other options... SameSite=Strict

进一步阅读

在登录时创建一个新的会话 ID 以防止会话固定攻击

我们清单上的下一个是会话固定攻击。以下是它们的工作方式:

  1. 例如,攻击者将 cookie 注入JSESSIONID=ABC123到您用户的浏览器中。攻击者有很多方法可以解决这个问题。
  2. 您的用户使用他们的凭据JSESSIONID=ABC123登录,在登录请求中提交攻击者选择的 cookie。
  3. 您的应用程序对 cookie 进行身份验证,并且从该点开始对用户进行身份验证。
  4. 从那时起,同样拥有 cookie 的攻击者也以用户身份登录

为防止这种情况发生,请创建一个新的、经过身份验证的会话 ID 并将其返回给用户,而不是对可能被泄露的现有 cookie 进行身份验证。

进一步阅读

正确命名您的 cookie 以防止会话固定攻击

您没想到会在应用程序安全检查表中找到 cookie 命名,对吗?这不是很广为人知,但说到饼干,名字很重要!命名您的 cookie__Host-Something和网络浏览器将...

  1. 不允许通过未加密的连接设置 cookie,以防止会话固定攻击和其他与攻击者强制将 cookie 插入用户浏览器相关的威胁。
  2. 不允许子域覆盖 cookie,这可以防止来自受损/恶意子域的类似攻击。
Set-Cookie: __Host-foo=bar ...options...

进一步阅读

提供适当的 Cache-Control 标头以保护您的用户数据免受后续计算机用户的影响

默认情况下,Web 浏览器会缓存他们看到的所有内容,以加快页面加载速度并节省网络带宽。

缓存是存储访问过的网站和下载的文件在磁盘上未加密的同义词,直到有人手动删除它们

您的应用程序的用户应该能够相信,一旦他们注销,他们就会被注销,并且他们可以安全地离开(例如,图书馆)计算机。

Cache-Control出于这个原因,您应该在所有包含非公共/非静态内容的 HTTP 响应中适当地返回一个名为的标头。

Cache-Control: no-store, max-age=0

进一步阅读 缓存控制

注销时提供 Clear-Site-Data 标头,以保护您的用户数据免受后续计算机用户的影响

另一个用于确保在注销时清除用户数据的有用标头是新Clear-Site-Data标头。当用户注销时,您可以在 HTTP 响应中发送它。浏览器将清除域的缓存、cookie、存储和执行上下文(在撰写本文时尚未实现)。大多数浏览器都支持它;Safari 显然仍然没有。

您可以按如下方式发送:

Clear-Site-Data: '*'

进一步阅读 清除站点数据

正确注销您的用户以保护他们的数据免受后续计算机用户的影响

确保注销会使访问令牌/会话标识符无效。如果它后来从浏览历史/缓存/内存/等泄露给攻击者,它应该不再可用。

此外,如果有 SSO,请不要忘记正确调用单点注销端点。否则,注销将是徒劳的,因为仅单击“登录”按钮就会在 SSO 会话仍处于活动状态时自动将用户重新登录。

最后,清除您可能使用过的所有 cookie、HTML5 存储等。Clear-Site-Data例如,Safari 尚不支持上述内容,因此您还必须手动清除数据。

将 SessionStorage 用于 JavaScript 机密以保护您的用户数据免受后续计算机用户的侵害

它类似于 LocalStorage,但每个选项卡都是唯一的,并在关闭浏览器/选项卡后清除。因此,用户数据有可能泄露给下一个计算机用户。

注意 如果您想让您的用户在应用程序的多个选项卡中进行身份验证而不再次登录,则必须使用事件在选项卡之间同步 sessionStorage。

进一步阅读 会话存储

不要在 URL 中传输敏感数据,因为 URL 并非旨在保密

URL 地址并非设计为保密的。例如,它们会显示在屏幕上、保存到浏览历史记录中、与引荐来源头一起泄露以及保存在服务器日志中。所以不要把秘密放在那里。

使用引荐来源网址策略防止 URL 地址泄漏到其他网站

我们清单上的下一个:推荐人政策。默认情况下,当您从您的应用程序链接到一个网站,并且用户单击该链接时,Web 浏览器将发送一个Referrer标头来告诉该网站哪个网站链接到它。此标头包含整个 URL,这至少可能是一个隐私问题。

Referrer-Policy您可以通过在 HTTP 响应中指定标头来禁用此行为:

Referrer-Policy: no-referrer

进一步阅读

为您的应用程序使用唯一的域名,以保护它免受同一来源下的其他应用程序的影响(反之亦然)

托管这样的应用程序很危险:https://www.example.com/app1/https://www.example.com/app2/. 浏览器认为它们是相同的origin,即相同的主机、端口和方案。并且由于同源,他们将可以完全访问对方。任何影响 app1 的漏洞/恶意内容也会使 app2 处于危险之中。

出于这个原因,给每个应用程序一个他们自己的起源。所以解决方案可能是https://app1.example.com/and https://app2.example.com/

注意 共享父域的子域仍然可以为整个域设置cookie。例如,app1.example.com可以设置一个 cookie,example.com然后将其发送到app2.example.com. 能够为网站设置 cookie 有时会使会话固定等攻击成为可能。

如果您现在想知道.herokuapp.com 下的所有应用程序是否都存在漏洞,答案是否定的,因为公共后缀列表。此外,您可以通过将 cookie 命名为“ _ _ Host- ”来防止 cookie 被子域覆盖

进一步阅读

除非必须,否则不要使用 CORS,如果必须,请小心使用

Web 浏览器的安全模型主要基于同源策略,该策略阻止evil.example.com阅读您的电子邮件,但仍允许您从code.jquery.com. CORS 或跨域资源共享是一种允许其他网站违反该政策的方法。

因此,如果您决定需要它,请确保您知道自己在做什么。

进一步阅读

验证原点

如果您api.example.com需要通过 GET 请求访问它,www.example.com那么您可以在 上指定以下标头api.example.com

Access-Control-Allow-Origin: https://www.example.com

如果您有一个公共 API(假设您希望整个 Internet 从客户端 JavaScript 使用一个计算器),那么您可以指定一个通配符来源:

Access-Control-Allow-Origin: *

如果您有多个要允许但不是全部的域(例如,您只想允许 Google 和 Facebook 访问您的 API),那么您必须Origin从请求中读取标头,将其与允许的域列表进行比较,然后根据需要返回一个标题。建议为此使用经过严格审查的库,而不是手动弄乱标题,因为很多可能会出错。

请注意“允许凭据”选项

默认情况下,CORS 不允许有凭据的请求,即携带用户(会话)cookie 的请求。但是,Web 服务器可以通过指定以下标头来允许这样做:

Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Credentials: true

这组 CORS 标头很危险,因为它允许https://www.example.com像登录用户一样完全访问指定标头的网站。因此,如果您必须使用它,请非常小心。

验证方法

尽量减少攻击面并只允许您需要的 HTTP 方法是一个很好的做法。

Access-Control-Allow-Methods: GET

笔记

如果您不需要 CORS,请不要使用它。默认情况下,它被禁用。

进一步阅读

正确使用 WebSockets 避免 CSRF 和其他漏洞

清单上的下一个:WebSockets。WebSockets 仍然很新,几乎没有文档记录,使用它们时存在危险。所以请仔细阅读以下内容。

1.加密连接

就像你应该使用https://代替http://,使用wss://代替ws:///

HSTS 也会影响 WebSockets,并且会自动将未加密的 WebSocket 连接升级到wss://! 冰雹 HSTS。

2. 验证连接

如果您使用基于 cookie 的身份验证并且 WebSocket 服务器与应用程序位于同一域中,则您可以继续使用现有会话进行 WebSocket 连接。请注意下一节有关原产地验证的内容,否则您会被搞砸的。

如果没有,您可以在应用程序中创建票证,即绑定到用户 IP 地址的一次性、有时间限制的身份验证令牌,可以对 WebSocket 连接进行身份验证。

3.验证连接的来源

了解 WebSocket 的一个关键是同源策略不会绑定它们。任何网站都可以打开与您的应用程序的 WebSocket 连接,如果您使用基于 cookie 的身份验证,则可以访问登录用户的信息。

因此,您必须在 WebSocket 握手中验证连接的来源。您可以通过验证Origin请求标头来做到这一点。

如果您想要双重安全性,请将 CSRF 令牌作为 URL 参数输入。但是为作业创建一个一次性唯一令牌,不要使用您用来保护应用程序其余部分的 CSRF 令牌(因为在 URL 中发送某些内容可能会在许多地方泄漏)。

进一步阅读

使用 U2F 令牌或客户端证书保护您的关键用户免受网络钓鱼攻击

如果您的威胁模型包括网络钓鱼攻击,即“如果攻击者创建了一个从我们的管理员/CEO/等处窃取用户名、密码和 MFA 代码的假网站怎么办”,那么您应该使用U2F 令牌或客户端证书,即使攻击者拥有用户名、密码和 MFA 代码,也无法伪造。

注意 对于普通用户来说,实施网络钓鱼保护通常是多余的。但是,如果最终用户愿意的话,为最终用户提供使用服务的可能性(例如,他们的 YubiKeys )并没有错。但是,您始终可以做的是向用户展示有关网络钓鱼攻击的一般提示。

进一步阅读 创建不可钓鱼的安全密钥

实施针对跨站点泄漏的保护

XS-Leaks(或 Cross-Site Leaks)是一组浏览器侧通道攻击。它们使恶意网站能够从其他 Web 应用程序的用户那里推断数据。

这些攻击已经存在了很长时间,但浏览器最近才开始添加新的机制来防止它们。阅读本文,了解有关攻击和您应该实施的安全控制的详细信息。

在服务器端防御威胁 - 应用程序

正确验证输入以保护您的应用程序免受如此多的漏洞

此清单中最关键的事情之一:尽可能严格地验证所有输入。适当的验证将使许多漏洞难以发现和利用。拒绝无效输入,不要对其进行清理。

  • 使用限制性数据类型。DateTime 用于日期,Integer 用于数字,等等。将枚举用于可能值的列表。尽可能避免使用字符串。
  • 当你必须使用字符串时,如果可以的话,给它设置一个长度限制。
  • 当您确实必须使用字符串时,请将字符集限制为最少。
  • 如果您处理 JSON,请使用 JSON 模式。
  • 如果您处理 XML,请使用 XML 模式。

优雅地捕获异常以避免泄露技术细节

永远不要向最终用户显示堆栈跟踪或类似的调试信息。准备好全局异常处理程序,以捕获其他未处理的异常并向浏览器显示一般错误消息。这将使攻击者更难发现和利用您的应用程序中的漏洞。

不要自己进行身份验证

在对用户进行身份验证时,有太多事情可能会出错。防御各种密码猜测和用户枚举攻击、管理密码重置、存储凭据等并非易事。这几乎就像密码学一样:凡人不应该自己做。

相反,使用身份提供者auth0来验证用户并OpenID connect使用广泛使用且安全的软件组件在您的应用程序中实现协议(通常)。如果您不想使用像 auth0 这样的第三方 IDP,您可以自行托管类似KeyCloak.

进一步阅读

对所有内容进行身份验证以减少攻击面

配置您的应用程序,以便默认情况下对所有内容进行身份验证。然后为静态资产创建必要的例外,也许还有一些端点,如登录页面或“退出”页面。

在您的应用程序中使用 MFA 来破坏与身份提供者的信任关系

如果您想包括“如果有人完全破坏 IDP(身份提供者)怎么办?” 在您的威胁模型中,在您的应用程序中使用某种形式的 MFA(多因素身份验证)。即使 IDP 被黑客入侵并且攻击者可以在那里以任何人身份进行身份验证,攻击者仍然不会知道用户的应用程序本身的 MFA 机密。

使用严格的访问控制来防止对数据或功能的未经授权的访问

访问控制并不总是那么容易,但你可以做对。只需集中注意力,就不会出现 IDOR(不安全的直接对象引用)漏洞,因为您忘记检查用户在某些单独的控制器功能中的访问权限。

  • 默认情况下阻止访问所有控制器方法(或等效方法)。
  • 允许按角色访问各个控制器。
  • 还使用方法级安全性来限制对服务功能等的访问。
  • 使用集中的权限评估器来防止对个人记录的未经授权的访问。
  • 使用集中的权限评估器过滤返回给客户端的对象。
  • 使用具有前端 Web 应用程序和后端 API 的架构,然后在每个应用程序/API 中实现相同的访问控制,而不仅仅是面向 Internet 的部分。

为了稍微阐明权限评估器的方法,这里是它的症结所在:

  • 您的数据记录扩展了一个类,该类具有您用于访问控制的某些属性。例如,int ownerId
  • 您经过身份验证的用户有一个 ID。
  • 您有一个权限评估器类,它知道如果对象的ownerId等于用户的,则用户可以访问对象id
  • 然后将该权限评估器插入应用程序平台的访问控制系统,例如 Spring Security 的 PreAuthorize、PostAuthorize、PreFilter、PostFilter 等。
  • 如果您需要比ownerId或类似的更复杂的访问控制,那么您可以设置(例如)一个完整的 ACL 系统。

进一步阅读

使用适当的工具和技术来避免注入漏洞

多个漏洞属于“注入”类别,它们都是相似的。这些包括 SQL 注入、HTML 注入(XSS 的一种形式)、XML 注入、XPath 注入、LDAP 注入、命令注入、模板注入、SMTP 注入、响应标头注入……有很多“不同”的漏洞,在现实,同样的问题,同样的补救措施:

  • 问题:使用字符串连接/格式化来构造协议 X 的参数化消息。
  • 解决方案:为工作使用适当的、经过良好(安全性)测试的软件库并正确使用它。

我们不会在本文中讨论每个注入漏洞,因为列表将是无限的,所以无论您正在构建什么协议,只要记住这条规则即可。我们将在我们的清单中介绍一些更流行/有趣的,例如 SQL 注入。

安全构建数据库查询,避免 SQL 注入漏洞

为避免 SQL 注入漏洞,切勿通过字符串连接构造 SQL 查询。如果可以,请使用 ORM(对象关系映射器)。ORM 将使开发更快,应用程序更安全。

如果您想对查询进行精细控制,请使用低级 ORM(通常称为查询构建器)。

如果您不能使用 ORM,请使用准备好的语句,但要小心,因为它们比 ORM 更容易出现人为错误。

警告

ORM 框架在两种意义上都不是灵丹妙药。

首先是它们仍然具有支持原始 SQL 查询/部分查询的功能。只是不要使用这些功能,你就是黄金。

二是ORM框架时有漏洞,就像任何其他软件包一样。因此,请遵循其他好的做法:验证所有输入,使用 WAF 并保持你的包是最新的,你就可以开始了。

进一步阅读

如果可以避免,请不要执行操作系统命令。它总是有点狡猾。

如果必须这样做,您可以按照以下准则避免命令注入漏洞和相关问题:

  • 使用适当的库/函数来构造和参数化命令。参数应该是list数据类型。切勿将命令创建为单个字符串。
  • 不要使用 shell 来调用命令。
  • 预先确定您输入命令的参数。举个例子,通过curl允许用户指定-o参数,您将允许攻击者写入本地文件系统。
  • 了解程序的作用并适当地验证参数。再次以curl示例为例,您可能希望允许用户检索诸如此类的网站,https://www.appsecmonkey.com/但如果攻击者检索file:///etc/passwd了呢?
  • 认真考虑。即使您验证参数以http://or开头https://,您是否希望攻击者访问http://192.168.0.1/internal_sensitive_service/admin或对内部网络进行端口扫描?
  • 真的想清楚了。即使您验证该参数是不包含 eg 的有效 DNS 主机名yourcompany.local,是否有任何东西阻止攻击者创建指向的公共 DNSwww.example.com记录192.168.0.1?答案是不。可以办到。

进一步阅读

通过正确配置解析器来避免 XML 漏洞

XML 是一种危险的标记语言,它包含用于访问系统资源的功能。XSLT 的一些实现甚至支持嵌入式代码。出于这个原因,您在处理它时必须非常小心。

  1. 如果可以,请避免接受来自不受信任来源的 XML/XSLT。
  2. 如果您参数化 XML、XSLT 或 XPath 表达式,请使用适当的软件组件来执行此操作。这是为了避免注入漏洞。不要使用字符串连接/格式化/等。
  3. 使用众所周知且经过彻底(安全)测试的软件组件来解析 XML/XSLT。这是至关重要的。不要使用糟糕的库或您的代码来处理 XML。此外,在任何情况下,都不要尝试创建用于处理 XML 签名(例如 SAML)的自定义实现,因为有很多事情可能会出错。
  4. 正确配置解析器。禁用documentXSLT。禁用xinclude. 禁用文档类型定义。禁用外部实体。启用 DOS 保护。具体选项会因实现而异,请对您选择的解析器进行一些研究。

进一步阅读

通过使用适当的类进行 URL 构造来避免 URL 注入漏洞

当你有这样的事情时,就会发生 URL 注入:

flavour = request.getParam("flavour");
url = "https:/api.local/pizzas/" + flavour + "/";
return get(url).json();

有人输入这样的值:

../admin/all-the-sensitive-things/

这会导致 API 调用返回响应,https://api.local/admin/all-the-sensitive-things/而不是像开发人员预期的那样返回比萨端点。

与往常一样,解决方案是使用适当的 URL 构造库来参数化 URL,以便对值进行正确编码。

通过使用适当的类来构造路径来避免路径遍历漏洞

就像 URL 地址一样,如果攻击者设法../../../在路径中的某处偷偷摸摸序列,文件路径也可能最终指向不需要的位置。为避免这种情况,请创建一个安全地构造路径并验证最终路径是否在预期目录中的类。避免在文件路径中使用不受信任的数据,或者更好的是,完全避免使用文件系统,而更喜欢例如云存储。

进一步阅读

如果可以避免,请勿将文件系统用于不受信任的内容(例如上传)

当允许您的用户编写服务器的文件系统时,有无数可能出错的事情。请改用云存储,或者如果这对您不起作用,请在数据库中使用二进制 blob。

如果您绝对必须访问磁盘,这些指南可以帮助您确保安全:

  1. 要非常小心,不要让任何不受信任的数据影响内部文件路径的任何部分。
  2. 将文件保存在远离例如 webroot 的隔离目录中。
  3. 在写入磁盘之前验证文件内容是否与预期格式匹配。
  4. 正确设置文件系统权限以防止写入不需要的位置。
  5. 不要提取压缩(例如 ZIP)档案,因为它们可以包含任何文件,包括符号链接和系统上任何位置的路径。

不执行动态代码,避免远程代码执行漏洞

不要使用eval或等效功能。想办法在没有它们的情况下实现你的目标。否则,将存在不受信任的数据到达函数调用的风险,并且有人会在您的服务器上执行任意代码。

谨慎使用序列化以避免反序列化漏洞

不可信数据的反序列化是一种危险的操作,很容易导致远程代码执行。

  1. 如果可以避免,请不要使用序列化。
  2. 如果您可以在服务器端序列化对象,则对它们进行数字签名。当需要再次反序列化它们时,在继续反序列化之前验证签名。
  3. 使用知名的软件组件来完成这项工作,并严格保持最新状态。漏洞一直在许多反序列化库中被发现。GSon是个不错的选择。
  4. 使用简单的文本格式(例如 JSON)而不是二进制格式。此外,应该避免像 XML 这样有问题的格式,因为除了反序列化漏洞之外,您还需要担心 XML 漏洞。
  5. 在处理之前验证序列化对象。例如,在 JSON 的情况下,在继续反序列化之前根据严格的 JSON 模式验证 JSON 文档。

进一步阅读

在服务器端防御威胁 - 基础架构

使用 WAF

在您的应用程序前面放置一个 Web 应用程序防火墙产品。这将使许多漏洞更难被发现和利用。ModSecurity 是一个很好的开源选项。

进一步阅读

仔细配置您的 Web 服务器以避免 HTTP 异步攻击

有一种称为“HTTP Desync”或“Request Smuggling”的攻击,如果满足以下条件,攻击者可能会做各种令人讨厌的事情,例如窃取收集到 Web 应用程序的随机用户的 HTTP 请求:

  1. 有一个前端 Web 服务器,例如负载平衡器/任何反向代理,它接受带有 和 标头的请求Content-LengthTransfer-Encoding不规范化请求的情况下传递它们。
  2. 上线的下一个 Web 服务器,例如应用程序 Web 服务器,使用或可能被欺骗使用与前端 Web 服务器不同的机制来确定 HTTP 请求从哪里开始和在哪里结束,例如前端将使用Content-Length,而应用程序服务器将使用Transfer-Encoding.
  3. 前端 Web 服务器重用与后端 Web 服务器的连接。
  4. 前端 Web 服务器在后端服务器连接中使用 HTTP/1(而不是 HTTP/2)。

那么如何保护自己呢?取决于产品,但一般来说:

  1. 请查阅您正在使用的反向代理产品的文档/供应商,并确保他们积极防御攻击。
  2. 将前端网络服务器配置为在后端连接中使用 HTTP/2。
  3. 配置前端 Web 服务器以防止来自不同客户端 TCP 流的 HTTP 请求聚合到同一服务器端连接中。
  4. 使用 WAF(Web 应用程序防火墙)并确保它具有阻止请求走私企图的模块

进一步阅读

使用容器

单独运行您的应用程序,以便在发生违规事件时,攻击者不会不必要地访问不需要的文件、系统或网络资源。因此,最好使用 Kubernetes 或无服务器云堆栈之类的东西来部署您的应用程序。如果您因任何原因被迫使用裸服务器,请手动运行例如 Docker 来约束应用程序。

进一步阅读

使用 SELinux/AppArmor

即使您在容器中运行应用程序,使用 SELinux 或 AppArmor 策略进一步限制它也是值得的。这将使利用容器逃逸漏洞变得非常困难,以及其他好处。

进一步阅读

使用具有最低权限的服务帐户

当出现问题时,这通常会限制损坏。再一次详尽的列表是不可能的,但这里有几个例子可以理解:

  • 即使您使用 Docker,即使您使用 SELinux/AppArmor,也不要以 root 身份运行应用程序。这将使攻击者更难利用容器逃逸/内核漏洞和其他讨厌的技巧。为应用程序创建具有最小权限的特定用户。
  • 如果您有数据库,请确保应用程序的数据库用户对表、列和 dbms 功能具有最低访问权限。
  • 如果您与 API 集成,请确保应用程序具有访问 API 的最低权限。

限制出口网络连接

攻击者通常需要某种反向通信通道来建立命令和控制通道和/或窃取数据。此外,一些漏洞需要发现和利用出口网络连接。

出于这个原因,您不应该允许从您的应用程序到外部世界的任意连接,这包括 DNS。如果您可以nslookup www.example.com从服务器成功运行,则说明您没有正确限制出口。

你将如何处理这取决于你的基础设施。

出口 TCP/UDP/ICMP 通常可以通过以下一项或多项禁用:

  • 如果您有网关级别的防火墙。
  • 如果您有老式服务器,则本地防火墙(例如 iptables 或 Windows 防火墙)。
  • iptables 如果你在你的服务器上运行 Docker。
  • 如果您使用 Kubernetes,则为 NetworkPolicy 定义。

DNS 有点棘手,因为通常需要允许某些主机使用它。

  • 如果您可以摆脱本地主机文件,那就完美了。这是一个简单的解决方案,您可以完全禁用 DNS(使用前面列表中的任何技术)。
  • 如果不是,那么您必须在上游 DNS 中配置一个专用区域,并将网络级别的访问限制为仅对该 DNS 服务器的访问。该区域应该只解析预先确定的主机名列表。

跟踪您的 DNS 记录以防止子域接管

子域接管是这样发生的:

  1. 你有一个域example.com
  2. 您为广告系列购买了另一个域www.my-cool-campaign.com并创建了一个CNAMEfrom campaign.example.comto www.my-campaign.com
  3. 您的活动结束,并最终www.my-cool-campaign.com到期。
  4. 您仍然拥有CNAME指向campaign.example.com过期域的指向。
  5. 攻击者购买了过期的域,现在在您的域 ( campaign.example.com) 下有一条 DNS 记录指向攻击者控制的域。
  6. 攻击者托管恶意内容www.my-cool-campaign.com,可从其访问https://campaign.example.com

因此,请注意您的 DNS 记录。如果您必须处理大量这样的域名,强烈建议使用自动化的监控解决方案。

进一步阅读

在服务器端防御威胁 - 架构

创建用于访问数据源的内部 API,以摆脱危险的信任边界

您不应该对面向 Internet 的 Web 应用程序过于信任。例如,它不应该直接访问数据库。否则,当有人闯入面向 Internet 的应用程序时,您的整个数据库都将丢失。

相反,将您的架构分成多个组件,例如:

  • 您的 Web 应用程序www.example.com将在 上对您的用户进行身份验证auth0
  • 允许您的 Web 应用程序使用经过身份验证的用户(从 获取)www.example.com连接到内部 API ,然后在调用内部 API 时将其作为标头传递。api.example.localaccess-tokenauth0Authorization
  • 您的 APIapi.example.local将根据(最终用户的)访问令牌强制执行访问控制,并适当地读/写数据库。

现在,如果攻击者完全破坏了您的www.example.com应用程序,则攻击者将无法完全访问整个数据库,而只能访问其访问令牌当时恰好在内存中的个人用户数据。

加密和验证所有连接

不要相信您的内部网络是安全的;有很多方法可以破坏它。使用 TLS 加密所有系统到系统的连接(即使用 HTTPS),并最好在网络和应用程序级别对连接进行身份验证:

  1. Web App -> API:这是我的客户端证书。它由我们信任的 CA 签名,上面写着“CN=WebApp”。
  2. Web App <- API这是我的服务器证书。它由我们信任的 CA 签名,上面写着“CN=API”
  3. Web App -> API:这是我的访问令牌,由我们信任的 IDP 签名,我通过 OAuth2 客户端凭据授予流程获得了它。
  4. Web App -> API: ...这是登录用户“John Doe”的访问令牌,我代表他发出这个请求,该请求也由我们信任的 IDP 签名。
  5. Web App -> API: ...那么你能给我 John Doe 的信息吗?
  6. Web App <- API很高兴。由于这是一个加密且相互认证的连接网络级别,并且因为您在应用程序级别上看起来像是“Web App”,并且因为您似乎在使用“John Doe”的权限进行操作。

集中管理机密

如果没有适当的秘密管理解决方案,就很难让凭证保持短暂的生命周期、记录审计日志并且不让它们暴露在人眼中。出于这个原因(以及许多其他原因),建议使用 HashiCorp vault 之类的工具来集中管理集成机密、加密密钥等。

进一步阅读

在服务器端防御威胁 - 备份

进行备份

你永远不知道什么时候出了问题,所以要备份。

保护您的备份

你的整个数据库都在备份中,所以要非常小心你允许谁访问它们。强烈建议对备份进行加密,这样您就不必太相信没有人可以访问它们。只是不要丢失加密密钥。

测试你的备份

这是至关重要的。养成检查备份是否有效的习惯,当出现问题时,您实际上可以恢复它们。

在服务器端防御威胁 - 监控

收集、分析、警报

将日志集中收集到系统中,例如 SIEM(安全信息和事件监控),您可以在其中针对指示漏洞或攻击的特定事件触发警报。配置警报通道,以便相关人员在发生重大威胁时立即知道。

收集应用程序安全事件

可能最重要的日志源是您的应用程序本身。当可疑行为发生时,您应该提出异常,记录事件,甚至可能自动锁定似乎造成问题的用户/IP 地址。

此类事件可以是(这些只是示例,具体情况在很大程度上取决于您的应用程序):

  • 输入验证错误(例如,试图为不应通过 UI 提供的参数值)。
  • 访问控制错误(例如,尝试访问本不应该通过 UI 访问的记录)。
  • 数据库语法错误表明有人发现了 SQL 注入漏洞,你需要快速行动。
  • XML 错误表明有人发现了 XML 注入漏洞或可能正在尝试查找/利用 XXE(XML 外部实体)漏洞。
  • 错误的请求错误表明最终用户发送了被应用程序拒绝的内容。Spring 框架的 RequestRejectedException 就是一个例子。
  • CSRF 令牌验证错误通常意味着有人在您的应用程序中寻找漏洞。

收集运行时安全日志

使用 Falco 等运行时安全监控工具来检测异常系统调用。如果您碰巧使用 Kubernetes,Falco 特别有用。还可以远程收集和监控这些日志。

进一步阅读

收集 SELinux/AppArmor 日志

如果您有一个阻止传出连接的 SELinux 策略,并且您的应用程序突然尝试向 eg 发出 HTTP 请求burpcollaborator.net,那么立即了解它会非常有用。或者您的应用程序可能会尝试访问/etc/passwd. 这两种情况都表明有人已经在您的应用程序中发现了一个严重的漏洞。

收集网络服务器事件

至少从您的 Web 服务器软件中收集访问日志和错误日志,并将它们也发送到中央日志服务器。这将有助于在事件响应中绘制时间表。

收集 WAF 日志

如果您使用上述推荐的 WAF,请同时收集这些日志。但不一定会触发他们的警报,因为通常,WAF 产品会受到来自 Internet 的各种垃圾的轰炸,大多数时候您不必担心。

在服务器端防御威胁 - 事件响应

有一个计划

一旦你的监控和加固到位,攻击者就不容易发现漏洞,成功利用漏洞的速度会很慢,而且你会很快了解这些尝试——这是个好地方。

但是了解攻击并减缓攻击者的速度是不够的。你仍然需要对它们做点什么。因此,请准备好人员、工具和流程:

  • 快速分析日志并了解正在发生的事情和需要做的事情
  • 快速限制单个 URL 地址或参数,例如应用程序防火墙产品
  • 如果需要,快速关闭应用程序

安全开发注意事项

威胁模型

经历一个思考“可能出什么问题”的过程,然后采取一些措施。最好在开始设计系统时从一开始就这样做,但开始永远不会太晚,无论如何,当您将更改引入系统时,您应该重新访问此过程。

例如:

Jim:如果攻击者破坏了面向 Internet 的 Web 服务器怎么办?

鲍勃:那我们就完蛋了。

Jim:好的,所以我们在那里建立了信任关系,我们相信面向 Internet 的 Web 服务器不会被盗用。我们真的可以相信吗?

Bob:嗯,不,有无数的事情可能导致那个东西被黑客入侵,例如我们自己的代码中的漏洞,或者我们使用的依赖项中的漏洞,或者我们的 Web 服务器软件中的漏洞。

吉姆:对。所以让我们打破这种信任关系。但是怎么做?

Bob:让我们打破整体并创建一个内部 API 来执行实际的数据库访问。然后前端 Web 服务器将无法一次访问所有内容。

吉姆:好主意。那么还有什么可能出错的呢?

Bob:那么,如果攻击者破坏了我们的内部网络怎么办?

Jim:一切都会丢失,服务器到服务器的连接都是未加密的。

鲍勃:……

这是威胁建模,它不必复杂或可怕。用它来发现危险的信任关系,然后打破这些关系。

在源代码控制中强制同行评审

实施一项技术控制,以防止代码在没有至少一两个其他开发人员批准的情况下进入存储库。这是您安全开发生命周期的基础,因为现在发生了两件事:

  1. 如果攻击者破坏了开发人员的工作站或开发人员流氓,将无法直接将恶意代码推送到存储库中。
  2. 如果开发人员犯了错误并试图将易受攻击的代码引入存储库,那么其他审查代码的开发人员很有可能会在错误被合并之前发现错误。

进一步阅读

自动化 CI 管道并限制对它的普通访问

单个开发人员应该能够触发例如 Jenkins 构建,但应该将 Jenkins 配置为允许这样做,仅此而已。个人开发人员不应该能够将任意代码引入构建阶段。但是,您可以将 Jenkinsfile 保留在源代码控制中,只要像上面推荐的那样在技术上强制进行同行评审过程。

签署构建工件

签署工件。例如,如果您正在构建容器镜像,请将镜像作为构建的一部分进行签名。安全地存储签名密钥。构建阶段需要访问密钥,但不应将它们与 Jenkinsfile 一起存储在版本控制中。最好将密钥保存在例如 HashiCorp 保险库中,并在构建时提取它们。

进一步阅读

作为 CI 管道的一部分运行静态应用程序安全扫描程序

在 CI 管道中运行诸如 SpotBugs + FindSecBugs(或适用于您选择的技术的类似工具)之类的工具。这将帮助您在部署代码之前发现代码中的一些已知漏洞。

您还可以在开发人员的工作站上运行这些工具(例如作为 IDE 插件),甚至在将它们检查到版本控制之前就可以发现问题。

进一步阅读

验证对构建的依赖并将它们保持在最低限度

您依赖的每个软件包都是一种风险。您正在从其他人的存储库中提取代码并在您的应用程序服务器上执行它。所以要注意你依赖什么以及如何依赖。

  1. 将依赖项保持在最低限度。
  2. 仅使用您信任的依赖项。它们都应该被广泛使用并享有盛誉。
  3. 使用支持依赖验证的构建框架,并确保验证已启用。

由于额外的强化限制了来自您的应用程序服务器的出口连接(本文前面描述),以防止任何后门“回家”。

进一步阅读

作为 CI 管道的一部分运行依赖项安全扫描程序

运行诸如 OWASP DependencyCheck 之类的工具作为 CI 管道的一部分,以捕获您可能正在使用的一些依赖项,这些依赖项中存在已知的安全问题。

您也可以在开发人员的工作站上运行这些工具(但也要在最重要的 CI 管道中运行它们)。

进一步阅读

运行容器映像安全扫描器作为 CI 管道的一部分

如果您使用容器,请使用 Trivy 等工具扫描创建的容器映像以查找已知漏洞。

进一步阅读

自动化部署和验证签名

个人开发人员很可能有权部署到生产环境,但只有在之前阶段构建和签名的特定映像才能部署。访问生产机密或直接访问服务器应该是不可能的。验证部署映像的签名,例如,如果您使用的是 Kubernetes,则通过 Notary 和 Open Policy Agent 验证容器签名。

进一步阅读

有一个安全冠军

一个人的痴迷程度是有限度的。你不能期望每个开发人员都是熟练的渗透测试员或安全工程师。正如您不能期望所有安全专家都是优秀的开发人员一样。

因此,向您的团队介绍以安全为重点的人员通常是一个好主意,以便与开发人员、架构师等进行讨论,并帮助保护您的应用程序并在团队中传播安全意识。

进一步阅读

结论

保护您的应用程序不仅仅是避免漏洞。总结一些主要思想:

  • 使用最新的、现代的、知名的软件组件来执行有风险的操作,例如身份验证、访问控制、加密、访问数据库或解析 XML。并确保您已正确配置这些组件,例如通过禁用 XML 解析器中的外部实体。
  • 使用您的平台提供的安全控制,例如 CSRF 保护。
  • 使用 Web 浏览器提供的安全控制,例如 HSTS、SameSite cookie 和内容安全策略。
  • 集中您的安全控制,尤其是身份验证和访问控制,以避免您“忘记为某些控制器功能添加安全性”等漏洞。
  • 使用 Web 应用程序防火墙使查找和利用应用程序中的许多类别的漏洞变得困难。
  • 通过限制其对文件、网络和系统资源的访问来控制您的应用程序。
  • 威胁模型以发现架构中任何危险的信任关系,然后破坏它们。这可能包括例如源代码控制策略,以打破对每个开发人员工作站完整性的信任关系,以及打破对前端网络服务器的完全信任而不会受到损害的巧妙架构。
  • 当事情向南时,积极监控并制定计划。
  • 在开发环境和 CI 管道中使用代码/图像/依赖项漏洞扫描程序。
  • 对开发人员、架构师等进行有关安全性的教育,并在团队中拥有一名安全冠军。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值