breach attack_解决BREACH漏洞的实用方法

breach attack

两周前,CERT发布了一个名为BREACH的新漏洞的公告 。 他们在通报中说,没有针对此漏洞的实际解决方案。 我相信我已经提出了一个可行的解决方案,我们可能会在Play Frameworks CSRF保护中实现该解决方案。

一些背景

首先,什么是BREACH漏洞? 我建议您阅读该建议,这里没有必要重复我的内容,但是对于那些懒惰的人,这里是一个摘要。 利用此漏洞的先决条件是:

  1. 目标页面必须使用HTTPS,最好使用流密码(例如RC4),尽管在使用带填充的分组密码(例如AES)时可以利用
  2. 目标页面必须使用HTTP级别压缩,例如gzip或deflate
  3. 目标页面必须产生其中包含静态机密的响应。 一个典型的示例是表单中的CSRF令牌。
  4. 目标页面还必须在响应中反映请求参数。 如果它在响应中反映了POSTED表单正文值,也可能会利用。
  5. 否则,响应必须是静态的。 动态响应,尤其是那些会改变响应长度的响应,其开发成本要高得多。
  6. 攻击者必须能够窃听连接,尤其是要测量加密响应的长度。
  7. 攻击者必须能够强迫受害者浏览器多次请求目标网页。

为了加以利用,攻击者使受害者浏览器提交特制请求。 这些请求将包含压缩算法将压缩的重复模式。 如果该模式与机密的第一部分匹配,则响应将比不匹配时短,因为该机密的该部分也将与重复模式一起被压缩。 然后,一个字符一个字符,攻击者就可以确定秘密。

一些解决方法

咨询中提到了一些解决方法。 这些变通办法是否有效,在很大程度上取决于特定的应用程序,在没有潜在破坏应用程序的前提下,框架无法有效地完成这些工作。

可能最有效的解决方法是根据每个请求随机化秘密。 对于通常由许多框架提供的CSRF保护令牌,这将阻止用户同时使用多个选项卡中的应用程序。 用户使用“后退”按钮时,也会引起问题。

我想提出一种使用随机令牌的变体,该变体应适用于大多数框架提供的CSRF保护机制,并且在互联网上收到有关我的方法是否有效的反馈之前,我们可能会在Play框架中实施。

签名随机数

这个想法是使用静态机密,但将其与随机数结合起来,对机密和随机数进行签名,并对发送机密的每个响应执行此操作。签名将有效地创建一个令牌,该令牌在每个响应中都是随机的,因此违反了上述第三个先决条件,即机密是静态的。

随机数不需要以密码安全的方式生成,它可以是可预测的值,例如时间戳。 重要的是,随机数应该足够频繁地更改,并且不应该频繁地重复旧值,这样就不可能使使用相同随机数的许多响应返回。 签名是令牌的不可预测的部分。

应用服务器将需要具有一种使用共享机密对随机数和机密进行签名的机制。 对于从多个节点提供服务的应用程序,秘密将需要在所有节点之间共享。

该应用程序将使用两种类型的令牌来表示机密,一种是“原始令牌”,这只是原始机密,另一种是“签名令牌”。 签名令牌是每次使用都会生成随机数的令牌。 该随机数与原始令牌连接在一起,然后签名。 在Scala中执行此操作的算法可能如下所示:

def createSignedToken(rawToken: String) = {
  val nonce = System.currentTimeMillis
  val joined = rawToken + "-" + nonce
  joined + "-" + hmacSign(joined)
}

其中, hmacSign是使用HMAC算法使用应用程序共享密钥对输入String进行签名的函数。 HMAC不是唯一可以使用的签名算法,但是对于这些类型的用例,它是一个非常常见的选择。

每次在响应中发送令牌时,它必须是新生成的签名令牌。 虽然可以在HTTP响应标头中发布原始令牌,但是为了避免混淆哪些传入令牌必须签名以及哪些可以是原始令牌,我建议始终发布并仅接受签名令牌。 比较令牌时,应在每个令牌上验证签名,如果通过了,则仅需要比较令牌的原始部分。 从使用上述算法创建的签名令牌中提取原始令牌的算法可能如下所示:

def extractRawToken(signedToken: String): Option[String] = {
  val splitted = signedToken.split("-", 3)
  val (rawToken, nonce, signature) = (splitted(0), splitted(1), splitted(2))
  if (thetaNTimeEquals(signature, hmacSign(rawToken + "-" + nonce))) {
    Some(rawToken)
  } else {
    None
  }
}

其中, thetaNTimeEquals在字符串的长度相等时与Θ(n)时间进行字符串比较,以防止计时攻击。 验证两个令牌是否匹配如下所示:

def compareSignedTokens(tokenA: String, tokenB: String) = {
  val maybeEqual = for {
    rawTokenA <- extractRawToken(tokenA)
    rawTokenB <- extractRawToken(tokenB)
  } yield thetaNTimeEquals(rawTokenA, rawTokenB)
  maybeTrue.getOrElse(false)
}

为什么这样做

使用签名令牌时,攻击者仍然可以算出使用BREACH漏洞的原始令牌,但是,由于应用程序不接受原始令牌,因此这对攻击者无济于事。 由于攻击者没有用于签名的令牌的秘密,因此他们无法从原始令牌自己生成签名的令牌。 因此,他们不仅需要确定原始令牌,还需要确定整个签名令牌。 但是由于签名的令牌对于每个响应都是随机的,因此这打破了上面的第三个前提条件,即响应中的秘密必须是静态的,因此它们不能使用BREACH漏洞逐字符进行评估。

加密令牌

另一种选择是加密级联的随机数和原始令牌。 这可能会导致令牌更短,因此我不知道HMAC和AES在此方面的主要性能差异。 安全地使用HMAC签名的API确实比使用AES加密的API更容易安全使用,这就是为什么我将HMAC签名用作我的主要示例的原因。

框架注意事项

可能阻止框架实现此目标的主要问题是,他们可能不容易拥有用于进行签名或加密的秘密。 当应用程序在单个节点上运行时,可以在启动时生成新密码,尽管这意味着密码在每次重新启动时都会更改。

某些框架(例如Play框架)确实具有适用于应用程序的秘密,因此,此解决方案可在应用程序提供的基于令牌的保护机制(例如CSRF保护)中实施。


翻译自: https://www.javacodegeeks.com/2013/08/a-practical-solution-to-the-breach-vulnerability.html

breach attack

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值