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

带有防盗链的图片

防盗链技术,是一种较为低级的防盗用的技术。比如 μ’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

提示我们 invalid Referer header,从这里我们能看出原因,是因为咱们请求的 https 链接头部没有携带在网站白名单中的域名,导致防盗链功能生效,阻止了我们的访问。

这些防盗链的设置很常见,在 CDN 和 OSS 中都能找到相关的设置:

对象存储中的防盗链设置
CDN中的防盗链设置

仅有在白名单中的这些域名进行 https 请求,才可以获得许可允许访问资源。

方法一:修改前端

知道了防盗链的原理(就是判定 Referer header 的域名是否在白名单中),我们就知道如何解决了。第一种方法就是,直接修改前端进行访问。

我们直接在前端修改已有的 <img> 图片的链接,或者新增加一个 <img> 标签,让图片在前端显示出来:

任意找到一个 <img>

替换图片的链接:

替换 <img> src 链接

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

因为这样保存出来的照片是 .webp 格式的,不是很方便。

方法二:控制台打开

第二种方法就很简单了,我们利用 window.open() 函数即可解决问题。

window.open('https://cdn.lovelive.cx/ms/web/banner/Kousaka-Honoka/7.jpg')

在当前页面打开代码审查,然后在控制台执行一行 js 代码,即可携带当前网站的 header referer 在新窗口打开图片,然后保存。

控制台执行 window.open

方法三:抓包

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

Burp Suite 抓包页面

我们只需要截取这个请求,再手动增加 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()

3 thoughts on “带有防盗链的图片获取方法

  1. 博主这篇博客真棒,通过方法三的启示,成功找到了个修改referer的浏览器插件reference manager解决了这个问题。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

Next Post

Github个人资料页面自定义调整

周六 2 月 13 , 2021
我们都知道 Github 的个人资料页面的 Pop […]