[英]JSR-356 WebSockets with Tomcat - How to limit connections within single IP address?
我制作了一個 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
監聽傳入的 HTTP 請求request.getSession()
以確保它具有會話並將客戶端 IP 存儲為會話屬性。ServerEndpointConfig.Configurator
,它從HandshakeRequest#getHttpSession
中提取客戶端 IP,並使用modifyHandshake
方法將其作為用戶屬性附加到EndpointConfig
。EndpointConfig
用戶屬性中獲取客戶端 IP,將其存儲在地圖或其他任何內容中,如果每個 IP 的會話數超過閾值,則觸發清理邏輯。 您還可以使用@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();
如果使用:實現 'io.quarkus:quarkus-websockets'
@OnOpen public void onOpen(final Session session, final @PathParam("userId") String userId) { UndertowSession us = (UndertowSession) session; System.out.println("Remote Address: " + us.getChannel().remoteAddress()); SESSIONS.put(userId, session); log.info("User " + userId + " joined"); }
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.