Skip to content
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

Should we have a way to block open_tcp_stream from connecting to certain hosts? #479

Open
njsmith opened this issue Mar 22, 2018 · 1 comment

Comments

@njsmith
Copy link
Member

njsmith commented Mar 22, 2018

I was reading https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf , and there are a few examples that follow this pattern:

  • you have some program running behind your firewall (e.g., the code running Github, or Wordpress)
  • it wants to make an outgoing HTTP request, but the destination URL is at least somewhat under the control of an untrusted user (e.g., Github webhooks)
  • you want to make sure that user doesn't direct the request at some sensitive behind-the-firewall service and pwn you
  • so you have some check, like you forbid any URL that says localhost in it
  • but doing this correctly is extremely tricky, so everyone screws it up, and they get pwned.

The only correct place to do this check is after you've done the final conversion to a (normalized) IP address, and even there it has subtleties (e.g. sock.connect(0.0.0.0, port) seems to connect to localhost for me, and ipv4-mapped addresses are a thing that someone could potentially put in an AAAA record if they wanted to be perverse). If you're using open_tcp_stream – and we'd hope that HTTP clients on top of trio will use open_tcp_stream! – then that means that the checking has to happen inside open_tcp_stream.

One option would be to provide a forbidden_targets=... argument, that gives a list of ipaddress.IPv{4,6}Network objects, and for each resolved address checks if they fall into any of the given networks, and raises an error if so. The ipaddress module also provides mechanisms to check if an address is private, reserved, loopback, etc., which might be useful here.

Or I guess we could be even more minimal, and let the user pass in a validate_target=... function, that gets called on all the targets, and can raise an error or return False or whatever to indicate a bad target.

We should check with urllib3/requests maintainers whether this is something they would actually use.

@njsmith
Copy link
Member Author

njsmith commented Mar 22, 2018

Prior art:

(I'm surprised/confused that I can't find any equivalents in Python.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant