I noticed that each time I save a jpg file in PHP, it is saved with sub-sampling. How to remove that? I'm using GD library.
I believe newer versions of libgd disable chroma-subsampling if you set the quality to 90 or higher.
Failing that, you could consider using PHP Imagick and disabling chroma sub-sampling with:
$img->setSamplingFactors(array('1x1', '1x1', '1x1'));
I currently use Imagick library on PHP and use the resizing functionality of Image Magick. I've just learned about decompression bombs and how ImageMagick is vulnerable to it.
I have checked how we can ping the image and verify the dimensions of the image without actually loading it into memory/disk. It's also safer to limit the memory and disk limits of ImageMagick so it wouldn't just write a huge file on disk.
I've read and I can do that with setResourceLimit().
http://php.net/manual/en/imagick.setresourcelimit.php
IMagick::setResourceLimit(IMagick::RESOURCETYPE_MEMORY , 100);
IMagick::setResourceLimit(IMagick::RESOURCETYPE_DISK , 100);
$thumb = new Imagick('image.png');
$thumb->resizeImage(320,240,Imagick::FILTER_LANCZOS,1);
However, what happens is that after setting the limit of disk and memory, if an image does hit this limit, all I get is a segmentation fault error, no exceptions are thrown. This makes it impossible for me to handle it properly.
Update:
Here are the package versions I'm using:
dpkg -l | grep magick
ii imagemagick-common 8:6.6.9.7-5ubuntu3.3 image manipulation programs -- infrastructure
ii libmagickcore4 8:6.6.9.7-5ubuntu3.3 low-level image manipulation library
ii libmagickwand4 8:6.6.9.7-5ubuntu3.3 image manipulation library
ii php5-imagick 3.1.0~rc1-1 ImageMagick module for php5
Setting the 'Resource Area' limit only sets the size at which images are not held in memory, and instead are paged to disk. If you want to use that setting to actually restrict the maximum size image that can be openend, you also need to set the 'Resource Disk' limit.
The code below correctly gives a memory allocation error for the image bombs taken from here.
try {
Imagick::setResourceLimit(Imagick::RESOURCETYPE_AREA, 2000 * 2000);
Imagick::setResourceLimit(Imagick::RESOURCETYPE_DISK, 2000 * 2000);
$imagick = new Imagick("./picture-100M-6000x6000.png");
$imagick->modulateImage(100, 50, 120);
$imagick->writeImage("./output.png");
echo "Complete";
}
catch(\Exception $e) {
echo "Exception: ".$e->getMessage()."\n";
}
Output is:
Exception: Memory allocation failed `./picture-100M-6000x6000.png' # error/png.c/MagickPNGErrorHandler/1630
If you want to set the width and height resource, and have a version of ImageMagick >= 6.9.0-1 you should be able to using the values directly of WidthResource = 9, HeightResource = 10
//Set max image width of 2000
Imagick::setResourceLimit(9, 2000);
//Set max image height of 1000
Imagick::setResourceLimit(10, 1000);
These don't have to be set programmatically, you can set them through the policy.xml file installed with ImageMagick. ImageMagick reads that file and uses those settings if none are set in a program - which may be a more convenient way of setting them, as you can change them per machine.
This makes it impossible for me to handle it properly.
It makes it impossible for you to handle it in the same process. You can handle it just fine by running the image processing in a background task.
Personally I think anyway that uses Imagick in a server that is accessed directly by web-browsers is nuts. It is far safer to run it in as a background task (managed by something like http://supervisord.org/) and communicating with that background task via a queue of jobs that need to be processed.
Not only does that solve the 'bad images can bring down my website' problem, it also makes it far easier to monitor resource usage, or shift the image processing to a machine with a faster CPU than a web-front end server needs.
Source - I'm the maintainer of the Imagick extension and I recently added this to the Imagick readme:
Security
The PHP extension Imagick works by calling the ImageMagick library.
Although the ImageMagick developers take good care in avoiding bugs it
is inevitable that some bugs will be present in the code. ImageMagick
also uses a lot of third party libraries to open, read and manipulate
files. The writers of these libraries also take care when writing
their code. However everyone makes mistakes and there will inevitably
be some bugs present.
Because ImageMagick is used to process images it is feasibly possible
for hackers to create images that contain invalid data to attempt to
exploit these bugs. Because of this we recommend the following:
1) Do not run Imagick in a server that is directly accessible from
outside your network. It is better to either use it as a background
task using something like SupervisorD or to run it in a separate
server that is not directly access on the internet.
Doing this will make it difficult for hackers to exploit a bug, even
if one should exist in the libraries that ImageMagick is using.
2) Run it as a very low privileged process. As much as possible the
files and system resources accessible to the PHP script that Imagick
is being called from should be locked down.
3) Check the result of the image processing is a valid image file
before displaying it to the user. In the extremely unlikely event that
a hacker is able to pipe arbitrary files to the output of Imagick,
checking that it is an image file, and not the source code of your
application that is being sent, is a sensible precaution.
Starting with ImageMagick-6.9.0-1, "width" and "height"
resource limits were added. From the commandline, use "-limit width 32000", etc. ImageMagick's PNG decoder will bail out without decompressing the image if the width or height exceeds the specified limit.
The PNG decoder will not attempt to decompress images whose width or height exceeds the limits.
The "area" resource is available in earlier versions of ImageMagick (and Imagick); however the PNG decoder does not reject images based on the "area" limit (see Danack's comment).
In versions of ImageMagick earlier than 6.9.0, the width and height limits come from libpng, and depend upon the libpng version. Current libpng versions (1.0.16 and later, 1.2.6 and later, 1.5.22 and later, and 1.6.17 and later) impose 1,000,000-column and width limits. In versions 1.2.0 through 1.2.5, 1.5.0 through 1.5.23, and 1.6.0 through 1.6.16, the limits were 2.7 billion rows and columns by default.
Look for RESOURCETYPE_AREA in Imagick (I don't see _WIDTH or _HEIGHT in the manual that you referenced, so either Imagick or its manual needs to be updated). So try
IMagick::setResourceLimit(IMagick::RESOURCETYPE_AREA , 100M);
to set a 100MegaPixel limit. Hopefully, some future version of Imagick will support RESOURCETYPE_WIDTH and RESOURCETYPE_HEIGHT, to provide a better solution to the decompression-bomb vulnerability. See Danack's answer about setting these with the current version of IMagick.
all I get is a segmentation fault error, no exceptions are thrown
I'm guessing your segment fault is from resources being set too low for ImageMagick (and related delegates) to operate. The value of the resource are in bytes, not megabytes.
Imagick does throw an exception if a resource is reached. Usually something like...
"cache resource exhausted"
Decompression bombs, or Zip-Bombs, are extremely difficult to identify. What your doing by ping-ing the image, and setting resource limits is the correct course of action. I would roughly outline a solution as...
// Define limits in application settings, or bootstrap (not dynamically!)
define('MY_MAGICK_MEMORY_LIMIT', 5e+8);
// Repeat for AREA, DISK, & etc.
// In application
$image = new Imagick(); // allocate IM structrues
// Set limits on instance
$image->setResourceLimit(Imagick::RESOURCETYPE_MEMORY, MY_MEMORY_LIMIT);
// Repeat for RESOURCETYPE_AREA, RESOURCETYPE_DISK, & etc.
$filename = 'input.png';
if($image->ping($filename)) {
// Validate that this image is what your expecting
// ...
try {
$image->read($filename); // <-- Bomb will explode here
$image->resizeImage(320,240,Imagick::FILTER_LANCZOS,1);
} catch( ImageickException $err ) {
// Handle error
}
}
unset($image)
If you don't trust decompression, you can leverage Imagick::getImageCompression during ping validation to inspect which compression an image requires. The compression type would be a integer that would map to the following enum...
typedef enum
{
UndefinedCompression,
B44ACompression,
B44Compression,
BZipCompression,
DXT1Compression,
DXT3Compression,
DXT5Compression,
FaxCompression,
Group4Compression,
JBIG1Compression,
JBIG2Compression,
JPEG2000Compression,
JPEGCompression,
LosslessJPEGCompression,
LZMACompression,
LZWCompression,
NoCompression,
PizCompression,
Pxr24Compression,
RLECompression,
ZipCompression,
ZipSCompression
} CompressionType;
MagickStudio (written in PERL) offers a good starting point for default resource limits, and how they are checked against uploaded images (search for Ping.)
This is the first time I use PHP Imagick and experiencing problems with it.
I'm creating a website module where users can upload images that need to be converted to an encrypted file type containing the uploaded image as a DDS texture.
So what I need to do is convert the uploaded image to dds file format.
Code:
$img = new Imagick('test.png'); //Load the uploaded image
$img->setformat('dds'); //Set the format to dds
$img->setImageCompression(Imagick::COMPRESSION_DXT3); //Set compression method
$img->writeimage('test.dds'); //Write/save the dds texture
The problem: the output file is always an empty, 0 byte file.
PNG That needs to be converted: test.png
DDS That needs to be the output: test.dds
Maybe I need other version Imagick or ImageMagick?
My old version of these are:
PHP 5.5.15
Imagick 3.1.2-5.5-ts-vc11-x86
ImageMagick 6.8.4-0-Q16-x86
I am currently testing on Windows 8.1 x64.
Any suggestion/help would be appreciated.
Edit: Edited the question, hopefully its clear now what I am trying to achieve.
Solved: The problem was the version of ImageMagick that had no support for DDS write.
Installed ImageMagick 6.8.6-10-Q16-x86 since the DDS write support was added in that version (newer versions caused the imagick not to be loaded).
Current version of what I use and works:
PHP 5.5.15
Imagick 3.1.2-5.5-ts-vc11-x86
ImageMagick 6.8.6-10-Q16-x86
From the debugging above the issue is that your version of ImageMagick doesn't support writing DXT3 compressed DDS files.
The options to solve this problem are:
Use a different compression format - DXT5 might be okay, but it's not exactly the same as DXT3.
Upgrade to a newer version of ImageMagick and Imagick that support writing in DXT3 format.
I'm looking to use image masks in Typo3. After trying out various things and none of them worked, I decided to check out the install tool.
Install tool says ImageMagick is installed correctly, paths are correct and all image test complete successfully, except the convert/combine test. The test says "There was no result from the ImageMagick operation".
Also, when I'm using gifbuilder to output an image the image's dimensions get read correctly, allthough the outputted image stays blank. (which is white or another color when I have backColor set)
I am using:
Max OSX 10.7.5
XAMPP
PHP 5.3.1
Typo3 6.0.4
ImageMagic 6.7.3-2
Here is some test typoscript code (which on my install results in a fully red image instead of the original image)
lib.test = IMAGE
lib.test {
file = GIFBUILDER
file {
XY = [10.w],[10.h]
backColor = #FF0000
10 = IMAGE
10.file = fileadmin/user_upload/test.jpg
}
}
Attached is the result from the combining images test.
Does anyone have an idea as to what I might be doing wrong?
Some newer imagemagick versions use composite as command instead of combine. There is an option in the install tool to set the command to use. The setting is called [GFX][im_combine_filename].
I found that I did not have PNG support in my Imagick installation, so I installed libpng. Now I can see that PNG format is supported when I run a command
convert -list format
And conversion from PNG to JPG works fine from command line:
convert a.png b.jpg
But when I try to read a PNG file in PHP:
$src = new \Imagick();
$src->readImageBlob( file_get_contents($file) ); // reading PNG format
-it gives me Error 500 ("...no decode delegate for this image format")
Also when I run phpinfo() - I see no PNG in list of Imagick's supported file formats. There are many others, but no PNG.
I tried to I resintall PHP Imagick driver and then restart apache - still no succeess.
PS: I have PHP 5.4 # CentOS
According to the installation requirements. "The amount of formats supported is by Imagick is entirely dependent upon the amount of formats supported by your ImageMagick installation." I imagine you’ll need to rebuild the PHP imagemagick extension. See here: http://php.net/manual/en/imagick.installation.php
Problem solved.
I used a guide for Imagick installation #CentOS from the link below, reinstalled everything exactly as it said there, and it helped - http://www.directadmin.com/forum/showthread.php?t=44449&page=1