我们在网上总会保存一些自己喜欢的图片,当然很多时候是不能用简单的“右键另存为”来保存图片的,需要配合浏览器的代码审查下的网络功能来获取图片地址,然后保存。但是,有一些时候我们打开这些图片链接会遇到一些阻碍…
带有防盗链的图片
防盗链技术,是一种较为低级的防盗用的技术。比如 μ’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人
在呢 😛