Obfuscating Image Links
Latest update:
I noticed this recently, though it started happening about a year ago.
On some websites (archive.org's bookreader), a normal <img> tag
suddenly began to look like some insane MS Internet Explorer extension
from 1998:
<img src="blob:https://example.com/b501e863-fe43-4b63-ae5d-dac14cac097e">
The web page that contains it renders the image fine, but when a
fairly naïve user posts that link into a chat, it results in
nothing: the blob referenced by <img> exists only in the memory of a
specific browser instance, & the server will return 404 for
any https://example.com/UUID.
Why do they do this? Every n years some people get scared of
hotlinking (bastards keep stealing our traffic), AI luddites try to
ruin business of evil corporations, & fans of toy-level DRM amuse
themselves with a new scheme.
If you look at the fetch-requests of such a page, you'll see
resources that look like images but actually aren't:
$ url='https://ia800206.us.archive.org/🙈.jpg'
$ curl -sI "$url" | grep -e type -e length -e obfuscate
content-type: image/jpeg
content-length: 267470
x-obfuscate: 1|uoEV6/PZOWtGhOZdVM898w==
$ curl -s "$url" | head -c25 | file -
/dev/stdin: data
Such a .jpg is encrypted. Part of the key is in the X-Obfuscate
header, but neither a random AI scraper nor any social network knows
about this. The cipher is also not disclosed, & every website may use
any scheme it wants: following any recommendations would defeat the
entire purpose of obfuscation.
The image-rendering algorithm then becomes:
download the encrypted file;
decrypt it using the key from the corresponding header & push the
result into a blob;
create a link to the blob using URL.createObjectURL function;
inject into the DOM an img element whose src attribute is equal
to the newly created link.
We can increase entropy further by writing our own custom element:
<img-blob alt="a fluffy cat" src="cat.bin"></img-blob>
that will do all of the above on its own. (I terser'ed the source
code of the example for I absolutely don't want you to use it in
anything serious: the entire approach is extremely user-hostile &
anti-web.)
For efficiency, archive.org AES-CTR-encrypts only the first 1024 bytes
of the image. Browsers know about AES but strictly require a secure
context that can be annoying during testing; hence, for mickey mouse
DRM we can simply use XOR-encryption.
The X-Obfuscate header itself can be obfuscated even more, e.g.:
x-obfuscate: rlW2MKWmnJ9hVwbtZFjtVzgyrFV6VPVkZwZ0AFW9Pt==
looks like a base64 string, but:
$ echo rlW2MKWmnJ9hVwbtZFjtVzgyrFV6VPVkZwZ0AFW9Pt== | base64 -d | xxd
base64: invalid input
00000000: ae55 b630 a5a6 9c9f 6157 06ed 6458 ed57 .U.0....aW..dX.W
00000010: 3832 ac55 7a54 f564 6706 7400 55bd 3e 82.UzT.dg.t.U.>
I checked if DeepSeek could figure it out: it spent 9 minutes & left
2 villages in Zhejiang province without water, was several times
very close to the target, but ultimately failed.
The string had been post-processed with rot13:
$ alias rot13="tr 'A-Za-z' 'N-ZA-Mn-za-m'"
$ echo rlW2MKWmnJ9hVwbtZFjtVzgyrFV6VPVkZwZ0AFW9Pt== | rot13 | base64 -d
{"version": 1, "key": "12345"}
As homework, you can add an equivalent of loading="lazy" to the
custom element using the Intersection Observer API.
Tags: ойті
Authors: ag