/dims4/
/dims4/<client>/<signature>/<expires>/<commands>/?url=<image>
A signed request. The signature covers the commands and the image URL, so neither can be changed without the secret.
/dims4/CLIENT/0c0bf3/2147483647/resize/100x100/?url=https://example.com/cat.jpg
| Segment | Meaning |
|---|---|
client | a client id from DimsAddClient |
signature | the first six characters of the digest below |
expires | when the URL stops working, in seconds since the epoch |
commands | the operations to run |
Query parameters
| Name | Meaning |
|---|---|
url | the source image |
eurl | the source image, encrypted |
download | 1 sends Content-Disposition: attachment |
optimizeResize | overrides DimsOptimizeResize |
overlay | the watermark image, for the watermark command |
_keys | which other parameters the signature covers, comma separated |
Signing
Join these with nothing between them:
expires, in seconds since the epoch- the client's secret
- the commands, with a trailing slash and no leading one
- the image URL, exactly as it appears in
url, not percent encoded - the value of each parameter listed in
_keys, in that order
Take the MD5 of that, hexadecimal and lowercase, and use its first six characters.
Example
| client | CLIENT |
| secret | a-secret |
| expires | 2147483647 |
| commands | resize/100x100 |
| image | https://example.com/cat.jpg |
The message is:
2147483647a-secretresize/100x100/https://example.com/cat.jpg
Its MD5 is 0c0bf324d2eba5f0a2cbdf9a84a18332, so the signature is 0c0bf3
and the URL is:
/dims4/CLIENT/0c0bf3/2147483647/resize/100x100/?url=https%3A%2F%2Fexample.com%2Fcat.jpg
The commands have a trailing slash in the message. The image URL is percent encoded in the query string but not in the message.
Code
expires=2147483647
secret=a-secret
commands=resize/100x100/
url=https://example.com/cat.jpg
printf '%s%s%s%s' "$expires" "$secret" "$commands" "$url" | md5sum | cut -c1-6
import hashlib
def sign(secret, expires, commands, url, keys=None, params=None):
message = f"{expires}{secret}{commands.rstrip('/')}/{url}"
for key in (keys or []):
message += params[key]
return hashlib.md5(message.encode()).hexdigest()[:6]
function sign($secret, $expires, $commands, $url, $keys = [], $params = []) {
$message = $expires . $secret . rtrim($commands, '/') . '/' . $url;
foreach ($keys as $key) {
$message .= $params[$key];
}
return substr(md5($message), 0, 6);
}
import {createHash} from 'crypto';
function sign(secret, expires, commands, url, keys = [], params = {}) {
let message = `${expires}${secret}${commands.replace(/\/$/, '')}/${url}`;
for (const key of keys) {
message += params[key];
}
return createHash('md5').update(message).digest('hex').slice(0, 6);
}
require 'digest'
def sign(secret, expires, commands, url, keys = [], params = {})
message = "#{expires}#{secret}#{commands.chomp('/')}/#{url}"
keys.each { |key| message += params[key] }
Digest::MD5.hexdigest(message)[0, 6]
end
Signing another parameter
Only url and the commands are covered by default. To cover overlay as
well, list it in _keys and append its value to the message:
/dims4/CLIENT/<sig>/2147483647/watermark/0.2,0.5,se/?url=<image>&overlay=<overlay>&_keys=overlay
message = expires + secret + "watermark/0.2,0.5,se/" + image + overlay
Several parameters are appended in the order _keys gives, not in the order
they appear in the query string.
Expiry
A request whose expires has passed is refused with 400.
DimsSecretMaxExpiryPeriod caps how far ahead an
expiry may be, which stops a caller minting a URL that never expires.
Unsigned parameters
overlay and optimizeResize are covered only when _keys lists them. List
them in _keys so the signature covers them.
Set DimsAllowlistSigned to enforce so the
host allowlist applies to a signed request too. Prefer
/dims5/, which signs with HMAC-SHA256, for a new
integration.