Hacker News new | ask | show | jobs
by arkadiyt 2008 days ago
These different representations also lead to frequent server side request forgery (SSRF) bypasses - someone might be blocking local IPv4 but you can still access their AWS metadata endpoint at ::ffff:169.254.169.254, etc.

For anyone using Ruby, I'm the author of a gem [1] that comprehensively protects against SSRF bugs. For anyone using Golang I recommend this [2] blog post.

[1]: https://github.com/arkadiyt/ssrf_filter

[2]: https://www.agwa.name/blog/post/preventing_server_side_reque...

5 comments

For golang I wrote this:

https://github.com/skx/remotehttp

I've found, and reported, a whole bunch of services which take user-supplied URLs and don't filter out access to localhost:8080/server-status, and similar local resources.

A common route to attacking these is to access the AWS metadata URL endpoint. Something at least the Google cloud prevents, by forcing the use of the `Metadata-Flavor: Google` header.

> ::ffff:169:254:169:254

Just to note, this should be ::ffff:169.254.169.254

I wonder how many of these bugs are the result of people thinking "Well I've read the spec but most of it is 'cursed' so I'll just implement this subset which fits my idea of 'acceptable'".
It’s more like “I firewalled everything using iptables, job done” but, there are no firewall rules in ip6tables. IPv6, what’s that?

(My solution at home is to blanket block IPv6 entirely)

Unfortunately the blacklisting approach that works on IPv4 is completely broken for IPv6 since you can't really know where your own services are. I still did not find a good generic way to protect IPv6 and ended up just disallowing it so far everywhere.
IPv6 has internal ranges defined just like IPv4 does - anything in an internal range should be blocked, and anything in an external range is safe to pass through.
Since there is no NAT in an IPv6 deployment unsafe services you typically want to prevent access to look like non internal ranges. Whereas in your normal IPv4 deployment you might have your protected service on 192.168.4.111 with IPv6 it will just share the same prefix (potentially) as your host.
NAT was meant to work around IP exhaustion issues, not act as a security layer. By accident, it often happens to provide some additional security. By keep in mind those "internal" IP addresses may be routable in some cases, due to either accidental or deliberate mis-configuration.

IPv6, with end-to-end connectivity, is how the Internet is supposed to work. It's how it did work in the early 90's, even with IPv4.

If you want to secure your servers, use a firewall. Maybe it's a host-based firewall.

What are the practical reasons we should rearchitect our systems to remove NAT?

(I know the weaknesses of NAT but the cat’s out of the bag at this point...the question isn’t really “why should we use NAT”, it’s “why should we go through the pain of breaking it”.)

IPv6 does nothing to break NAT, you could deploy the exact same kind of NAT and it would have the exact same behaviour, if you really want to make your router use a bunch more memory/CPU and make it a pain for users to do anything that needs a direct connection. But you gain nothing from doing that.
It's nice that this is how the internet is "supposed to work". In practice not having a NAT makes "automatic" internal protection of web services hard to impossible.

> If you want to secure your servers, use a firewall. Maybe it's a host-based firewall.

Firewalls do not solve this problem because a you do want service to service communication. What you do not want is code that crawls to user supplied URLs to access your internal services. Do you need application level protections. With IPv6 you're basically forced to declare your CIDR explicitly whereas with IPv4 you could easily achieve a secure by default system.

In these situations, store your IPv6 prefixes in a config. This doesn’t sound like a hard problem to solve.
> IPv6, ..., is how the Internet is supposed to work

No, the way the Internet is supposed to work is that you have one routable address space. If you need to expand it, the previous address space is imported as a subset of the new one.

https://cr.yp.to/djbdns/ipv6mess.html

I will never forgive the IPv6 for not making the 32-bit IPv4 space a subrange of the 128-bit IPv6 space. Years after winning the IPng wars they admitted their mistake and standardized NAT64, but it was too late. NAT64 should have been part of IPv6 from day one, and every IPv6 router acting as a default route gateway should have been mandatorily-required to offer NAT64.

Mandating that every router has to do stateful connection tracking would have been an enormous, wasteful burden. NAT64 is there for those who need it; 464XLAT setups with IPv6-only clients are quietly the reality on networks that don't have too much legacy infrastructure (mostly mobile).
Maybe I'm misunderstanding you, but one of the points of the article in that you can represent IPv4 addresses in IPv6. In other words, IPv4 is a subset of IPv6.

If I'm on an ipv6-only host and blast UDP at ::ffff:1.2.3.4, they should get delivered to 1.2.3.4, no?

The actual, real-world problem with 4 being a subrange of 6 is that 4-only hosts are blissfully unaware that the super-range exists, so have no mechanism to send packets there. This is of course where you're right about NAT64 and the state requirements.

Isn't this what the fd/8 local addresses are for?
This is awesome; do you know if anybody has written a rails plugin to use ssrffilter by default for all requests?
Rails doesn't provide any standard mechanism/library for sending http requests, so I don't think there's anything in Rails to apply the gem to