So, you have some type of application that does simple image manipulation – in most web applications case, resizing. You use some library, you construct an object with the image binary as a stream, and it does some wizardry magic and poof, you’ve just resized the image and stored it in a file in an image format of your choosing.
But wait – some image formats aren’t that trivial. Maybe we don’t care too much about the detail of how it does this, but we do care about what happens to our streams, how it’s used, and how much memory is consumed – after all, we wouldn’t want our application to crash or become unresponsive, right?
*Note that Image.FromFile
is the same as Image.FromStream
without having to create the stream from the file yourself.
Now, let’s create a fairly large image.
void Main()
{
var outPath = @"C:\in.gif";
var width = 12000;
var height = 12000;
// huge bitmap
var bitmap = new Bitmap(width, height);
using (var g = Graphics.FromImage(bitmap))
{
g.FillRectangle(Brushes.Black, 0, 0, width, height);
}
bitmap.Save(outPath, ImageFormat.Gif);
}
This creates a measly 116 KB gif file.
Let’s see how simple it is to resize an image in .NET
with what’s available in the .NET Framework.
void Main()
{
var origPath = @"C:\in.jpg";
var resizedPath = @"C:\out.jpg";
var img = Image.FromFile(origPath);
var resized = new Bitmap(600, 400);
using (Graphics g = Graphics.FromImage(resized))
{
g.DrawImage(img, 0, 0, 600, 400);
}
resized.Save(resizedPath, ImageFormat.Jpeg);
}
This is a fairly simple code that resizes our input image to 600 x 400.
Most web applications have some form of validation for minimum width and height for an image, along with image format and file size upload limit.
The problem is, most web application don’t have a validation for an upper bound for image dimensions. This can be exploited quite easily due to image compression.
All image resizing algorithm first buffers the image to resize as a bitmap in memory, to do all the usual computations to produce a new bitmap image. This means that the image that we produced at 12,000 x 12,000 dimension, although only being 116 KB as a compressed gif, would actually be at around 138 MB in memory as a minimum just for the first phase of buffering in memory as a bitmap, plus all the other extra overhead.