logo of Shuibaco

AWS图床防盗链

2023. / 2,401字 / 2,167阅 / 1评

老博友应该知道,我博客上的图片视频什么的都是放在AWS S3上的,蹭了一年免费到期之后收费还是非常合理(约等于零),另据国内博友友情反馈,官方的cdn服务CloudFront在祖国大陆也特别给力,所以我便打算“长住”下来。但S3 + CloudFront搭建图床有个“历史遗留问题”——没有防盗链功能。虽然我这无名小博没什么访客(也没黑客),且根据收费情况来看图床流量也不大,但居安思危的传统美德还是让我在前阵子折腾博客的时候决定解决一下这个问题。先说结论,搞定了🎉!直接访问图片❌,除了本博客以外的网站引用❌,完美达到我的要求。

先搞清楚在哪里设置防盗链

在记录具体做法之前,我想先啰嗦一下。其实更早之前我在查静态博客生成器的时候偶然找到了S3存储桶的防盗链写法,便兴奋地保留了网页,以为只要花点时间更新一下存储桶策略就能搞定,心态非常乐观——

另一方面,此次探索我意外得知了图床防盗链的方法,有空搞搞看,成功的话就补充在之前的博文里。

结果等我终于挤出时间进一步研究的时候发现,因为我使用CloudFront(以下简称CF)分发,所以防盗链应该设置在CF上而非S3存储桶上。搭建图床已经是两年前的事了,别说当时就似懂非懂照猫画虎,时隔多年现在基本也没剩什么记忆了。

说实话,我也是跟着网上的教程一步步设置的,对于整体的机制不甚了解,所以有点儿心虚。

既然如此,我干脆重新研究了用S3做图床的机制,以及使用cdn分发的优势,顺带求证了关于防盗链应该设置在cdn上的想法。

我把图片全部上传到S3的某个存储桶里,并且设置了仅CF读取的策略,所以除了CF任何人都没法直接访问这个桶。也因此,作用于S3的防盗链策略实际上并不会被触发。简单说来,存放在S3里的资源只有CF一个出口,所以要限流就应该在CF上做文章。

当有人阅读我的博客时,浏览器便向CF请求图片,如果之前已经请求过,那么CF会直接调取缓存,而不需要再向S3伸手。要知道S3的收费点在于储存空间大小与请求次数,所以使用CF除了可以加快图片读取速度,还能给我省钱。当然CF也是要收费的,但和S3一样也有一定的免费额度供使用。具体多少我没仔细查,但根据我查资料时获得的额外信息,CF肯定要比S3优惠,至少不会更贵吧。所以我决定积极使用cdn分发,因为它能保护我即便在真的被资源盗链的情况下也能将损失降到最低。

说实话了解到这一层,我发现防盗链好像也不算刚需,除非除了省钱,还对自己博客上的图片被无端引用或盗取感到厌烦。毕竟认真写博的人也会认真做图,都是自己的心血,被无故偷走还是会心痛。我的博客是生活向,所以被盗用的价值不高,但毕竟私人图片,自己博客上发发没关系,未经允许被发在其他地方就觉得挺不舒服的。总之,注重原创精神的我有“洁癖”,不想自己博客上的图片在别处也能显示。于是我继续调查,有趣的是,我查到不少人推荐转到Cloudflare,我也终于想起来为什么当时我在文末留下了这么一句话——

总之我先用着,反正S3和CloudFront有一年免费期,到期后再转到Cloudflare吧。

CloudFront or Cloudflare?

查了很久都没查到直接在CloudFront上设置防盗链的方法,倒是明白了我的需求更适合使用signed cookie而非signed URL。但我还是没弄明白到底怎么设置,好像不能仅在AWS上搞定。于是我把目光投向了Cloudflare,颜控的我更喜欢它的界面设计,对比之下AWS简直直男审美。Cloudflare直接就有防盗链设置,简单填写就能使用,非常适合我这样的技术小白。随后我又了解到它也能托管域名,也有对象存储服务R2,收费比S3还诱人,于是我的DNA动了,又想“举家迁移”了,明明上个月我才决定把域名转到Route 53。

