It is not uncommon to find one of your images, the one you spent hours clicking and perfecting, being used on another website without your permission. What is worse is the offender not giving proper credit for the image.
The open nature of internet does not prevent anyone from using your image URL on their personal website or app. Or from downloading your image and saving it to their computer and then using it on their website or app. This post looks at a few basic techniques that can reduce the possibilities of someone directly using your URL on their website or app (the first of the two problems mentioned above).
The methods are more applicable to real-time image transformation servers, the ones that modify images on-the-fly using certain parameters in the URL. But some of them can be extended to even non real-time image servers, the ones that serve pre-transformed images from an existing image storage.
For this post, we would be using the following image as an example.
<a class="markup--anchor markup--pre-anchor" href="https://ik.imagekit.io/demo/img/tr:f-jpg/medium_cafe_B1iTdD0C.jpg" target="_blank" rel="noopener" data-href="https://ik.imagekit.io/demo/img/tr:f-jpg/medium_cafe_B1iTdD0C.jpg">https://ik.imagekit.io/demo/img/tr:f-jpg/medium_cafe_B1iTdD0C.jpg</a>
Now let’s say, to prevent misuse or to at least get the deserved credit for the image, you want to overlay a watermark on the image. Also, instead of sharing the larger original image, let’s say you wish to use a 500px x 500px image. You could do this using Photoshop or any other image-editing software. But this is not scalable if you are handling several thousands of images.
If you were to use a real-time image transformation service, like ImageKit, you can get the required image by specifying this change these transformations in the URL. Note that the URL contains tr:h-500,w-500,oi-logo-white_SJwqB4Nfe.png to specify the height (h), width (w) and the overlay image (oi).
<a class="markup--anchor markup--pre-anchor" href="http://ik.imagekit.io/demo/img/tr:f-jpg,h-500,w-500,oi-logo-white_SJwqB4Nfe.png/medium_cafe_B1iTdD0C.jpg" target="_blank" rel="noopener" data-href="http://ik.imagekit.io/demo/img/tr:f-jpg,h-500,w-500,oi-logo-white_SJwqB4Nfe.png/medium_cafe_B1iTdD0C.jpg">http://ik.imagekit.io/demo/img/tr:f-jpg,h-500,w-500,oi-logo-white_SJwqB4Nfe.png/medium_cafe_B1iTdD0C.jpg</a>
The very purpose of URL-based transformations is to provide flexibility around different image operations. This means that any of the transformations that are applied from the URL can be modified or removed from the URL to get a different variation of the image. A third-party with the intention of misusing the above URL could just change the transformation string and access a watermark-less, resized image like this.
<a class="markup--anchor markup--pre-anchor" href="http://ik.imagekit.io/demo/img/tr:h-200,w-200/medium_cafe_B1iTdD0C.jpg" target="_blank" rel="noopener" data-href="http://ik.imagekit.io/demo/img/tr:h-200,w-200/medium_cafe_B1iTdD0C.jpg">http://ik.imagekit.io/demo/img/tr:h-500,w-500/medium_cafe_B1iTdD0C.jpg</a>
To prevent this unauthorised change in the transformed image, here are a few methods that will, to a certain extent, prevent unauthorised use of your image URLs or prevent modification to your image URLs without authorisation. These methods are not just specific to ImageKit, but even to an image server that you might want to build.
1. Transformation restrictions
Limiting the transformations that are allowed on your images is the easiest way to prevent modification to your image URLs. This is something that you would need to code on your image server. The incoming transformation request is checked against the allowed transformations. If a match is found, then the image is served else an appropriate error response is sent to the client.
Advantage: Restricting transformations is useful in keeping a large development team on the same page in terms of image transformations to be used.
Disadvantage: The image transformations get bound to the user interface of your application. Any update to the user interface that requires a new image transformation would require a change in the application logic to permit the new transformation. The list could potentially become unmaintainable over years of development and design changes.
2. Signature-based restrictions
You don’t need to check each image transform from a map to confirm its validity. After all, the basic check that we need to make is that the image URL has originated from an authorised source.
To accomplish this, whenever your application server creates an image URL, it should calculate an HMAC-SHA1 digest (or any hash) of all the transformations in the URL and the image name and use a secret key that is known only to your application server and your image server. This hash needs to be passed as a query parameter or a path parameter to the image URL.
Your image server, before serving the image would check for the validity of the hash against the transformations and the image name provided in the URL using the same secret key that was used by the application server.
This ensures that the transformation or the image name cannot be changed without changing the hash. And since the hash uses a secret key that is known only to your servers, it is impossible for someone to guess the hash for a different transformation.
The advantage of this method is that none of the transformations is restricted and the logic of allowing or restricting an image is independent of the user interface. This method just ensures that the new transformations can only be generated by authorised applications.
3. Signature-based restrictions with expiry time
The slight disadvantage of the above method is that the unmodified-URL can still be used in other applications. To prevent this, we can add upon the hash that we calculated in the previous step.
Let us consider that you want the URLs to automatically expire at the end of the month (choosing such an expiry has other advantages like CDN and client-side caching). This means that even if someone gets hold of an image URL from your application, he would be able to use that only up to the end of the current month. After that, a new URL would be needed for the same image.
To accomplish this, we will get the UNIX timestamp of the end of the current month i.e. the intended expiry time. So if the current month is December 2016, then we get the timestamp for 31 December 2016, 23:59:59. This timestamp is 1483208999. We will use this timestamp to calculate the HMAC-SHA1 hash and also add this as a parameter in the URL.
The image server would then check for the validity of the hash against the transformations, image name and the expiry timestamp. Only if the hash is valid and the expiry timestamp in the URL is still in the future, only then can the image be served. Also, if someone tries to change the expiry time in the URL or removes it completely, the hash becomes invalid. Same when someone tries to change the image transformation.
Note: The idea of choosing the expiry time as month-end ensures that all URLs generated for a resource in the current month are the same. This ensures caching on the CDN (if any) and the user’s device given that the URL does not change for that month. The image server, to accommodate edge cases where the image URL generation and the actual request are on either side of the specified expiry time, can allow for a grace period of a few minutes over the expiry time.
The above techniques can be applied to your own image server or are readily available when you use ImageKit.
Do share your suggestions and feedback about the above techniques. Or if you use some other technique at your organisation, please share those in the comments for the benefit of other readers.