逆向工程专业摄影平台的水印
专业摄影师通常会在图片上添加水印,以便识别、标明版权、推广品牌等。在线图库商店常采用多种措施防止图片被盗,例如禁用JavaScript右键菜单、堆叠/覆盖DOM元素或其他混淆尝试。
当我收到家庭照片时,注意到摄影师使用平台的水印,并对它的工作原理产生了兴趣。立即想到两种可能:
- 生成离散的图像资源并输出到文件系统,然后由网络服务器直接在画廊中提供
- 使用服务器端处理动态生成带有叠加水印的新图像
深入分析
虽然不会直接指明具体网站,但让我们仔细查看画廊的示例标记:
1
2
3
|
<a class="thumb-image">
<div style="background-image: url("https://example.com/image/eyJmZWF0dXJlZF9pbWFnZSI6ZmFsc2UsInNpemUiOiJtZWRpdW0iLCJ1dWlkIjoiMzk5NDg5MWUtNGQ2Yy0xMWVkLTk5NTItZmExNjNlOWMiLCJjcm9wIjoiZmFsc2UiLCJub19jcm9wIjoidHJ1ZSIsInYiOiIxNjY1OTM2MjczIiwid2F0ZXJtYXJrIjoxNjY1OTM3MTQ1LCJ0aHVtYiI6dHJ1ZX0=");" class="img-tag" data-id="81393248"></div>
</a>
|
这里通常期望使用img标签而不是带有背景图像的div,但有趣的是路径不以图像扩展名(如.png)结尾。
完整URL:https://example.com/image/eyJmZWF0dXJlZF9pbWFnZSI6ZmFsc2UsInNpemUiOiJtZWRpdW0iLCJ1dWlkIjoiMzk5NDg5MWUtNGQ2Yy0xMWVkLTk5NTItZmExNjNlOWMiLCJjcm9wIjoiZmFsc2UiLCJub19jcm9wIjoidHJ1ZSIsInYiOiIxNjY1OTM2MjczIiwid2F0ZXJtYXJrIjoxNjY1OTM3MTQ1LCJ0aHVtYiI6dHJ1ZX0=
对于有经验的人来说,路径参数立即显现为可能的Base64编码字符串。解码后确认:
1
2
3
4
5
6
7
8
9
10
|
{
"featured_image": false,
"size": "medium",
"uuid": "3994891e-4d6c-11ed-9952-fa163e9c",
"crop": "false",
"no_crop": "true",
"v": "1665936273",
"watermark": 1665937145,
"thumb": true
}
|
这里有许多有趣的用户控制参数。为了确认它们可以被修改并被后端接受,我将size值编辑为"small"并重新编码:
1
2
3
|
[rw ~]$ JSON='{"featured_image":false,"size":"small","uuid":"3994891e-4d6c-11ed-9952-fa163e9c","crop":"false","no_crop":"true","v":"1665936273","watermark":1665937145,"thumb":true}'
[rw ~]$ echo -n $JSON | base64 -w 0
eyJmZWF0dXJlZF9pbWFnZSI6ZmFsc2UsInNpemUiOiJzbWFsbCIsInV1aWQiOiIzOTk0ODkxZS00ZDZjLTExZWQtOTk1Mi1mYTE2M2U5YyIsImNyb3AiOiJmYWxzZSIsIm5vX2Nyb3AiOiJ0cnVlIiwidiI6IjE2NjU5MzYyNzMiLCJ3YXRlcm1hcmsiOjE2NjU5MzcxNDUsInRodW1iIjp0cnVlfQ==
|
在路径中替换这个新编码的参数确实显示了较小的图像。接下来,我好奇是否支持来自客户端的任何额外参数——更具体地说,我想知道水印行为是否也是用户控制的。我在JavaScript源代码中搜索featured_image的提及,并找到了有用的结果:
1
2
3
4
5
6
|
featured_image_encoded = btoa(JSON.stringify({
uuid: self.featured_image.uuid,
width: 150,
featured_image: true,
ignore_watermark: true,
}));
|
这证实了我的怀疑——所有关于图像处理的控件,包括水印本身,都是由客户端指定的。
让我们在原始URL上添加ignore_watermark键,值设为true:
1
2
3
4
5
6
7
8
9
10
11
|
{
"featured_image": false,
"size": "medium",
"uuid": "3994891e-4d6c-11ed-9952-fa163e9c",
"crop": "false",
"no_crop": "true",
"v": "1665936273",
"watermark": 1665937145,
"thumb": true,
"ignore_watermark": true
}
|
重新编码为Base64并发出请求后,服务器成功响应了干净版本的图像。
替代方法
最终,我已经支付了照片费用并直接从摄影师那里收到了原件。我出于好奇偶然发现了这一点,不禁注意到平台对摄影师的保护存在根本性的设计缺陷。
更好的方法可能是让后端决定在哪些条件下可以显示原始照片。如果绝对需要客户端方法(可能由于使用单独的微服务进行资源生成),签名JWT将是确保客户端请求合法的更好选择。