IE 在使用 NTLM 身份验证时随机发送空 POST 正文(使用 angular 到 Spring)
IE sends empty POST body randomly while using NTLM authentication (using angular to Spring)
我们发现在 IE 11 中缺少 POST 的看似随机的调用存在问题。经过进一步检查,来自浏览器的请求包含 NTLM 协商令牌。我们也时不时在 GET 上看到这个 token,但它们不受 body 问题的影响,因为它们没有。 Chrome和FF没有这个问题
进一步调查表明,如果我们的会话超时并且浏览器被重新授权,那么之后我们将以大约 1 - 2 分钟的间隔持续看到此问题。据我了解,如果 IE "expects" 是 401,那么它不会包含 POST 正文,因为它希望 NTLM 握手再次发生。
我们有一个 angular 应用程序(目前混合 Angular 1 和 2,但发生在 1 之前)。 angular 应用程序对我们的 Spring 启动应用程序进行 restful 网络服务调用。我们正在使用 waffle 进行 NTLM 验证
// Shiro-Waffle / Security
'com.github.dblock.waffle:waffle-parent:1.7.3',
'com.github.dblock.waffle:waffle-shiro:1.7.3',
'org.apache.shiro:shiro-core:1.2.3',
'org.apache.shiro:shiro-web:1.2.3',
我有一个糟糕的解决方法,我将在下面 post 作为答案,但我一点也不喜欢它。
我创建了一个 shiro 过滤器,我将其作为链中的最后一个过滤器 (Groovy)
package com.security.shiro
import com.security.AuthenticateTrait
import com.ws.RemoteUserAccessor
import com.ws.RequestAccessor
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse
/**
* Created by esarrazin on 11/30/2016.
*/
class IEBugFilter3 extends NegotiateAuthenticationFilter implements RemoteUserAccessor, AuthenticateTrait, RequestAccessor {
/**
* Returns <code>false</code> If the giant piece of sh1t IE chooses to think that it will need to re-authenticate
* and not send the body to us. Here we will deny access (as IE expects - big turd) and have waffle re-negotiate
* this useless sh1t-bag. Then and only then will IE choose to give us the body of the POST. This problem is intermittent
* and there is no telling when IE will use its infinite wisdom to do this, so please don't think if you comment this
* out it is still working without.
*
* Not a silver bullet, but here is a link to some reading about this:
* https://blogs.msdn.microsoft.com/ieinternals/2010/11/21/challenge-response-authentication-and-zero-length-posts/
*
* @return Returns true if isAccessAllowed
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
!isRequestExpectingToReAuth(request, response)
}
private boolean isRequestExpectingToReAuth(ServletRequest request, ServletResponse response) {
if (request?.request?.request?.coyoteRequest?.methodMB == 'POST' && toHttp(request).getHeader('content-length') == '0') {
System.out.println("Found the dreaded zero length body and attempting to recover from it. $request.remoteHost")
return true
}
return false
}
}
我们的 NegotiateAuthenticationFilter 扩展了 waffle.shiro.negotiate.NegotiateAuthenticationFilter 并且非常简单。
这会看到空主体 POST 请求(请原谅请求?.请求垃圾 - 未准备好产品)并拒绝访问,从而为 IE 提供它所期望的。由于我们拒绝访问,401 通过并开始握手。然后当 200 出现时,IE 释放正文,我们就可以了。但这是废话-尽管是一个解决方案。
好的,我找到了一个注册表 hack。基本上,IE 会主动从服务器发送 NTLM 凭据,而不会出现 401。发送 "pre auth" 时,IE 将省略 POST 正文(如果请求是 POST。)如果会话已经有效并且服务器已经获得您的授权,它会允许请求通过而不正文并会导致服务器出错(假设正文是您的 POST 所必需的)
所以正常的 NTLM 协商应该是这样的
- 来自客户端的请求(带有 POST 正文)-> 服务器 returns 401 带有
www-authenticate:negotiate, www-authenticate:NTLM
- 客户端发送
使用 NTLM 令牌的附加请求(w/o post 正文)-> 服务器 returns 401 与服务器
NTLM 令牌
- 客户端发送第 3 个带有会话(带有 POST 正文)和服务器 returns 的请求
200
有了这个优化
- 客户端期望 401 并发送 NTLM 令牌(w/o 正文)-> 服务器 returns 401 带有服务器令牌
- 客户端通过会话发送第二个请求(带正文)-> 服务器 returns 200
出现问题
- 客户端期望 401 并发送 NTLM 令牌(w/o 正文)-> 服务器处理请求并收到内部错误(可能是 500)
要添加的注册表项可防止此 "optimization"
HKEY_CURRENT_USER/Software/Microsoft/Windows/CurrentVersion/Internet Settings/
Add the following registry value:
Value Name: DisableNTLMPreAuth
Data Type: REG_DWORD
Value: 1
或者将这些条目保存到您机器上的 .reg 文件中,然后双击它
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\]
"DisableNTLMPreauth"=dword:00000001
我不确定为什么 IE 认为它必须重新授权,但从广泛的测试来看,一旦 IE 出现会话超时或会话无效并且它从服务器返回 401,它就想之后每 1-2 分钟重新授权一次。这可能与您在身份验证时 URL 的状态有关,因为您可能不在根目录下。我提供的第一个 link 谈了很多关于 IE 对 URLs 的挑剔,以及它如何根据路径猜测什么应该被授权,什么不应该被授权。
相关文章:
我们发现在 IE 11 中缺少 POST 的看似随机的调用存在问题。经过进一步检查,来自浏览器的请求包含 NTLM 协商令牌。我们也时不时在 GET 上看到这个 token,但它们不受 body 问题的影响,因为它们没有。 Chrome和FF没有这个问题
进一步调查表明,如果我们的会话超时并且浏览器被重新授权,那么之后我们将以大约 1 - 2 分钟的间隔持续看到此问题。据我了解,如果 IE "expects" 是 401,那么它不会包含 POST 正文,因为它希望 NTLM 握手再次发生。
我们有一个 angular 应用程序(目前混合 Angular 1 和 2,但发生在 1 之前)。 angular 应用程序对我们的 Spring 启动应用程序进行 restful 网络服务调用。我们正在使用 waffle 进行 NTLM 验证
// Shiro-Waffle / Security
'com.github.dblock.waffle:waffle-parent:1.7.3',
'com.github.dblock.waffle:waffle-shiro:1.7.3',
'org.apache.shiro:shiro-core:1.2.3',
'org.apache.shiro:shiro-web:1.2.3',
我有一个糟糕的解决方法,我将在下面 post 作为答案,但我一点也不喜欢它。
我创建了一个 shiro 过滤器,我将其作为链中的最后一个过滤器 (Groovy)
package com.security.shiro
import com.security.AuthenticateTrait
import com.ws.RemoteUserAccessor
import com.ws.RequestAccessor
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse
/**
* Created by esarrazin on 11/30/2016.
*/
class IEBugFilter3 extends NegotiateAuthenticationFilter implements RemoteUserAccessor, AuthenticateTrait, RequestAccessor {
/**
* Returns <code>false</code> If the giant piece of sh1t IE chooses to think that it will need to re-authenticate
* and not send the body to us. Here we will deny access (as IE expects - big turd) and have waffle re-negotiate
* this useless sh1t-bag. Then and only then will IE choose to give us the body of the POST. This problem is intermittent
* and there is no telling when IE will use its infinite wisdom to do this, so please don't think if you comment this
* out it is still working without.
*
* Not a silver bullet, but here is a link to some reading about this:
* https://blogs.msdn.microsoft.com/ieinternals/2010/11/21/challenge-response-authentication-and-zero-length-posts/
*
* @return Returns true if isAccessAllowed
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
!isRequestExpectingToReAuth(request, response)
}
private boolean isRequestExpectingToReAuth(ServletRequest request, ServletResponse response) {
if (request?.request?.request?.coyoteRequest?.methodMB == 'POST' && toHttp(request).getHeader('content-length') == '0') {
System.out.println("Found the dreaded zero length body and attempting to recover from it. $request.remoteHost")
return true
}
return false
}
}
我们的 NegotiateAuthenticationFilter 扩展了 waffle.shiro.negotiate.NegotiateAuthenticationFilter 并且非常简单。
这会看到空主体 POST 请求(请原谅请求?.请求垃圾 - 未准备好产品)并拒绝访问,从而为 IE 提供它所期望的。由于我们拒绝访问,401 通过并开始握手。然后当 200 出现时,IE 释放正文,我们就可以了。但这是废话-尽管是一个解决方案。
好的,我找到了一个注册表 hack。基本上,IE 会主动从服务器发送 NTLM 凭据,而不会出现 401。发送 "pre auth" 时,IE 将省略 POST 正文(如果请求是 POST。)如果会话已经有效并且服务器已经获得您的授权,它会允许请求通过而不正文并会导致服务器出错(假设正文是您的 POST 所必需的)
所以正常的 NTLM 协商应该是这样的
- 来自客户端的请求(带有 POST 正文)-> 服务器 returns 401 带有 www-authenticate:negotiate, www-authenticate:NTLM
- 客户端发送 使用 NTLM 令牌的附加请求(w/o post 正文)-> 服务器 returns 401 与服务器 NTLM 令牌
- 客户端发送第 3 个带有会话(带有 POST 正文)和服务器 returns 的请求 200
有了这个优化
- 客户端期望 401 并发送 NTLM 令牌(w/o 正文)-> 服务器 returns 401 带有服务器令牌
- 客户端通过会话发送第二个请求(带正文)-> 服务器 returns 200
出现问题
- 客户端期望 401 并发送 NTLM 令牌(w/o 正文)-> 服务器处理请求并收到内部错误(可能是 500)
要添加的注册表项可防止此 "optimization"
HKEY_CURRENT_USER/Software/Microsoft/Windows/CurrentVersion/Internet Settings/
Add the following registry value:
Value Name: DisableNTLMPreAuth
Data Type: REG_DWORD
Value: 1
或者将这些条目保存到您机器上的 .reg 文件中,然后双击它
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\]
"DisableNTLMPreauth"=dword:00000001
我不确定为什么 IE 认为它必须重新授权,但从广泛的测试来看,一旦 IE 出现会话超时或会话无效并且它从服务器返回 401,它就想之后每 1-2 分钟重新授权一次。这可能与您在身份验证时 URL 的状态有关,因为您可能不在根目录下。我提供的第一个 link 谈了很多关于 IE 对 URLs 的挑剔,以及它如何根据路径猜测什么应该被授权,什么不应该被授权。
相关文章: