所谓 Gravatar 的公共可识别头像服务(Globally Recognizable Avatar),就是用户通过自己的邮箱注册一个 Gravatar 的帐号,然后给此帐号上传一个自己的头像图片。 于是 Gravatar 就建立了一个全球性的“邮箱 ↔️ 头像”的关联库。 任何需要通过用户邮箱展示其头像的地方(比如:发表社交评论),都可以通过用户邮箱来向 Gravatar 获取。 这样就简化了用户需要在不同的站点上维护相同头像的麻烦。 Gravatar 有被大量广泛地使用于各种博客/个人站点/WordPress/GitHub 等。
Gravatar 的简单用法
非常简单,访问类似:https://secure.gravatar.com/avatar/{HASH}
的链接即可。
其中的 {HASH}
是通过标准的算法根据用户的邮箱计算得来的哈希值:md5(email)
/ sha256(email)
。
比如我的头像链接是:
https://secure.gravatar.com/avatar/09fe7907900043edaa672b7a2620543b。
还有一些其它的查询参数可以设置,比如大小、默认头像、头像分级等等。不在本文今天的讨论范围内,可参考:Image Requests – Gravatar Developer Docs。
本地化
另外,如果你有“你自己的网站可以访问,但是头像却加载不了”的问题,可以参考:本地化。 关于为什么要本地化:
自己做代理是因为早在十年前玩的 WordPress 的时候我就发现,官方的 Gravatar 在中国经常不能访问,但是我自己的网站却能访问,外加第三方的代理站也是经常不能访问/不稳定,所以从那时候开始我就萌生了自己代理的念头,毕竟可以和我自己的网站本身“共生死”,几个头像流量也不大。没有理由不这样做。
上面的参考链接差不多是🔟年前的代码了。Gravatar 的请求响应头部会包含一些可能包含哈希的字段,比如:
- Link:
link: <https://gravatar.com/avatar/09fe7907900043edaa672b7a2620543b>; rel="canonical"
- Content-Disposition:
content-disposition: inline; filename="09fe7907900043edaa672b7a2620543b.png"
请记得去掉,最好用白名单过滤一下就好了。
隐私与安全性㊙️
为了安全与隐私,链接中肯定是不能直接将用户的邮箱带上的,所以最容易想到且实际也被大量运用的方案就是“使用哈希后的结果”。 尽管 md5/sha256 名义上是摘要算法,不可逆,但是也免不了仍然被人们经常称其为“MD5 加密/解密”。 解密的过程并不是真正通过复杂的数学计算🧮,而仅仅是预先创建了一个 哈希 ↔️ 邮箱 的关联表,直接查表得出结果。这个表被称为“彩虹表🌈”。
而预先创建这个表需要的邮箱列表通常就是 爬取各种社区数据库/泄漏数据库/随机构造常用的 等方式得来。 一旦有了这个列表,md5/sha256 的结果也就有了。 可能是由于 md5 已经很容易被“解密”,所以 Gravatar 可能是后来把算法切换成了 sha256(没考证具体原因,反正我记得早期确实是只支持 md5 的)。 但是我觉得这完全💯没有任何用处。
随便在网上搜索🔍一下“MD5 解密”/“SHA256 解密”就能出现很多网站。比如这个:Hashes。
你可以试试把我的邮箱的哈希(09fe7907900043edaa672b7a2620543b
)粘贴上去看看我的邮箱📪是啥(“你能看见的都是我想让你看见的。”)。
然而,在当今 WordPress 占据半壁江山的时代,包括所有使用 Gravatar 作为用户头像的个人网站/博客,用户的邮箱就这样地泄露了。你可以随便找一个个人站点试试看评论区的头像。
我认为 Gravatar 也没有更好的办法解决这个问题。
我能做的🥵
为了实现我博客发表评论时“邮箱(不公开)”的承诺,我做了我自己的努力。
早期我也是直接用哈希(2015年)的,但是后来就改成了内部接口(2020年)。
接口从 /v2/avatar/{HASH}
换成了 /v3/comments/{ID}/avatar
。
后面的 {ID}
是评论的编号(注:因为我的博客目前没有“用户系统”,所以我只能根据评论编号来区分用户,否则我完全可以直接用“用户编号”来代表一个用户。),内部通过此编号反查出用户的真实邮箱,再内部代理转发 HTTP 请求获取头像。
前端不可能根据评论编号拿到用户的邮箱📮,这样就真正地保护了用户的隐私安全/我自己的数据安全。
你真的可以选择相信我而大胆地写你真实的邮箱地址,除了生成头像以及接收评论回复↩️外,不会有任何其它用途。
改接口后的性能/资源优化
在二狗那里 我发现我这个 /v3/comments/{ID}/avatar
请求不够好:
另外,昨天在跟你反馈这个问题后,我发现一个小细节/性能问题:多个评论可能是同一个人的头像,如果根据评论ID来生成头像,则强制把它们区别开了,实际上图片是同一张,这会导致请求重复/浪费网络流量。哈哈哈哈。所以我昨天简单改了下(上上面的那张图/API废弃了),内部根据邮箱生成头像“编号”返回给前端,类似一对一映射,这样请求会大量减少。
所以我再次把接口改了(2024年):/v3/comments/{ID}/avatar
➡️ /v3/avatar/{ephemeral}
。
内部会为每个邮箱生成一个可在服务重启后也尽量保持一致的、非常简单/值又小的“短生/临时(ephemeral)编号”,然后用此编号反查出用户邮箱。 和最开始使用 md5/sha256 类似,只不过这次是我用我自己选择的简单生成算法,越简单别人就越不能根据生成的值(ephemeral)反推出邮箱。
至此,一切都刚刚好。
原来gravatar还有这种安全隐患... 都没有想到过。
一眼看成了多邻国
。
今天你学习了吗!
连环夺命催!
晚上又重新看了一下这篇文章,觉得讲得还是很有道理的,于是我花了 20 分钟把我的博客评论的头像也小小隐藏了一下。不过我直接用的是 user id 这种数字字段去做的,比较懒得设计一个什么 hash 了。
https://github.com/syhily/yufan.me/pull/53
是有,有用户 ID 就直接用了,最方便省事。可惜我的博客目前没有真正的用户系统。
说个奇怪的现象,那天二狗回复了你的关于合肥的那篇文章后,我发现,在我的电脑上,就他的头像一直是错的,长下面这样,也不知道是为什么🤔。
图片来自:https://yufan.me/avatar/1690.webp。
就算我在完全没有代理的小主机上用 curl 请求,也是错误的,非常神奇,那天我们还专门研究了一下,没找到原因🤪。
233,我用的是 weavatar 的服务,很有可能是这个服务的问题,它对于一些可能敏感的头像,会自动屏蔽。
但是但是,二狗那里显示没有问题……说明有可能不是头像本身的问题🥹,我们找了一会儿原因,没有结论,就放弃了。
我这也有问题了
🤣🤣🤣🤣🤣
经过仔细测试,确认是 weavatar 的问题,目前已经替换了镜像地址,应该暂时解决了这个问题。
哈哈哈,一个猜想:是不是二狗的头像太五彩斑斓了,被某种不严谨的算法识别成了类似彩虹🌈旗的东西,被认为是政治不正确了,然后因为使用的代理所在的区域不一样,有的地方正确显示,有的地方不能。如果真的是这样的话,也太狗血了。![狗头 [狗头]](/v3/dynamic/assets/weixin/doge.png)
![狗头 [狗头]](/v3/dynamic/assets/weixin/doge.png)
![狗头 [狗头]](/v3/dynamic/assets/weixin/doge.png)
看着评论区自己的头像陷入了沉思
哈弗大狗。
感觉本地化最大的问题是,如果更新了头像并不能及时更新。
不会的。直到几天前我都还是直接原样地转发的官方请求,我本身并没有缓存这个头像。官方是 cache-control 5️⃣分钟,我转发给浏览器那也是5️⃣分钟。
这几天我才发现5️⃣分钟实属没必要这么频繁,才覆盖了官方的时长,延长到了几天。 在浏览器上强制刷新页面也可以让缓存失效。所以我觉得这不算太大的问题。
其实我 1 个小时前基本看完了你的博客代码,大概清楚你这块的实现了。 --包括当时看到 KaTeX 时饶有兴致地看了看底层是怎么实现的 🫣。-- 如果是为了安全,避免评论者头像地址导致被逆向出 Email 的话,确实你现在这个实现方式还阔以。不过我使用的 FaaS 服务流量就要双倍计费,所以当下还是直接一个 Mirror Site 完事。(逃
哈哈哈😂,这是我近🔟年慢慢从零开始学写前端写来的博客代码,竟然1️⃣个小时就给看!完!了!!
我现在不怕流量了,自从上次被恶意攻击过两次后我就加了监控,有大流量的话,我几分钟内就能知道。VPS 自带的流量我完全用不完,虽然只有 600G,并且我家里全部外网流量都走它。
代码结构还好,我主要看了我关心的几个部分,如文章的渲染解析、评论系统、主题实现,很坦诚地说,代码整体结构并不复杂,很清晰,(比我写的好上太多)所以直接都是一眼过。
不过感觉还多东西,如果用 Node.js 生态,可能比 Golang 写起来更容易一点。
谬赞谬赞🥰🥰!
我一直恐惧:
它们多次因为版本问题使我非常崩溃😇😇😇。
我这个博客程序确实写得简单,也算是我写它的理念吧。复杂的前端咱我也写不来,我力求把安全、备份之类的做好一点吧。
快来和我一起用 Astro。
逃这次该我说逃了……🤪🤪🤪
还能这样?
漏网之鱼🐟!博客是禁止访客写任何 HTML 的,哪知前几天改崩了逻辑🥵🥵🥵……
已经增加了端到端测试,没得玩儿了!
值得学习一下,我也是后期加的 Gravatar 服务,本地化确实是很好的解决方案。
你网站好像打不开了:
偶尔cdn抽风,刷新一下就好了
巧了不是,前天刚写了 正向代理 Gravatar 并生成私有哈希以替代暴露原始哈希的头像链接
然后今天给默认头像做了个 dogface
没测试好就上线了,差点运行不起来🥵
奇怪,三张图片,为什么只有第一张有 lazy loading,后面两张没有呢;以及后两张都有 title,而第一张没有
你真细腻……排查了好一会儿,解决了,墨菲定律还是靠谱🥵。
原因是遍历语法树🌲的时候替换/移除了节点(坏习惯!),导致遍历函数提前退出,未完成全部节点的自定义渲染。
不得不说,这3️⃣个狗子🐶🐶🐶排在一起看起来真的非常优雅……
不是,你这个文章怎么还“藏起来”了呢……竟然在首页都看不到的……🥵
刚才看到 Freya 在你文章里面的回复有了头像,以为是他/她看到了我这篇文章后给自己设了个头像…… 原来是你给弄的默认头像🥵。
你真的很有才……还会画画🧑🎨的……回想我的“关于”,感觉有点羞愧😳。
没事,我都是线上风风火火改BUG的,哈哈哈哈😂。想到刚发生的“淘宝首页证书过期”事件,感觉没有比更草台的班子了。不屑😕。
其实来源是 这个 我只加了一小部分自己画的,然后用 Java 实现了而已。
至于藏起来…因为那是 wiki 😬
那我正好跟你作对🥵……看来我真的要开始写旅行日记了✈️。
其实说实话,我的博客在 Feedly 上我记得有 100+ 订阅,而且肯定是因为技术文章才订阅。我其实有点儿担心写非技术文章污染别人的时间线的🥵。我是不是太……
算了,love me,love my dog,🐶。有时候我就是过于顾及别人的感受而忽略自己了。
如果有了读者基础,那确实需要好好斟酌,是否需要维护相应的关系。幸运又不幸的是:我没有读者基础,所以更能肆无忌惮一些。
幸运洞 ^
好的,宝🥵,学会自爱(literally)。