Citrix 已确定我们遇到的 TCP 通信问题,原因是客户端和服务器之间的 NetScaler 负载平衡器上的活动TCP 小窗口攻击保护 (TCP-SWAP) 。负载均衡器将间歇性地断开 TCP 连接,Citrix/文章的建议是禁用 TCP-SWAP。由于 NetScaler 用于除我之外的其他系统流量,禁用此设置可能会导致全局事件并使 NetScaler 暴露于潜在的小窗口攻击。
禁用 TCP-SWAP 的替代方法是确保客户端请求不被归类为小窗口攻击。
特定受影响的客户端通过 NetScaler 向服务器发送多部分请求。请求是标头、XML 部分和文件部分,由标准生成的边界分隔。NetScaler 间歇性地将附件大小为 62-66kb 的某些请求标记为小窗口攻击,并阻止服务器接收请求的最后部分。后续相同的请求(包括使用相同的边界)成功;该场景无法按需复制,但可以批量复制。
在请求被视为小窗口攻击的合格场景中,整个客户端请求被传递到负载均衡器,然后客户端等待服务器响应。服务器接收标头、XML 部分和大约 50% 的文件部分,然后等待请求文件部分的其余部分。负载平衡 NetScaler 从不传输文件部分的其余部分。客户端和服务器最终超时等待。
客户端和服务器端的代码审查表明代码没有问题。从通信路径中移除 NetScaler 解决了问题;当不再在客户端和服务器之间时,延长时间跨度没有问题。不幸的是,负载均衡器是必需的。
问题是由于 TCP-SWAP 导致客户端请求被 NetScaler 错误分类为小窗口攻击,这会阻止服务器接收请求的最后部分。除了在 NetScaler 上禁用 TCP-SWAP 之外,客户端可以进行哪些更改以防止请求被归类为小窗口攻击?
客户端是在 Linux (Oracle Linux Server 7.8) 上的 IBM Liberty (WAS Liberty 20.0.0.7) 下运行的 Java 8 (IBM SDK 8.0-6.11-linux-x86_64) 应用程序,它使用正常超时设置进行org.apache.httpcomponents.httpclient
通信,PoolingHttpClientConnectionManager
安全和连接管理。 是否有适用于 Liberty 或 HttpComponents 的配置设置以防止请求被识别为Small Window Attacks
?
邮政编码如下;这是非典型的:
public HttpResponse post(URL url, Map<String, Object> parameterMap) throws Exception {
HttpPost httpPost = new HttpPost(url.toString());
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
for (Map.Entry<String, Object> entry : parameterMap.entrySet()) {
if (entry.getValue() instanceof String) {
builder.addTextBody(entry.getKey(), (String)entry.getValue());
} else {
String partName = getNextPartName();
if (entry.getValue() instanceof File) {
builder.addBinaryBody(partName, (File)entry.getValue(), ContentType.APPLICATION_OCTET_STREAM, entry.getKey());
} else
if (entry.getValue() instanceof InputStream) {
builder.addBinaryBody(partName, (InputStream)entry.getValue(), ContentType.APPLICATION_OCTET_STREAM, entry.getKey());
} else
if (entry.getValue() instanceof byte[]) {
builder.addBinaryBody(partName, (byte[])entry.getValue(), ContentType.APPLICATION_OCTET_STREAM, entry.getKey());
} else {
throw new IllegalArgumentException("Cannot attach entry " + entry.getKey() + " with Object of class " + entry.getValue().getClass().getName());
}
}
}
httpPost.setEntity(builder.build());
}
return new HttpResponseImpl(getClient().execute(httpPost), httpPost);
}