Repository navigation
Reject foreign host names on the loopback server - #332
Merged
JuergenFleiss merged 5 commits intoNov 9, 2026
Merged
Conversation
A website whose domain resolves to 127.0.0.1 (DNS rebinding) could load and drive aTrain while it listened on loopback. When aTrain binds to 127.0.0.1 or localhost, as the native window does by default, the server now answers only to those two host names. socket.io then only accepts pages from the server's own origin. --allow-any-host turns both checks off for a reverse proxy on the same machine. Servers bound to other addresses are unchanged. Refs aTrainTranscription#300
Collaborator
|
Thanks @BW-Projects for taking this on. I looked it through, and this generally looks good to me. One point: testing confirmed that |
JuergenFleiss
requested changes
Nov 5, 2026
JuergenFleiss
left a comment
Collaborator
There was a problem hiding this comment.
see my previous comment
aTrain start --no-native without --host bound to all interfaces, where the host and origin check did not apply. It now binds to 127.0.0.1 like the native window, so the check applies there by default. Serving on the network takes an explicit --host such as 0.0.0.0, which keeps the check off as before. Refs aTrainTranscription#300
Contributor
Author
|
@JuergenFleiss Thanks, I went with your preferred way:
Tested on Linux. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Once a website's domain resolves to 127.0.0.1 (DNS rebinding, #300), a page from that site could load and drive aTrain while it listens on loopback. The server did not check the
Hostheader and NiceGUI accepts socket.io connections from every origin.aTrain start --no-nativeused to bind to all interfaces when--hostwas not given. It now binds to127.0.0.1like the native window. When aTrain binds to127.0.0.1orlocalhost, the server now:TrustedHostMiddleware, which also covers the socket.io mount*)Serving on the network takes an explicit
--host, for example0.0.0.0. With any address other than127.0.0.1orlocalhost, aTrain behaves as before and checks neither host names nor origins. A reverse proxy on the same machine in front of a loopback-bound aTrain sends its public host name or origin, so such setups now need the new--allow-any-host.Existing
--no-nativesetups that serve on the network need--host 0.0.0.0after the update. The Linux service guide now sets it. The README and the service guide warn that this mode has no check. The CHANGELOG lists the check,--allow-any-hostand the new default.The browser e2e tests start the app with plain
--no-native, so they run through the check with the default address.References
Refs #300. The issue's docs step follows once #296 is merged. The #296 page still lists
--no-nativewithout--hostas network mode, which this PR changes.Screenshots / Examples
Tested on Linux in Chromium and Firefox, on Windows 11 in Edge and a WebView2 window like the native app's. "Foreign host" is a test domain the browser resolves to 127.0.0.1, the state after a successful rebinding. The rebinding itself and browser protections against it were not tested, nor were the frozen Windows build and macOS. The new default address was checked on Linux with curl.
--no-nativewithout--host: address, foreign host127.0.0.1, 400--no-native --host 127.0.0.1: list, download, delete a transcript/archive127.0.0.1,localhost, the native window and WebView2--allow-any-host--allow-any-host--host 127.0.0.2): foreign host and originVerification Steps
ATRAIN_USER_DIR=$(mktemp -d) uv run aTrain start --no-native --port 8090 --no-show, thenss -ltnshows it only on127.0.0.1:8090.curl -s -o /dev/null -w "%{http_code}\n" -H "Host: rebound.example:8090" http://127.0.0.1:8090/prints 400, without the header 200.curl -s -o /dev/null -w "%{http_code}\n" -H "Origin: https://example.com" "http://127.0.0.1:8090/_nicegui_ws/socket.io/?EIO=4&transport=polling"prints 400.--allow-any-hostor with--host 127.0.0.2: step 2 prints 200.uv run aTrain start(native window) work as before.uv run --no-sync pytest tests/unit/test_hosts.py tests/e2e_browser/test_host_check.pycc @gerardo-navarro