Security ESI issue with symfony2

2019-06-03 08:09发布

问题:

I'm facing a security problem with ESI on Symfony2 (2.2) :

Some ESI of my application don't require to be logged and are public, but other ESI required the user to be logged and have the role ROLE_USER, for example.

The problem is that every guy can display an ESI, by writing its URL in the address bar of the navigator... So a man can access to a ESI (that is called inside an action which requires an admin role), even none logged !

For example, my ESI "SybioWebsiteBundle:Controller:showEsiAction" can be read at this URL : http://mywebsiteurl.com/_proxy?_path=id%3D1%26slug%3Dlorem%26locale%3Dfr%26ranks%3D1-2-3-5-6-7%26page%3D1%26isPhotograph%3D1%26_format%3Dhtml%26_controller%3DSybioWebsiteBundle%253AAlbum%253AshowEsi

This ESI should only be visible by logged users : they go to showAction, and inside the Twig template, this ESI is called... But all people can cheat and read thanks to this "issue" !

I searched solutions, I got one really ugly : check if the user is logged in the ESI action... it's OK, but I'm using HTTP Cache validation to optimize my site loading (and memory). So if I choose this solution, I need to add an additionnal ETag that test the user role, to clear the ESI cache each time a none user access to the ESI, and display an empty response, and after if one is logged, clear it again and display the normal view, etc...

I now that people who want to cheat will be uncommon, so it could be a satifying solution... In theory, the cache won't be constantly cleared because of them, luckily !

But I want to know if you have another solution ? Thanks !

回答1:

In <=2.1 versions, the ESI URL came from importing the internal.xml routing file, which exposed a regular Symfony route that was capable of rendering any controller.

If a normal user had access to this, they could render any controller with any arguments in your system, which is the issue you are currently facing.

In >=2.2, the internal.xml routing file is gone. You now have a fragments key in config.yml. Instead of a route, this activates a listener that watches for any requests that start with /_proxy, which is the URL that the ESI tags now render as.

This alone doesn’t help security, except that the listener uses a few tricks internally.

So what prevents an evil user from exploiting this URL to render any controller in our system with any parameters? Since 2.2, there are two built in protections: trusted proxies and signed URLs.

The class that handles all this magic is called FragmentListener. Before it starts serving anything from your application, it first checks to see if the person requesting is “trusted”.

If you’re using a reverse proxy like Varnish, then you’ll want to add its IP address or - CIDR IP address range for the super-geeks - to your config.yml file:

framework
    trusted_proxies:
        - 192.168.12.0

If the request comes from this IP or range, it allows it. And, if it comes from a local address, it also allows it. In other words, if it’s someone you trust, then it’s ok.