我制作了一个 JSR-356 @ServerEndpoint
,我想在其中限制来自单个 IP 地址的活动连接,以防止简单的 DDOS 攻击。
请注意,我正在搜索 Java 解决方案(JSR-356、Tomcat 或 Servlet 3.0 规范)。
HandshakeRequest
我已经尝试过自定义端点配置器,但即使在对象中我也无法访问 IP 地址。
如何在没有外部软件(如 iptables)的情况下限制来自单个 IP 地址的 JSR-356 连接数?
根据 Tomcat 开发人员@mark-thomas 的说法,客户端 IP不会通过 JSR-356 公开,因此不可能使用纯 JSR-356 API-s 实现这样的功能。
您必须使用相当丑陋的 hack 来解决标准的限制。
需要做的事情归结为:
至少有两个 hacky 选项可以实现这一目标。
ServletRequestListener
request.getSession()
传入请求以确保它具有会话并将客户端 IP 存储为会话属性。ServerEndpointConfig.Configurator
从客户端 IP 提取并将其作为用户属性HandshakeRequest#getHttpSession
附加到的客户端。EndpointConfig
modifyHandshake
EndpointConfig
,将其存储在地图或其他任何内容中,如果每个 IP 的会话数超过阈值,则触发清理逻辑。您也可以使用 a@WebFilter
而不是ServletRequestListener
请注意,此选项可能会消耗大量资源,除非您的应用程序已经使用会话(例如用于身份验证)。
/mychat
ServletRequest#getRequestDispatcher
将请求转发到/mychat/TOKEN
@ServerEndpoint("/mychat/{token}")
@PathParam
并解密以获取客户端 IP。如果每个 IP 的会话数超过阈值,则将其存储在地图或其他任何内容中并触发清理逻辑。为了便于安装,您可能希望在应用程序启动时生成加密密钥。
请注意,即使您正在执行对客户端不可见的内部调度,您也需要对 IP 进行加密。/mychat/2.3.4.5
如果没有加密,没有什么可以阻止攻击者直接连接,从而欺骗客户端 IP。
也可以看看:
套接字对象隐藏在 WsSession 中,因此您可以使用反射来获取 IP 地址。该方法的执行时间约为 1ms。这个解决方案不是完美的,但很有用。
public static InetSocketAddress getRemoteAddress(WsSession session) {
if(session == null){
return null;
}
Async async = session.getAsyncRemote();
InetSocketAddress addr = (InetSocketAddress) getFieldInstance(async,
"base#sos#socketWrapper#socket#sc#remoteAddress");
return addr;
}
private static Object getFieldInstance(Object obj, String fieldPath) {
String fields[] = fieldPath.split("#");
for(String field : fields) {
obj = getField(obj, obj.getClass(), field);
if(obj == null) {
return null;
}
}
return obj;
}
private static Object getField(Object obj, Class<?> clazz, String fieldName) {
for(;clazz != Object.class; clazz = clazz.getSuperclass()) {
try {
Field field;
field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(obj);
} catch (Exception e) {
}
}
return null;
}
pom配置是
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-all</artifactId>
<version>1.1</version>
<type>pom</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-websocket</artifactId>
<version>8.0.26</version>
<scope>provided</scope>
</dependency>
如果您使用的是符合 JSR-356 的 Tyrus,那么您可以从 Session 实例中获取 IP 地址,但这是一种非标准方法。
如果使用带有 Undertow Websocket 引擎的 Springboot,请尝试以下方式获取 IP。
@OnOpen
public void onOpen(Session session) {
UndertowSession us = (UndertowSession) session;
String ip = us.getWebSocketChannel().getSourceAddress().getHostString();