阅读 939

以Referer方案写一个图片防盗链服务并实现网页端"破解"

什么是盗链

资源不在自己服务器上, 而通过技术手段, 把资源放置到自己的网站中, 通过这种方法盗取他人的资源.

什么是Referer

Refererhttp请求header的一部分, 当浏览器(或者模拟浏览器行为)向web服务器发送请求的时候,头信息里有包含 Referer. 它表示当前接口的访问来源. Referer的正确英语拼法是referrer. 由于早期http规范的拼写错误, 为了保持向后兼容就将错就错了. 其它网络技术的规范企图修正此问题, 使用正确拼法, 所以目前拼法不统一.

为什么要设置防盗链

  • 防爬虫(流量暴涨, 服务提供者却是受害者)
  • 安全

如何设置防盗链

此处以基于express的二次开发框架nestjs起一个小demo

你完全可以用token验证等方法去实现更严格的防盗链

...(如何添加路由等等就不说了, 有兴趣直接去看看我的代码, 直接跳到验证器的路由代码去)

routers/doorChain/controller.ts

import express from 'express';
import { Get, Headers, Res, Controller } from '@nestjs/common';
import { DoorChainService } from './service';

@Controller('door-chain')
export class DoorChainController {
    constructor(private readonly doorChainService: DoorChainService) {}

    @Get()
    root(@Headers('referer') referer: string, @Res() res: express.Response) {
        return this.doorChainService.root(referer, res);
    }
}
复制代码

routers/doorChain/service.ts

import * as url from 'url';
import * as path from 'path';
import express from 'express';
import { Injectable } from '@nestjs/common';
import logger from 'utils/logger';

@Injectable()
export class DoorChainService {
    root(referer: string, res: express.Response) {
        let imageName = 'break.png';
        if (!referer) {
            imageName = 'yellow.png';
        } else {
            try {
                const refererParsed = url.parse(referer);
                if (refererParsed.host === 'localhost:8080') {
                    imageName = 'yellow.png';
                }
            } catch (err) {}
        }
        const imagePath = path.resolve(__dirname, `./../../assets/${imageName}`);
        res.sendFile(imagePath, null, err => {
            if (err) {
                logger.error(err);
                res.status(404).end();
            } else {
                logger.info(`Sent: ${imagePath}`);
            }
        });
    }
}
复制代码

主要的逻辑代码就那么几行!

验证的基本思路:

  • 当referer为空时, 返回正确的图
  • 当referer不为空, 且host命中我的目标网站时, 返回正确的图
  • 当referer不为空, 但host未能命中我的目标网站时, 返回错误的图

启动服务, 访问http://localhost:3333/door-chain, 返回正确的图!!!

如何去掉访问限制

这次我们仅讨论怎么在网页端去掉访问的限制, 另外还有七牛镜像储存, 或者把实现方案交由服务端处理等等都是可以的, 而且原理都是一样, 这里不一一赘述.

以下简述去掉header中的Referrer的两种方法:

  • 添加name为referrer, content为never(whatwg标准, 兼容性相对较好)或no-referrer(MDN标准)的meta标签

    策略名称 属性值(MDN标准) 属性值(whatwg标准)
    No Referrer no-referrer never
    No Referrer When Downgrade no-referrer-when-downgrade default
    Origin Only origin -
    Origin When Cross-origin origin-when-crossorigin -
    Unsafe URL unsafe-url always

    无论选择哪一个值, 都有一个缺点, 就是默认情况下全部资源(包括接口)都被处理了

  • 对标签添加ReferrerPolicy属性

    更精确的指定了某一个资源的referrer策略

    相对以上的值列表, ReferrerPolicy在此基础上扩展了三个可供选择的值:

    • same-origin 对于同源的请求会发送引用地址,但是对于非同源请求则不发送引用地址信息。
    • strict-origin 在同等安全级别的情况下,发送文件的源作为引用地址(HTTPS->HTTPS),但是在降级的情况下不会发送 (HTTPS->HTTP)。
    • strict-origin-when-cross-origin 对于同源的请求,会发送完整的URL作为引用地址;在同等安全级别的情况下,发送文件的源作为引用地址(HTTPS->HTTPS);在降级的情况下不发送此首部 (HTTPS->HTTP)。

    如果在纯粹开发的角度上, 这个方式是接近完美的, 因为没有污染到其他任何东西. 再来看看浏览器兼容性方面:

    常规值的问题也不大.

    新扩展的三个值的兼容性图

    有点不容乐观啊!

    关于扩展img标签属性

    在tsx中img标签默认是不支持referrerPolicy的, 实现方案可以参考ts-react-webpack, 查看我是如何扩展的, 欢迎来踩!!!

当然了, 虽然常用的会是类似上述的方案, 总的来说, 这里只是防盗链知识体系中的凤毛麟角, 仍有待探索.

此外还有referrer-killer等等的项目可以参阅

关注下面的标签,发现更多相似文章
评论