Plan B is ready并且虎视眈眈,亚马逊你给我争气点!然后我就找到了用AWS WAF防火墙限制访问的方法。粗略了解下来,它的功能跟Cloudflare提供的服务大同小异,本着能少折腾就不折腾的躺平思想,我决定先试试WAF,如果费用难以承受,再搬也不迟。于是我按照官方博客上的介绍一步步设置,成功使用referer添加了唯一一个白名单域名——blog.shuiba.co。之后我尝试直接访问博客上的图片,非常完美地返回了403。又把图片贴在了为测试而特意新建的blogger上,图片也没法显示。有人说Cloudflare仅保护图片不保护视频等其他多媒体文件,我没条件测试Cloudflare2023年8月27日更新:已测试,如果是最简单的Scrape Shield里的Hotlink Protection设置,确实如此,但测试了WAF是可以保护视频的。

详细步骤

写这篇博文的时候我又查了一些资料,找到了一些奇形怪状的方法,而每种方法都有利弊,可因为我实在过于门外汉,所以根本搞不清楚它们之间的差异,找到最适合自己的方法。不论如何,能找到一种我能实施的办法就已经很不容易了,且先用着。
注:以下步骤中省略的部分皆为默认。

  1. 在服务中搜索WAF,选择WAF & Shield;
  2. 创建Web ACL,填写名称比如HotlinkProtection,CloudWatch也会自动写入相同名字;
  3. Resource type选择Amazon CloudFront distributions,此时Region会自动变为Global (CloudFront)
  4. 点击Add AWS resources关联对应CF分发,Next下一步;
  5. 点击Add rules选择Add my own rules and rule groups,在弹出的页面上选择Rule Builder创建自定义规则;
  6. 填写规则名称比如Whitelist,Type默认Regular rule
  7. 因为这条规则的基本逻辑是——如果符合条件则允许访问,所以If a request matches the statement, Inspect Single Header, Header field name填入Referer, Match type Contains string, String to match填入白名单域名,比如我的是blog.shuiba.co,Text transformation选择Lowercase以确保大小写不影响判断结果。如果有多个域名,则If a request matches all the statements (OR),重复以上步骤填入所有域名即可;
  8. Then选择Allow表示符合以上条件则许可,然后Add rule;
  9. Default web ACL action for requests that don't match any rules选择Block,整个Web ACL的逻辑变为——如果不满足添加的规则,则全部拒绝;
  10. 接下来一路默认,最后确认各项配置无误后Create web ACL。

设置好WAF之后可以从浏览器打开图片或视频等其他文件地址,如果无法访问,则说明设置成功。

终于又解决了一个老大难,也把这篇历时三天的博文写完了,长舒一口气~技术类的文章因为逻辑性很强所以特别花时间,而如果时间过于零碎则又需要额外的时间来关联上下文,这就导致好不容易有点时间了,刚回顾完前文连上逻辑准备接着写,又到时间了。不过似乎经过这阵子的“被迫练习”,我的回顾速度提升了不少,虽然肯定还是不如一整片时间来得高效率,但起码也能写下去了,这比起之前没法零碎写文的我来说已经算是巨大的进步。

参考文章

1,247°
因博客而结识的朋友
Comments
Write a Comment
  • Marshall reply

    您好,详细步骤的第七条是不是有点问题呀?如果有多个域名的话,应该是使用OR条件而不是AND条件。

    • @Marshall 我的理解是多个域名都在白名单里,所以用AND。

      • Marshall reply

        @水八口 假设有两个域名在白名单里,按照AND来设置的话,此时判断的代码可以假设为`if (domain == "domain1.tld" && domain === "domain2.tld")`,但是很显然这是一个永远不可能为真的判断式,因为domain不可能同时满足所有的条件,它不能在等于domain1.tld的时候又同时等于domain2.tld,因此我觉得此处应该使用OR。

        • @Marshall 啊真的,你说得对!我简单地认为往白名单里添加域名了,实际上添加的是条件。谢谢,我这就修改一下~

          • @水八口 AWS的门槛实在是有点高,设置的时候确实很难不避免出现一些疏忽,不过还是得感谢您的分享,如果不是您的文章我可能还得摸索很久。👍

            • @Marshall 能帮上忙真的太令人高兴了,也感谢您的反馈,使文章内容更为准确😊

点击加载Disqus