The post is related to the image resize vulnerability fix introduced in Sitecore 7.5. To read more about the Sitecore fix go to the Release notes page and search for “Media request protection”. While I was holding off for a number of months on the publication of the post as it puts the attack vector in plainer sight that I would like it to be (while the community figured out how to work with Media Resizing in a neat way) – but recently I’ve seen voices raised considering turning the Media Request protection off which I hope you will not be doing after reading this post. The post will also tell you how to enable such security on your older versions of Sitecore.
So here’s the story…. At some point in Cognifide we have performed a research around Sitecore security and one of my colleagues (Marek) found out that you could easily kill any Sitecore instance by performing an image resize attack on it. While the CMS did some rudimentary checks and limited the values of height and width you could still perform an attack by harvesting the images from the site and perform multiple parallel & iterative size increase or just plain use the scale parameter to achieve any image size. A result of such attack would be a a denial of service due to 100% CPU & memory usage and would potentially allow for filling the server drive by creating the endless number of scaling calls.
Marek was even kind enough to provide a proof of concept code that confirmed the hypothesis by performing attack on a few of our internal servers. The program would load the home page; parse to find images linked from it and perform resizing of the images in a number threads.
Following the discovery I’ve attempted to remedy the problem and as a consequence came up with the solution which I have recently put on GitHub – ImageGuard which signs the rendered media links that use any of the resizing/scaling capabilities and filters all request that try to resize/scale, allowing the sizing only when the hash matches and provided it to Sitecore.
This solution is nowhere as complete as the one that was later provided by Sitecore – starting from version 7.5 – still I think it’s still worth making it public to allows for older versions of Sitecore to be guarded against this type of attacks.
Following post describes the solution I have provided to Sitecore some time ago (and is very similar in nature to what Sitecore finally implemented) and should protect you against this kind of attacks. You should only use it if migrating to a newer version of Sitecore is not an option for your company or a client – otherwise I suggest you consider upgrading your Sitecore solution to Sitecore 7.5 or newer. Obviously before deploying it on production you need to do your due diligence testing if it does not interfere with any other functionality of your Sitecore deployment. I take no responsibility for any adverse effects and for that matter any negative effects it might have for your site operation. The full source code have been provided on GitHub for your review and any corrections that the code might require to work properly within your solution. Feel free to fork the code – just make sure you and your clients are protected!
I’ve been thinking about the different solutions like white-listing or managing allowed sizes and following are my thoughts of benefits and drawbacks of using the signed sizing (that Sitecore ultimately agreed with):
- One major benefit of using the links signing is that its completely maintenance free. The CMS generated links are signed automatically and there is no process required for approval or white-listing sizes and thus there is no need for the authors to jump through any hoops when they resize the image in the rich text editor or even be careful to use one of the unapproved sizes.
- Signing is immediately compatible with the site content – no need to look through the content to find any unapproved or not white-listed sizes. All links will automatically get signed.
- The method has only minimal impact on the CMS performance as it is only employed when the image size is actually changed (the non resized images don’t get the hash attached).
- It immediately protects the Sitecore instance from ANY resizing attack (other methods potentially allow for trial-error to find out the allowed sizes and then resizing all of the assets into all allowed sizes – granted – a very limited attack surface when compared to the previous free-for-all).
- One drawback that I realize the method has – the links cannot be easily calculated from a JS client (possible though cumbersome and reveals the shared secret to the public within the script).
What should I do to use it?
You can protect any Sitecore instance simply by dropping the Cognifide.ImageGuard.dll in your bin folder and dropping the Cognifide.ImageGuard.config file in the Sitecore App_Config\Include\ include folder. The hashes generated are salted with the shared secret that you can find in the include file – just with the Sitecore provided solution make sure that you change the shared secret so that your instance does not use the same hash salt and consequently allow any attacker to reproduce the hashes your instance would be using.
If the hash does not match the Sitecore instance will simply return the unprocessed media not creating any additional load on the server and any additional cache entry. So the worst case scenario is that you will get the original image out.
How does it work?
On the outgoing side – the solution plugs into the renderField – field rendering pipeline and looks for any <img> tags that contain the dangerous parameters and hashes them together with the salt and the image item id. It leaves all the unmodified images untouched.
The solution has been written with performance in mind. My tests did not show any measurable impact. The pipeline module works as follows:
- does the field contain any “<img” tag? if not – pass through the original markup
- for each “<img” check it’s content to see if there are any parameters – if the field contains no <img> tags with parameters the rendered text is simply returned to the pipeline
- if the link contains parameters – check if it’s any of the abusable parameters – if not – return the string untouched to the pipeline.
- Up till this moment no string cutting and copying has been performed and all searches are performed on the smallest chunks of the originally rendered texts that are possible, so it’s very fast whenever the signing is not required.
- if the content contains any <img> tag with abusable parameters – the URLs are signed. This process is fairly lightweight
On the receiving side creates a thin layer over the the mediaLibrary/requestParser and when an image with modifiers is requested it creates the hash again and compares the hash to the one sent from the client. If the hashes are not identical it will return the original-unchanged image.
The following factors influence the hash:
- width (w=)
- height (h=)
- max width (mw=)
- max height (mh=)
- scale (sc=)
- media item id
Following is a list of some concerns that were raised when we worked with Sitecore on the solution.
It only covers one case, The FieldRenderer. There are more cases where it can happen
Agreed. Since it’s not a vendor solution it will not cover all situations without parsing all outgoing HTML pages which would be extremely slow. Arguably even a systemic solution (using hashing) would find it hard to deal with all of those without the partner developers making proper use of it. The code solves problem with content, it does not solve problem with the solution code itself. e.g. we’ve already identified that it can impact the Content Editor as it creates its thumbnails and enforces the resize in code. The solution provided couldn’t be perfect until it got included within the product with a through code analysis (the ideal point for it would be to modify the Sitecore.Resources.Media.MediaUrlOptions to include the signature), however this was not doable at the time of writing as the class is instantiated explicitly in a number of places (an IoC solution would be super cool for that!). The worst case scenario in those situations would be that the original image would be transferred and the scaling will happen on the client side.
But direct links to the original images still work…
This is very much the intentional behavior. The solution is not designed to deal with deep linking, but rather with the consequences of resizing attacks. The consequences of a “sc=##.####” attack are as follows:
- immediate increase in memory consumption (limited to the duration of attack – the server will eventually recover once the attack ceases )
- immediate increase in CPU load (limited to the duration of attack – the server will eventually recover once the attack ceases)
- disk space consumption – to the limit of the drive or user quota. (the server will likely not recover from this – potentially to the point of inability to boot)
The solution deals with those issues exclusively. It attempts to be completely maintenance free (no user involvement whatsoever after the code is deployed) and to be of minimum impact to the system performance and operations.
It’s a hotfix untested by the product team
That is very true – this solution is quite similar to what Sitecore ultimately went with but as I stated above it does not every case (e.g. XSLT or MCV). If you can move to Sitecore 7.5 and make use of the Sitecore’s final solution – you should.
The code looks quite CPU intensive
This is very situational. In each client’s scenario the behavior may vary depending mostly on how much scaling your content contains thus how much HTML needs to be rewritten. The solution is still open for more optimizations. Testing on our site, the solution didn’t produce any measurable slowdowns. It’s worth mentioning again that the solution works within the constraints of an external module and without the ability to replace the class mentioned above (or being otherwise included with the very product) it has to rely on parsing the rendered HTML, which will always have some overhead.
How unique are the hashes?
The solution uses SHA1 algorithm, included with the .Net framework and AFAIK is considered a fairly good hashing algorithm and an industry standard. One important thing is that the hashes do NOT need to be globally unique. The hash only needs to be unique in the context of a single media item usage (ID+width+height+scale+mw+mh) – single usage must always result in the same hash.
We decided against going public with our discovery at the time of discovery as it may have negatively affected Sitecore customers.
The code is battle tested as Cognifide clients were always protected using this solution since the moment it was developed.
This entry (Permalink) was posted on Friday, May 15th, 2015 at 12:00 am and is filed under .Net Framework, ASP.NET, C#, Open Source, Security, Sitecore, Software, Software Development, Solution, Visual Studio, Web applications. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response , or trackback from your own site.