I made a JSR-356 @ServerEndpoint
in which I want to limit alive connections from single IP address, to prevent simple DDOS attacks.
Note that I\'m searc
the socket object is hidden in WsSession, so you can use reflection to got the ip address. the execution time of this method is about 1ms. this solution is not prefect but useful.
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;
}
and the pom config is
<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>
If you are using Tyrus which is JSR-356 compliant, then you can get the IP address from the Session instance, but this is a non-standard method.
See here.
According to Tomcat developer @mark-thomas client IP is not exposed via JSR-356 thus it is impossible to implement such a function with pure JSR-356 API-s.
You have to use a rather ugly hack to work around the limitation of the standard.
What needs to be done boils down to:
There are at least two hacky options to achieve that.
ServletRequestListener
request.getSession()
on incoming request to ensure it has a session and store client IP as a session attribute.ServerEndpointConfig.Configurator
that lifts client IP from HandshakeRequest#getHttpSession
and attaches it to EndpointConfig
as a user property using the modifyHandshake
method.EndpointConfig
user properties, store it in map or whatever and trigger cleanup logic if the number of sessions per IP exceeds a threshold.You can also use a @WebFilter
instead of ServletRequestListener
Note that this option can have a high resource consumption unless your application already uses sessions e.g. for authentication purposes.
/mychat
ServletRequest#getRequestDispatcher
to forward the request to /mychat/TOKEN
@ServerEndpoint("/mychat/{token}")
@PathParam
and decrypt to get client IP. Store it in map or whatever and trigger cleanup logic if the number of sessions per IP exceeds a threshold.For ease of installation you may wish to generate encryption keys on application startup.
Please note that you need to encrypt the IP even if you are doing an internal dispatch that is not visible to the client. There is nothing that would stop an attacker from connecting to /mychat/2.3.4.5
directly thus spoofing the client IP if it's not encrypted.
See also: