Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove magento validator jpeg force quality #475

Closed
wants to merge 2 commits into from

Conversation

bastienlm
Copy link
Contributor

@bastienlm bastienlm commented Mar 29, 2018

I just realized, since the 1.9.3.3 update, magento forces the quality of the jpeg images to 100, which increases the weight of the image.

For me Magento should not perform this treatment.

@seansan
Copy link
Contributor

seansan commented Mar 29, 2018 via email

@bastienlm
Copy link
Contributor Author

@seansan I don't understand everything :)
but for the story about the weight, for me imagejpeg() increases the weight of the picture every time
https://stackoverflow.com/questions/11629768/image-resize-increment-file-size

I can't test this with photoshop right now, but I try this when I can.

@seansan
Copy link
Contributor

seansan commented Mar 29, 2018 via email

@colinmollenhour
Copy link
Member

Recompressing an image with 100 quality will definitely result in a larger image if it was previously less than 100 so I can see how this would do what is claimed, I don't even think a test is necessary.

The question is, is it necessary? It would seem it was done for a good security reason so just removing it from core may not be a good option, but I don't know enough about what vulnerabilities can be put inside a JPG file (very brief Google search did not expose much). From my perspective uploading a catalog image is an admin action and there is only so much you can do to protect against malicious admins.. E.g. they could just insert an <img> tag somewhere in product description linking to a malicious jpg if one existed rather than uploading it to Magento to bypass this validation. Is it really Magento's responsibility to now be a virus scanner?

Without using ImageMagick to get the original quality I don't know of a good general purpose solution which keeps the recompression and does not assume a quality setting. Perhaps the "100" should be configurable? E.g. 85 is good for most users and saves a lot of space, but some people may want 90 or higher (even though above 90 there is almost no difference whatsoever).

Quickest fix: change to 85 or 90 to improve the situation for most users.
Best fix: I don't know...

@seansan
Copy link
Contributor

seansan commented Mar 29, 2018 via email

@colinmollenhour
Copy link
Member

Yes but there are two problems with it:

  • It isn't just a check, it modifies the image making it take a lot more space. Perhaps the user took great care to save it with proper color profiles, squash the size losslessly, etc.. Now Magento just blindly recompresses it with the crappy GD library using 100 quality.
  • This assumes that GD doesn't have vulnerabilities from reading jpeg files (like imagemagick did for PNG files not too long ago) because if it does then the validation itself will expose a vulnerability. Not likely, but then it seems whatever vulnerability they are trying to protect from is about equally unlikely.

Perhaps this recompression should be a configurable option for those that are more concerned with photos than this particular security vulnerability or have sufficient workarounds in place?

@bastienlm
Copy link
Contributor Author

bastienlm commented Mar 30, 2018

Magento already leave the choose to make the reprocess image with the config
'general/reprocess_images/active'

/** if 'general/reprocess_images/active' false then skip image reprocessing. */
if (!Mage::getStoreConfigFlag('general/reprocess_images/active')) {
      return null;
}

So, I think, and colin already say that, this upload is only available for admin users and if you trust your admin user, you can disable reprocess images.
For me this config is maybe too generic and it's maybe interesting to disable only the jpeg reprocess.

@Flyingmana
Copy link
Contributor

There can be so many reasons why this was added, but there is a config to disable it, so I dont see a need to change the code.

@seansan
Copy link
Contributor

seansan commented May 2, 2018

There does not seem to be any backend gui config for it, just a flag in app/code/core/Mage/Core/etc/config.xml

https://github.com/OpenMage/magento-lts/search?utf8=%E2%9C%93&q=reprocess&type=

So adding a backend config flag to enable/disable would be a good idea

@seansan
Copy link
Contributor

seansan commented May 2, 2018

After looking into this (and running into this exact problem: result was bloated bloated 200-300kb images uploaded as 30-40kb) ... terrible

I came to this conclusion. If we can detect the CompressionQuality then just store it like it was

If we cant detect it then keep the default (which is 75)

I think the wrong here is that it overwrites the default behavior of imagejpeg which has a quality of 75 to 100

