-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Securing websockets #1802
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Securing websockets #1802
Conversation
Reviewer's GuideThis PR secures the WebSocket handshake by extracting the client’s remote address from the socket, evaluating whether it corresponds to localhost (IPv4, IPv6, or IPv4‐mapped), and conditioning the Engine.IO handshake acceptance on both the presence of the EIO parameter and the isLocalhost check. Sequence diagram for secured WebSocket handshakesequenceDiagram
participant Client
participant WebsocketController
participant Socket
Client->>WebsocketController: Initiate WebSocket handshake (with EIO param)
WebsocketController->>Socket: Extract remoteAddress
WebsocketController->>WebsocketController: Check if remoteAddress is localhost
alt EIO param present AND isLocalhost
WebsocketController-->>Client: Accept handshake
else
WebsocketController-->>Client: Reject handshake
end
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @frieck - I've reviewed your changes and they look great!
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location> `src/api/integrations/event/websocket/websocket.controller.ts:31` </location>
<code_context>
allowRequest: async (req, callback) => {
try {
const url = new URL(req.url || '', 'http://localhost');
+ const isInternalConnection = req.socket.remoteAddress === '127.0.0.1' || req.socket.remoteAddress === '::1';
const params = new URLSearchParams(url.search);
</code_context>
<issue_to_address>
Consider handling IPv6-mapped IPv4 addresses for localhost.
Also include '::ffff:127.0.0.1' in the internal connection check to cover IPv6-mapped IPv4 localhost addresses.
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
const isInternalConnection = req.socket.remoteAddress === '127.0.0.1' || req.socket.remoteAddress === '::1';
=======
const isInternalConnection =
req.socket.remoteAddress === '127.0.0.1' ||
req.socket.remoteAddress === '::1' ||
req.socket.remoteAddress === '::ffff:127.0.0.1';
>>>>>>> REPLACE
</suggested_fix>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| allowRequest: async (req, callback) => { | ||
| try { | ||
| const url = new URL(req.url || '', 'http://localhost'); | ||
| const isInternalConnection = req.socket.remoteAddress === '127.0.0.1' || req.socket.remoteAddress === '::1'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Consider handling IPv6-mapped IPv4 addresses for localhost.
Also include '::ffff:127.0.0.1' in the internal connection check to cover IPv6-mapped IPv4 localhost addresses.
| const isInternalConnection = req.socket.remoteAddress === '127.0.0.1' || req.socket.remoteAddress === '::1'; | |
| const isInternalConnection = | |
| req.socket.remoteAddress === '127.0.0.1' || | |
| req.socket.remoteAddress === '::1' || | |
| req.socket.remoteAddress === '::ffff:127.0.0.1'; |
|
@sourcery-ai review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| const url = new URL(req.url || '', 'http://localhost'); | ||
| const params = new URLSearchParams(url.search); | ||
|
|
||
| const remoteAddress = req.socket.remoteAddress; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): Prefer object destructuring when accessing and using properties. (use-object-destructuring)
| const remoteAddress = req.socket.remoteAddress; | |
| const {remoteAddress} = req.socket; |
Explanation
Object destructuring can often remove an unnecessary temporary reference, as well as making your code more succinct.From the Airbnb Javascript Style Guide
|
@sourcery-ai review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @frieck - I've reviewed your changes - here's some feedback:
- Consider using a CIDR-based or net.isLoopback utility instead of hardcoding specific loopback address strings to cover all IPv4/IPv6 variants.
- If the app sits behind a reverse proxy, req.socket.remoteAddress might not reflect the original client; ensure you handle X-Forwarded-For headers or configure trusted proxies.
- You could extract the localhost check into a reusable helper to keep the controller code concise and improve testability.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consider using a CIDR-based or net.isLoopback utility instead of hardcoding specific loopback address strings to cover all IPv4/IPv6 variants.
- If the app sits behind a reverse proxy, req.socket.remoteAddress might not reflect the original client; ensure you handle X-Forwarded-For headers or configure trusted proxies.
- You could extract the localhost check into a reusable helper to keep the controller code concise and improve testability.
## Individual Comments
### Comment 1
<location> `src/api/integrations/event/websocket/websocket.controller.ts:34` </location>
<code_context>
const params = new URLSearchParams(url.search);
+ const { remoteAddress } = req.socket;
+ const isLocalhost =
+ remoteAddress === '127.0.0.1' || remoteAddress === '::1' || remoteAddress === '::ffff:127.0.0.1';
+
// Permite conexões internas do Socket.IO (EIO=4 é o Engine.IO v4)
</code_context>
<issue_to_address>
Consider IPv6-mapped IPv4 and proxy scenarios for localhost detection.
This check may not handle all localhost scenarios, such as alternative IPv6-mapped formats or proxy headers like X-Forwarded-For. Consider updating the logic or documenting these limitations if relevant to your deployment.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| const isLocalhost = | ||
| remoteAddress === '127.0.0.1' || remoteAddress === '::1' || remoteAddress === '::ffff:127.0.0.1'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Consider IPv6-mapped IPv4 and proxy scenarios for localhost detection.
This check may not handle all localhost scenarios, such as alternative IPv6-mapped formats or proxy headers like X-Forwarded-For. Consider updating the logic or documenting these limitations if relevant to your deployment.
Fixing publicly exposed WebSocket endpoints.
Summary by Sourcery
Restrict WebSocket Engine.IO handshake to only allow connections from localhost
Bug Fixes:
Enhancements: