fix: validate Host header in graph_server request handlers - #17932
fix: validate Host header in graph_server request handlers#17932Samin061 wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces host header validation to ensure that the graph server only accepts requests targeting the loopback interface, mitigating potential DNS-rebinding attacks. This is implemented via a new _host_is_loopback helper method and applied to both GET and POST requests, with corresponding unit tests added. Feedback suggests wrapping the urllib.parse.urlsplit call in a try...except ValueError block to gracefully handle malformed Host headers and prevent unhandled exceptions.
| hostname = urllib.parse.urlsplit(f"//{host}").hostname | ||
| return hostname in _LOOPBACK_HOSTS |
There was a problem hiding this comment.
Using urllib.parse.urlsplit on an arbitrary Host header can raise a ValueError (for example, if the Host header contains an IPv6 address without square brackets like ::1, or contains other invalid characters/control characters). To prevent unhandled exceptions and potential denial of service, wrap the parsing in a try...except ValueError block and return False as a graceful fallback.
| hostname = urllib.parse.urlsplit(f"//{host}").hostname | |
| return hostname in _LOOPBACK_HOSTS | |
| try: | |
| hostname = urllib.parse.urlsplit(f"//{host}").hostname | |
| except ValueError: | |
| return False | |
| return hostname in _LOOPBACK_HOSTS |
References
- Do not replace historical graceful fallback behaviors (such as returning False) with exceptions to maintain backwards compatibility.
|
Hi @Samin061, Thanks for opening a PR! We'll need to add this to each new test |
|
I'm going to switch this to draft but please mark it ready for review once tests are green |
GraphServerHandler.do_GET/do_POST served any request that reached the loopback port, so a page in the notebook user's browser can reach the server through DNS rebinding and drive its /post_query handler, which routes into convert_graph_params and builds a BigQuery client with the user's credentials to call list_rows against a caller-supplied api endpoint and table. The widget is legitimately cross-origin because the notebook page and this server run on different ports, so Origin cannot gate the request, but real widget traffic always carries a loopback Host. The handlers now check the Host header and refuse anything that is not localhost, 127.0.0.1, or ::1.