starlette icon indicating copy to clipboard operation
starlette copied to clipboard

Host Header Attack: `url_for()` should not trust unvalidated Host header

Open Kludex opened this issue 1 year ago • 4 comments

Discussed in https://github.com/encode/starlette/discussions/1854

Originally posted by lieryan September 9, 2022 This is somewhat related to ticket https://github.com/encode/starlette/issues/843.

Currently, url_for() can be used to generate an absolute URL for a route and it uses Host header to do so. There is currently no validation in the value of the host header, which can be abused for some quite funky stuffs.

This behavior is a security issue because it opens up the application to Host Header Attack.

You can fix this issue in your application by adding a TrustedHostMiddleware and setting allowed_hosts=["yourdomain.com"], but I think most people aren't going to know that they must use TrustedHostMiddleware if they want to use url_for() securely.

To reproduce:

from starlette.applications import Starlette
from starlette.responses import RedirectResponse, JSONResponse
from starlette.routing import Route


async def main(request):
    return JSONResponse({

        "url_for": request.url_for("main"),
        "url_path_for": app.url_path_for("main"),
    })


async def redirect(request):
    return RedirectResponse(request.url_for("main"))


routes = [
    Route("/", endpoint=main),
    Route("/redirect", endpoint=redirect),
]

app = Starlette(routes=routes)


if __name__ == '__main__':
    import uvicorn
    uvicorn.run(app)

And on the shell do (note the funky looking Host header):

$ curl -L -v -H 'Host: www.google.com/search' localhost:8000/redirect
< HTTP/1.1 307 Temporary Redirect
< date: Fri, 09 Sep 2022 13:51:10 GMT
< server: uvicorn
< content-length: 0
< location: http://www.google.com/search/


$ $ curl -L -H 'Host: www.google.com/search' localhost:8000/ | jq
{
  "url_for": "http://www.google.com/search/",
  "url_path_for": "/"
}

[!IMPORTANT]

  • We're using Polar.sh so you can upvote and help fund this issue.
  • We receive the funding once the issue is completed & confirmed by you.
  • Thank you in advance for helping prioritize & fund our backlog.
Fund with Polar

Kludex avatar Sep 10 '22 06:09 Kludex

Here's a so far unanswered discussion topic that points another issue coming (I think) from the same problem: taking directly raw headers to generate url/redirect etc, without any chance to put some validation logic in-between: https://github.com/encode/starlette/discussions/1835

wrzasa avatar Sep 12 '22 09:09 wrzasa

thus TrustedHostMiddleware should be required for each application to disable Host Header Attack.

csrgxtu avatar Sep 16 '22 08:09 csrgxtu

What was the idea behind making a full URL instead of only path part?

alex-oleshkevich avatar Oct 27 '22 07:10 alex-oleshkevich

@alex-oleshkevich sometimes you're rendering absolute url so it can be inserted into Emails/SMS.

For API, rendering absolute URL can make it easier for clients to cache the result, either in full or just parts of the response, without requiring them to cache the base context URL or trying to resolve the relative URLs.

This might not be that big of a problem if you only have one API domain, but clients that calls cross domain requests to many different APIs, and where the API often cross references other APIs as well, taking care of which relative URL belongs to which base URL is an extra unnecessary complexity.

lieryan avatar Oct 27 '22 09:10 lieryan