java - crear - JSR-356 WebSockets con Tomcat: cómo limitar las conexiones dentro de una única dirección IP?
websocket java netbeans (3)
el objeto socket está oculto en WsSession, por lo que puede usar reflection para obtener la dirección IP. el tiempo de ejecución de este método es de aproximadamente 1 ms. esta solución no es perfecta pero útil.
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;
}
y la configuración pom es
<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>
Hice un JSR-356 @ServerEndpoint
en el que quiero limitar conexiones vivas desde una sola dirección IP, para evitar ataques DDOS simples.
Tenga en cuenta que estoy buscando una solución Java (especificaciones JSR-356, Tomcat o Servlet 3.0).
He probado el configurador de punto final personalizado, pero no tengo acceso a la dirección IP incluso en el objeto HandshakeRequest
.
¿Cómo limitar el conteo de conexiones JSR-356 desde una sola dirección IP sin software externo como iptables?
De acuerdo con el cliente de Tomcat developer @ mark-thomas, la IP no está expuesta a través de JSR-356 por lo que es imposible implementar dicha función con las API JSR-356 puras.
Tienes que usar un hack bastante feo para evitar la limitación del estándar.
Lo que debe hacerse se reduce a:
- Genere a cada usuario un token que contenga su IP en la solicitud inicial (antes de la toma de contacto de websocket)
- Pase la ficha por la cadena hasta que llegue a la implementación del punto final
Hay al menos dos opciones de hacky para lograr eso.
Usar HttpSession
- Escucha las solicitudes HTTP entrantes con
ServletRequestListener
- Llame a
request.getSession()
en la solicitud entrante para asegurarse de que tenga una sesión y almacene la IP del cliente como un atributo de sesión. - Cree un
ServerEndpointConfig.Configurator
que levante la IP del cliente deHandshakeRequest#getHttpSession
y laHandshakeRequest#getHttpSession
aEndpointConfig
como una propiedad del usuario utilizando el métodomodifyHandshake
. - Obtenga la IP del cliente de las propiedades de usuario de
EndpointConfig
, guárdela en el mapa o lo que sea y active la lógica de limpieza si el número de sesiones por IP excede un umbral.
También puede usar un @WebFilter
lugar de ServletRequestListener
Tenga en cuenta que esta opción puede tener un alto consumo de recursos a menos que su aplicación ya use sesiones, por ejemplo, con fines de autenticación.
Pase IP como un token cifrado en la URL
- Cree un servlet o un filtro que se una a un punto de entrada que no sea de websocket. por ejemplo
/mychat
- Obtenga IP del cliente, encripte con una sal al azar y una clave secreta para generar un token.
- Use
ServletRequest#getRequestDispatcher
para reenviar la solicitud a/mychat/TOKEN
- Configure su punto final para usar parámetros de ruta, por ejemplo
@ServerEndpoint("/mychat/{token}")
- Levante la ficha de
@PathParam
y descifre para obtener IP del cliente. Guárdelo en el mapa o lo que sea y active la lógica de limpieza si el número de sesiones por IP excede un umbral.
Para facilitar la instalación, es posible que desee generar claves de cifrado en el inicio de la aplicación.
Tenga en cuenta que debe encriptar la IP incluso si está realizando un despacho interno que no es visible para el cliente. No hay nada que impida que un atacante se conecte a /mychat/2.3.4.5
directamente, falsificando así la IP del cliente si no está encriptada.
Ver también:
- apache tomcat 8 origen del websocket y dirección del cliente
- Encontrar la cantidad de sesiones activas creadas desde una IP de cliente determinada
- Acceder a HttpSession desde HttpServletRequest en un socket Web @ ServerEndpoint
- https://tyrus.java.net/documentation/1.4/index/websocket-api.html
- http://docs.oracle.com/javaee/7/tutorial/doc/websocket010.htm#BABJAIGH
Si está utilizando Tyrus, que es compatible con JSR-356, puede obtener la dirección IP de la instancia de sesión, pero este es un método no estándar.