自定义安全 HTTP 标头是不是违反关注点分离

Posted

技术标签:

【中文标题】自定义安全 HTTP 标头是不是违反关注点分离【英文标题】:Does custom security HTTP headers violate separation of concerns自定义安全 HTTP 标头是否违反关注点分离 【发布时间】:2017-05-21 03:42:39 【问题描述】:

自定义应用程序特定的、与安全相关的 HTTP 标头是否违反了关注点分离,这是否被认为是一种不好的做法?我意识到使用自定义标头来控制服务会将客户端与服务实现紧密耦合。或者在这种情况下,控制安全框架的行为。我计划使用自定义标头的上下文如下:

我们正在使用基于令牌的身份验证,其中令牌具有固定的生命周期,并且每次经过身份验证的客户端调用 Web API 时都会发出新令牌。 SPA 客户端可以在两种情况下使用 AJAX 调用服务器

用户操作(导航和提交) 自动刷新(当前视图以固定间隔重新获取数据)

现在,如果用户让页面保持打开状态,则会话永不过期,因为每次自动提取都会生成新令牌。 不知何故,我们需要在服务器端区分用户操作和自动刷新,并仅为用户操作颁发新令牌

我意识到基于 Websocket 的刷新将是一种解决方案,但由于具体问题,我们决定坚持使用定时 AJAX 调用。另一种解决方案是将令牌刷新作为单独的端点提供,但这从客户端的角度来看会违反 DRY 原则,并且使用 Spring Security 进行设置会更加麻烦。

唯一剩下的选择是将用户/自动信息嵌入到请求本身中,使用标头在这里似乎是一个可行的选择。某些标头的存在会阻止令牌刷新。只需几行代码即可轻松实现。

我只担心,如果这将客户端与服务实现过度耦合。从技术上讲,它不是将客户端与服务耦合,而是将前面的安全过滤器耦合,从而在用户界面中泄露安全问题。理想情况下,安全性内容应该对用户界面透明,因此可以在不了解任何安全性的情况下对新客户端进行编码(尤其是在使用 cookie 时)。

另一方面,这种解决方案不是破坏性的或变异的。这是一个可选功能。通过客户端使用它,安全性得到了增强,但在任何一种情况下都不会降低(从服务器的角度来看,实际上是这样)。现在的问题是,使用可选标头来增强安全性违反了哪些原则,在这种情况下它是一个有效的解决方案吗?

在我的选择中,应该透明地最大化安全性,但我不知道在这种情况下如何不泄露客户端的安全问题。

【问题讨论】:

【参考方案1】:

听起来您在这里使用的是自己构建的自定义令牌身份验证解决方案。这不是一个好主意。

我会花一点时间解释为什么你不想做你提议的事情,然后更好的选择是什么。

首先,您在这里要解决的问题是,您不希望用户在打开标签页时永远保持登录到您的网站。您需要解决此问题的原因是因为现在,您正在为用户的每个请求分配一个新的访问令牌。

处理上述问题的正确解决方案是拥有两种类型的令牌。

一个生命周期很短(比如:1 小时)的访问令牌和一个生命周期较长(比如:24 小时)的刷新令牌。

这个应该的工作方式是:

当用户首次对您的服务进行身份验证时,会生成访问令牌和刷新令牌,并具有各自的超时时间。 这些令牌都设置在客户端 JS 无法访问的 HTTP cookie 中。 从现在开始,每次您的用户浏览器向您的服务发出请求时,您都会从 cookie 中解析出访问令牌,检查它是否有效,然后允许该请求。 如果访问令牌不再有效(如果它已过期),您将从 cookie 中解析出刷新令牌,并查看它是否有效。 如果刷新令牌有效,您将生成一个具有另外 1 小时生命周期的新访问令牌,并使用新的开启覆盖旧的访问令牌 cookie。 如果刷新令牌无效,您只需将 301 重定向返回到应用的登录页面,强制用户再次手动重新验证。

这个流程有很多好处:

有一个最大会话长度,它是技术性的(刷新令牌的持续时间 + 访问令牌的持续时间)——在本例中也就是 25 小时。 访问令牌的寿命很短,这意味着如果令牌以某种方式遭到破坏,攻击者无法长时间使用它来冒充用户。

上述流程的好处在于它是一个网络授权标准:OAuth2。

OAuth2 密码授予流程完全符合您的描述。它生成两种类型的令牌,处理“刷新”令牌,以安全、符合标准的方式从头到尾处理整个事情。

我强烈建议您在服务器和客户端上实现 OAuth2 库,它将满足您的这些需求。

现在——关于令牌,现在大多数 OAuth2 实现都会生成JSON Web Tokens 的令牌。这些是加密签名的令牌,可提供许多安全优势。

无论如何:我希望这会有所帮助!我用 Python、Node 和 Go 编写了几个流行的身份验证库——所以这是我在过去几年中使用这些协议的直接经验。

【讨论】:

这是否意味着用户必须在 24 小时后重新进行身份验证,即使他/她正在不停地使用系统?此外,这比仅使用具有较长固定生命周期的单个(非刷新)令牌更安全吗?如果攻击者可以窃取短暂的访问令牌,为什么他也不能窃取刷新令牌呢?是否有某种机制可以防止这种情况发生? 没错。这更安全,因为如果令牌以某种方式被泄露(被您的后端泄露,或者如果您的后端被泄露并且访问令牌可以以某种方式可见),它只会持续 1 小时。这不是一种万无一失的方法,但其想法是寿命较短的令牌更安全,因为它们的活跃寿命较短,因此不易受到永久性损害。这就是为什么 OAuth 比使用静态 API 密钥的 Basic Auth 大受欢迎的原因。 但是如果攻击者获得了刷新令牌,他仍然可以冒充用户并获得新的访问令牌,对吧?所以我仍然不明白使用两个令牌如何提高安全性。这比仅使用具有长生命周期的单个令牌并以与刷新令牌相同的方式存储它有什么好处? 理想情况下,刷新令牌不会在每次请求时都发送到服务器——仅当访问令牌已失效时。这样,通过网络传递令牌的次数就会减少。 它并没有极大地提高安全性,并且可以说它使安全性变得更糟。充其量,如果通道被破坏,有人只需平均半小时嗅探流量即可获得寿命更长的令牌。如果数据库被破坏,他们会寻找寿命更长的令牌,而不是临时令牌,这使得差异在很大程度上没有实际意义。最好的部分是,通过使长寿命令牌仅持续一天,如果通道被破坏,您也每天发送密码,可能会访问多个站点,这与仅允许访问一个的持久 cookie 不同。 ...

以上是关于自定义安全 HTTP 标头是不是违反关注点分离的主要内容,如果未能解决你的问题,请参考以下文章

Spring自定义注解

当请求 API 的自定义标头错误时,将 http 状态代码设置为 417

jquery $.ajax 自定义 http 标头问题

为 iframe 设置自定义 HTTP 请求标头

创建自定义助手类?

如果授权标头不存在,则无法在 Webfilter 中发送自定义正文