📜 ⬆️ ⬇️

How I learned to protect images


Protection image

In this article I want to explain the hard way that I went through “protecting” images on the web. Before we begin this fascinating journey, I want to designate two approaches to protecting images:
  1. restriction / prohibition of posting direct links to original images
  2. you are paranoid and try to limit the distribution of copies of images

UPDATE
Universal protection of course does not exist. An article on how not to directly insert data from GET into SQL queries. Only in the context of image protection.


Limit copies: my kids bike


At the beginning of my journey was traditionally a bicycle. Many years ago I was developing one great project. There were so many wonderful photos of animals and nature. It was these photos (or rather their full-size version) that had to be protected with all their might. The client did not want just to prohibit direct links to image files, but to deprive the user of the opportunity to download these same images. At the same time impose watermarks did not want.

We have already read that programmers lie all the time . So I had to do what the client wanted. The decision was even quite nice. When you request a page with a photo, we generate a certain $secretKey and save to the session under this key the path to the full-size copy of the image:
')
 public function actionView() { // ... $_SESSION['protected-photos'][$secretKey]['file'] = $photoPath; // ... } 

In the view, we indicate the path to the photo in the following form:

 <img src="/photo/source/{secretKey}" /> 

Now in actionSource we get the path to the full-size copy of the photo from the session, send it with the correct headers and clear the path to the full-size file:

 public function actionSource() { $secretKey= $_GET['key']; $session = &$_SESSION['protected-photos']; $file = $session[$secretKey]['file']; if (is_file($file)) { header('Content-type: image/jpeg'); echo file_get_contents($file); } $session[$secretKey]['file'] = ''; } 

As a result, if a user tries to download / open in a new tab / share a picture, a small copy of it will be returned to him.

Important: The weak point of this approach is pretty obvious: if you request a page with a photo not from the browser, but say via wget . In this case, the img tag will not make the request /photo/source/{secretKey} . Thus, it will contain a full-size copy of the photo.

Restrict direct links: .htaccess


Later I learned that the easiest and most common way to protect images is to configure .htaccess accordingly. You can not only prohibit direct links to images, but also specify a stub that will be displayed on third-party resources instead of the original images from your site. Here is an example of this configuration:

 RewriteEngine On RewriteCond %{HTTP_REFERER} !^http://(.+\.)?mysite\.com/ [NC] RewriteCond %{HTTP_REFERER} !^$ RewriteRule .*\.(jpe?g|gif|png)$ http://i.imgur.com/qX4w7.gif [L] 

The first line contains a directive that includes the operation of the transformation mechanism. Everything is simple here. The second line, we block any sites except our own mysite.com. The code [NC] means “no options,” in other words, case-insensitive URL matching. The third line, we allow empty referrals. And, finally, the last line of files all the files with the extension JPEG, JPG, GIF or PNG and replaces them with the image qX4w7.gif from the server imgur.com .

If necessary, you can do otherwise: prohibit direct links to images for specific domains.

 RewriteEngine On RewriteCond %{HTTP_REFERER} ^http://(.+\.)?myspace\.com/ [NC,OR] RewriteCond %{HTTP_REFERER} ^http://(.+\.)?blogspot\.com/ [NC,OR] RewriteCond %{HTTP_REFERER} ^http://(.+\.)?livejournal\.com/ [NC] RewriteRule .*\.(jpe?g|gif|png)$ http://i.imgur.com/qX4w7.gif [L] 

Each RewriteCond, except the last, must contain the code [NC, OR] . OR means "or next", i.e. match current domain or next.

Also, instead of a stub image, you can return an HTTP error with code 403:

 RewriteRule .*\.(jpe?g|gif|png)$ - [F] 

Important: do not try to return an HTML page instead of images. You can return either another image or an HTTP error.

Restrict direct links: nginx


For nginx, everything is the same:

 location ~* \.(jpe?g|gif|png)$ { set $bad_ref "N"; if ($http_referer !~ ^(http://(.+\.)?myspace\.com|http://(.+\.)?blogspot\.com|http://(.+\.)?livejournal\.com)) { set $bad_ref "Y"; } if ($bad_ref = "Y") { return 444; } } 

Update: VBart suggested in his comments that it is much better to use ngx_http_referer_module for these purposes.

Restrict direct links: Amazon CloudFront Signed URLs


Amazon CloudFront is one of the best content delivery options for users. In addition to his direct duties as an ordinary CDN, he also gives the opportunity to generate signed links. Such links provide an opportunity to restrict access to the file by time range, as well as by IP. Thus, for example, you can specify that the image will be available within 10 minutes. Or 7 days from tomorrow.

Average, the link to the file is as follows:

one d111111abcdef8.cloudfront.net/image.jpg? 2 color=red&size=medium 3 &Policy=eyANCiAgICEXAMPLEW1lbnQiOiBbeyANCiAgICAgICJSZXNvdXJjZSI6Imh0dHA 6Ly9kemJlc3FtN3VuMW0wLmNsb3VkZnJvbnQubmV0L2RlbW8ucGhwIiwgDQogICAgICAiQ 29uZGl0aW9uIjp7IA0KICAgICAgICAgIklwQWRkcmVzcyI6eyJBV1M6U291cmNlSXAiOiI yMDcuMTcxLjE4MC4xMDEvMzIifSwNCiAgICAgICAgICJEYXRlR3JlYXRlclRoYW4iOnsiQ VdTOkVwb2NoVGltZSI6MTI5Njg2MDE3Nn0sDQogICAgICAgICAiRGF0ZUxlc3NUaGFuIjp 7IkFXUzpFcG9jaFRpbWUiOjEyOTY4NjAyMjZ9DQogICAgICB9IA0KICAgfV0gDQp9DQo 7IkFXUzpFcG9jaFRpbWUiOjEyOTY4NjAyMjZ9DQogICAgICB9IA0KICAgfV0gDQp9DQo 4 &Signature=nitfHRCrtziwO2HwPfWw~yYDhUF5EwRunQA-j19DzZrvDh6hQ73lDx~ -ar3UocvvRQVw6EkC~GdpGQyyOSKQim-TxAnW7d8F5Kkai9HVx0FIu-5jcQb0UEmat EXAMPLE3ReXySpLSMj0yCd3ZAB4UcBCAqEijkytL6f3fVYNGQI6 ~ -ar3UocvvRQVw6EkC ~ GdpGQyyOSKQim-TxAnW7d8F5Kkai9HVx0FIu-5jcQb0UEmat EXAMPLE3ReXySpLSMj0yCd3ZAB4UcBCAqEijkytL6f3fVYNGQI6 5 &Key-Pair-Id=APKA9ONS7QCOWEXAMPLE

And now the points:
  1. The basic link to your image. This is the link that you used to access the image and earlier, before the signed links.
  2. Arbitrary query parameters, which are usually used to log access to images. CloudFront allows you to transfer, cache and log these parameters. Important: the name of the parameters should not coincide with the reserved CloudFront itself: Expires , Key-Pair-Id , Policy , Signature . It is best to add the prefix x- to your parameters. This will be especially useful if your images are stored on Amazon S3 .
  3. Access rules for images in JSON-format and without spaces ( details ).
  4. A hashed and signed version of the access rules from the previous clause ( details ).
  5. Signature key ( details ).

Important: CloudFront does not support CNAMEs with HTTPS . Those. you cannot replace d111111abcdef8.cloudfront.net d111111abcdef8.cloudfront.net on images.example.com images.example.com . There are two possible solutions to the problem:
  1. Re-use domain https://*.cloudfront.com for images.
  2. Leave the domain images.example.com , but use it via the HTTP protocol.
The choice of one of two options is essentially a matter of taste. In principle, they do not differ among themselves.

Epilogue


I hope the approaches described above will help you navigate faster in the difficult task of protecting images on the web. And some useful related links:

  1. Hotlinking: Generator .htaccess
  2. Hotlinking: .htaccess configuration
  3. Hotlinking: Nginx configuration example
  4. Hotlinking: Validation
  5. Amazon CloudFront Signed URLs

Source: https://habr.com/ru/post/176299/


All Articles