Leading to the unexpected behavior

                            if( class_exists("Imagick") ) {
                                $img = new Imagick($filename);
                                $quality = $img->getImageCompressionQuality();
                                if ($quality > 0) {
                                    imagejpeg($img, $filePath, $quality);
                                }
                            } else {
                                imagejpeg($img, $filePath);
                            }

@partounian
Copy link

Bump

@bastienlm
Copy link
Contributor Author

Reprocessing cause other problem with gif format. Dynamic gif media become static after reprocessing...

@tbaden tbaden added the review needed Problem should be verified label Jun 1, 2019
@sreichel
Copy link
Contributor

After looking into this (and running into this exact problem: result was bloated bloated 200-300kb images uploaded as 30-40kb) ... terrible

I came to this conclusion. If we can detect the CompressionQuality then just store it like it was

If we cant detect it then keep the default (which is 75)

I think the wrong here is that it overwrites the default behavior of imagejpeg which has a quality of 75 to 100

Leading to the unexpected behavior

                            if( class_exists("Imagick") ) {
                                $img = new Imagick($filename);
                                $quality = $img->getImageCompressionQuality();
                                if ($quality > 0) {
                                    imagejpeg($img, $filePath, $quality);
                                }
                            } else {
                                imagejpeg($img, $filePath);
                            }

Where is this code from?

@sreichel sreichel added the Component: Core Relates to Mage_Core label Jun 5, 2020
@sreichel sreichel closed this Aug 26, 2020
@sreichel sreichel added invalid review needed Problem should be verified and removed review needed Problem should be verified invalid labels Aug 26, 2020
@sreichel sreichel reopened this Aug 26, 2020
@tmotyl
Copy link
Contributor

tmotyl commented Aug 26, 2020

A little context about potential security issues.
The problem is that its not enough to just check if the image has valid image headers/content inside.
There were several attacts to PHP applications which uses files which at the same time balid images and contained malicious code.
E.g. a valid jpeg file being at the same time a valid phar archive (and using some unserialize as attac vector). You can read more about this example (and how to protect yourself) here: https://github.com/TYPO3/phar-stream-wrapper

Is this validator used only in admin context? Or is /can be also used when Magento allows to upload files by customers?

@colinmollenhour
Copy link
Member

Thanks for the info @tmotyl, that's kinda along the lines of what I assumed the issue was.

In a properly configured web server it should not be possible to execute any files in the media directory so it is a risk that should already be fully mitigated. That's not to say that allowing phar files to be uploaded to your media directory is a good practice so the more layers of security the better. :)

