我们在网上总会保存一些自己喜欢的图片,当然很多时候是不能用简单的“右键另存为”来保存图片的,需要配合浏览器的代码审查下的网络功能来获取图片地址,然后保存。但是,有一些时候我们打开这些图片链接会遇到一些阻碍…

带有防盗链的图片
防盗链技术,是一种较为低级的防盗用的技术。比如 μ’s时光蛋 的首页图片链接:
https://cdn.lovelive.cx/ms/web/banner/Kousaka-Honoka/7.jpg_/quality/80/scale/40
这个图片链接的末尾 _/quality/80/scale/40,实际上是对象存储的图片参数,可以调整图片的大小和尺寸,而原图的实际地址当然也就是:
https://cdn.lovelive.cx/ms/web/banner/Kousaka-Honoka/7.jpg
将这个链接在新窗口中打开会遇到错误提示:

提示我们 invalid Referer header,从这里我们能看出原因,是因为咱们请求的 https 链接头部没有携带在网站白名单中的域名,导致防盗链功能生效,阻止了我们的访问。
这些防盗链的设置很常见,在 CDN 和 OSS 中都能找到相关的设置:


仅有在白名单中的这些域名进行 https 请求,才可以获得许可允许访问资源。
方法一:修改前端
知道了防盗链的原理(就是判定 Referer header 的域名是否在白名单中),我们就知道如何解决了。第一种方法就是,直接修改前端进行访问。
我们直接在前端修改已有的 <img> 图片的链接,或者新增加一个 <img> 标签,让图片在前端显示出来:

替换图片的链接:

然后另存为图片即可。当然,可能有人会问为什么不直接在代码审查的网络下,直接保存图片呢?

因为这样保存出来的照片是 .webp 格式的,不是很方便。
方法二:控制台打开
第二种方法就很简单了,我们利用 window.open() 函数即可解决问题。
window.open('https://cdn.lovelive.cx/ms/web/banner/Kousaka-Honoka/7.jpg')在当前页面打开代码审查,然后在控制台执行一行 js 代码,即可携带当前网站的 header referer 在新窗口打开图片,然后保存。

方法三:抓包
知道 CTF(夺旗战)的应该知道,我们常常使用 Burp Suite 这款抓包软件来截取浏览器的每一次 Get 和 Post 请求。

我们只需要截取这个请求,再手动增加 Referer,然后发送出去,即可。
拓展:防盗链和URL鉴权
开头已经说过了,防盗链技术是一种较为低级的防盗用的技术,本质是验证 Referer header。但是 Referer 内容可以伪造,所以 Referer 防盗链方式无法彻底保护站点资源。更高的方法就是 URL鉴权。
以下是用 Python 写的一个 Demo,包含 A、B、C 三种鉴权方法,有兴趣的同学可以深入研究下:
import re
import time
import hashlib
import datetime
def md5sum(src):
m = hashlib.md5()
m.update(src)
return m.hexdigest()
#鉴权方式A
def a_auth(uri, key, exp):
p = re.compile("^(http://|https://)?([^/?]+)(/[^?]*)?(\\?.*)?$")
if not p:
return None
m = p.match(uri)
scheme, host, path, args = m.groups()
if not scheme: scheme = "http://"
if not path: path = "/"
if not args: args = ""
rand = "0" # "0" by default, other value is ok
uid = "0" # "0" by default, other value is ok
sstring = "%s-%s-%s-%s-%s" %(path, exp, rand, uid, key)
hashvalue = md5sum(sstring)
auth_key = "%s-%s-%s-%s" %(exp, rand, uid, hashvalue)
if args:
return "%s%s%s%s&auth_key=%s" %(scheme, host, path, args, auth_key)
else:
return "%s%s%s%s?auth_key=%s" %(scheme, host, path, args, auth_key)
#鉴权方式B
def b_auth(uri, key, exp):
p = re.compile("^(http://|https://)?([^/?]+)(/[^?]*)?(\\?.*)?$")
if not p:
return None
m = p.match(uri)
scheme, host, path, args = m.groups()
if not scheme: scheme = "http://"
if not path: path = "/"
if not args: args = ""
# convert unix timestamp to "YYmmDDHHMM" format
nexp = datetime.datetime.fromtimestamp(exp).strftime('%Y%m%d%H%M')
sstring = key + nexp + path
hashvalue = md5sum(sstring)
return "%s%s/%s/%s%s%s" %(scheme, host, nexp, hashvalue, path, args)
#鉴权方式C
def c_auth(uri, key, exp):
p = re.compile("^(http://|https://)?([^/?]+)(/[^?]*)?(\\?.*)?$")
if not p:
return None
m = p.match(uri)
scheme, host, path, args = m.groups()
if not scheme: scheme = "http://"
if not path: path = "/"
if not args: args = ""
hexexp = "%x" %exp
sstring = key + path + hexexp
hashvalue = md5sum(sstring)
return "%s%s/%s/%s%s%s" %(scheme, host, hashvalue, hexexp, path, args)
def main():
uri = "http://xc.cdnpe.com/ping?foo=bar" # original uri
key = "<input private key>" # private key of authorization
exp = int(time.time()) + 1 * 3600 # expiration time: 1 hour after current itme
authuri = a_auth(uri, key, exp) # auth type: a_auth / b_auth / c_auth
print("URL : %s\nAUTH: %s" %(uri, authuri))
if __name__ == "__main__":
main()

博主这篇博客真棒,通过方法三的启示,成功找到了个修改referer的浏览器插件reference manager解决了这个问题。
2021了,还能见到LL人
在呢 😛