What about adding some basic detection of a hidden phar file when the auto re-encoding is disabled? E.g. searching for __HALT_COMPILER() in the contents of the jpg (apparently this is a requirement for phar files, based on my brief research - https://www.php.net/manual/en/phar.fileformat.stub.php).

@fballiano fballiano closed this Jun 2, 2022
@fballiano fballiano reopened this Jun 2, 2022
@fballiano fballiano changed the base branch from 1.9.3.x to 1.9.4.x June 2, 2022 11:21
@fballiano
Copy link
Contributor

What should we do with this? Add a configuration for general/reprocess_images/active? Change the logic to detect the compression level? Close the issue and leave everything as it is?

@addison74
Copy link
Contributor

I read all the posts from the links mentioned by @seansan. As far as I understand the quality of a new image when it is resized from another is questioned. Opinions are divided, some say that by resizing the new saved image has a larger size, others say that the issue is not confirmed.

I did a test by processing a high resolution TIFF image in Photoshop where I export it as a JPEG at a (width/height of 1600px, resolution 72, quality 100). The file size that I call it master was 255 kB. I opened this image in Photoshop where I saved it as JPEG at a (width/height of 800px, resolution 72, quality 100). This time the file size was 233 kB. Then I opened this image and saved it again at a (width/height of 400px, resolution 72, quality 100). its size was 89 KB.

Extrapolating this test that I did to the situation in Magento when saving a master image to a smaller size it should not increase its size on disk. Well, I looked in all directories in /catalog/products/cache and I did not find an larger than the master. All are smaller as file size.

The quality of a resized image should be configured in the Backend in no case eliminated as I saw that it is proposed by this PR. Any administrator can decide what quality fits his needs. But if he chooses for a quality less than 100 as it is now hardcoded maybe he will get a better file size, but he will lose the visual quality. I didn't confirm that the image size increases in disk size and I searched a lot on Internet related to php imagejpeg(). Maybe we should look into the Magento 2 code and see if there have been any changes on this topic.

My opinion asking to @fballiano we need that configuration in Backend. I am evaluating if detecting compression/quality level brings any benefits.

@colinmollenhour
Copy link
Member

Extrapolating this test that I did to the situation in Magento when saving a master image to a smaller size it should not increase its size on disk.

Your test does not use the same methodology that Magento uses, it may be true for Photoshop but it is not for Magento/PHP.

Here is a real test done with two images, one is a high quality wallpaper weighing in a 4.3MB, and the other is the same image that was saved at 75% quality that is almost half the size.

Test source, copied from the Magento code to have the exact same resampling strategy:

<?php

function recompress($filePath) {
  var_dump(filesize($filePath));
  list($imageWidth, $imageHeight, $fileType) = getimagesize($filePath);
  $image = imagecreatefromstring(file_get_contents($filePath));
  $img = imagecreatetruecolor($imageWidth, $imageHeight);
  imagealphablending($img, false);
  imagecopyresampled($img, $image, 0, 0, 0, 0, $imageWidth, $imageHeight, $imageWidth, $imageHeight);
  imagesavealpha($img, true);
  imagejpeg($img, $filePath.'X', 100);
  var_dump(filesize($filePath.'X'));
}
recompress('./original.jpg');
recompress('./quality-75.jpg');

Results:

/var/www/html/test.php:4:
int(4330074)
/var/www/html/test.php:12:
int(9176579)
/var/www/html/test.php:4:
int(2423079)
/var/www/html/test.php:12:
int(5843514)

So the Magento strategy is extremely inefficient and doubles the image size in both cases.

Changing the hard-coded 100 to 90 has much better results, the original image is slightly smaller and the 75% image is slightly larger:

/var/www/html/test.php:4:
int(4330074)
/var/www/html/test.php:12:
int(3701318)
/var/www/html/test.php:4:
int(2423079)
/var/www/html/test.php:12:
int(2761400)

At 85 the smaller version stays almost the same size:

/var/www/html/test.php:4:
int(4330074)
/var/www/html/test.php:12:
int(3232244)
/var/www/html/test.php:4:
int(2423079)
/var/www/html/test.php:12:
int(2457552)

So a quick fix would be change 100 to 85, but it is possible that someone who hates bandwidth and load times could not like this change. For everyone else it would be a huge win.

@addison74
Copy link
Contributor

I appreciate your reply Colin. PHP testing highlights the issue.

In this case my opinion would be to have that input box in the Backend where the value can be set and we should use 85, making a brief clarification to consider what happens if it is 100. Also, we can explain more in detail in another file that we use for documentation.

My proposal is to close this PR and make a new one with the necessary clarifications.

@addison74
Copy link
Contributor

addison74 commented Jun 9, 2022

Here are some suggestions on disabling image quality https://magento.stackexchange.com/questions/186405/supee-9767-patch-increasing-jpg-image-sizes.

insert into core_config_data (scope, scope_id, path, value) values ('default', '0', 'general/reprocess_images/active', '0')
case IMAGETYPE_JPEG:
   imagejpeg ($img, $filePath);

Both options are obscure in my opinion and I think the input box version in Backend is the way to go. If the value is set to zero covers both suggestions, but there is an opportunity to allow the administrator to add whatever quality he wants, as you suggested 85-90 should be ideal.

@tmotyl
Copy link
Contributor

tmotyl commented Jun 9, 2022

I'm ok with allowing admin to enable/disable image processing on upload.
It should be marked as potentially dangerous with link to security notice.
However if a shop owner has different means of ensuring security (e.g. by not allowing customers to upload data, and haveing means to validate/trust administrators) then its good to go.

@fballiano
Copy link
Contributor

I wanted to create a backend configuration for this, using the general/reprocess_images/active path but it doesn't make sense to have it in the general section, it should be in the admin section. so I don't know.

@kiatng
Copy link
Contributor

kiatng commented Sep 29, 2022

+1 To move general/reprocess_images/active to admin/security/..... After all, it is a security setting.

@fballiano
Copy link
Contributor

But if somebody tweaked this setting in their config.xml? :-\

kiatng added a commit to kiatng/magento-lts that referenced this pull request Sep 30, 2022
elidrissidev pushed a commit that referenced this pull request Oct 5, 2022
* Fixed issue #475.

* Removed <frontend_type>text</frontend_type> as it is the default.

* Fixed bug on users not able to modify image quality in backend if deprecated config exists.

* Fixed bug on incorrect check if image quality was not set in backend.

* Improved note in system.xml.
sreichel added a commit that referenced this pull request Oct 17, 2022
* Fixed "should return string but returns false"

* Fixed "should return XYZ but returns false"

* Fixed "should return array but returns null"

* Fixed "should return string but returns null"

* Fixed "should return int but returns null"

* Fixed "should return bool but returns"

* Fixed docs (see comments)

* Fixed "should return array"

* Update app/code/core/Mage/Adminhtml/Block/Widget/Grid/Massaction/Abstract.php

Co-authored-by: Ng Kiat Siong <kiatsiong.ng@gmail.com>

* Update app/code/core/Mage/Catalog/Model/Product/Attribute/Tierprice/Api.php

Co-authored-by: Ng Kiat Siong <kiatsiong.ng@gmail.com>

* Update app/code/core/Mage/Tag/Model/Resource/Tag.php

Co-authored-by: Ng Kiat Siong <kiatsiong.ng@gmail.com>

* Update app/code/core/Mage/Customer/Block/Form/Register.php

Co-authored-by: Ng Kiat Siong <kiatsiong.ng@gmail.com>

* Added module names to helper(#2617)

* Get catalog search result collection from engine (#2634)

* Add PHP dependencies security check workflow (#2639)

* [security-workflow] Fixed cron syntax (#2640)

* Added OpenMage Contributors Copyright (#2295)

* Added ddev snippets to docs (#2575)

* Improve dev/openmage/install.sh script for newer versions of Docker - no longer requires separate compose.

* Only run workflows when relevant files change (#2641)

* Add back notification popup severity icons URL (#2633)

* Reduce reprocessed jpeg file size by defaulting quality to 85% (#2629)

* Fixed issue #475.

* Removed <frontend_type>text</frontend_type> as it is the default.

* Fixed bug on users not able to modify image quality in backend if deprecated config exists.

* Fixed bug on incorrect check if image quality was not set in backend.

* Improved note in system.xml.

* Prevented editing of a non-editable order (#2632)

* Fix dev/openmage/install.sh script setting permissions on var directory.

* Allowed automatic full invoice from API (#2393)

* Add check if array key isset before using it (#2649)

* Fixed phpstan-baseline.neon

* Fixed phpstan-baseline.neon (updated dev tools)

* Revert "Fixed phpstan-baseline.neon"

This reverts commit 3c82e76.

* Fixed getRegion()

Co-authored-by: Ng Kiat Siong <kiatsiong.ng@gmail.com>
Co-authored-by: Mohamed ELIDRISSI <67818913+elidrissidev@users.noreply.github.com>
Co-authored-by: Justin Beaty <51970393+justinbeaty@users.noreply.github.com>
Co-authored-by: Colin Mollenhour <colin@mollenhour.com>
Co-authored-by: luigifab <31816829+luigifab@users.noreply.github.com>
Co-authored-by: Przemysław Piotrowski <przemyslaw.p@deligo.pl>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Component: Core Relates to Mage_Core review needed Problem should be verified
Projects
None yet
Development

Successfully merging this pull request may close these issues.