Featured image of post 解决 qrz.com 上 Bio 保存不全

解决 qrz.com 上 Bio 保存不全

编辑 qrz.com 简介发现内容莫名被截断,一番排查后原来是 utf8 与 utf8mb4 的字节之争。

恰好准备换卡,在 qrz.com 上整理了一下个人简介。翻了翻编辑页面,发现 Bio 支持 HTML语法,于是写了一份带样式的 Html,加了颜色、字号、间距。保存,刷新,结果页面上只剩开头一截。

第一反应是触发了 qrz 的安全过滤。HTML 里有 style 属性,说不定被当成 XSS 风险砍掉了。于是改成最朴素的 <table><td> 结构,内联样式都去掉,再试一次还是一样,保存后内容依然面目全非,只剩个开头残躯。div 标签被强制闭合,但是手动添加文字进去又能够正常保存。


qrz 的 Bio 并不是直接嵌在页面 HTML 里的,用开发者工具翻了一圈,内容藏在渲染 Bio 简介的 <iframe> 下的 <script> 里,用 Base64 编码塞进去,关键部分长这样:

1
2
3
jQuery('.action-render-').contents().find('#biodata').html(
    Base64.decode("PHRhYmxlIGNlbGx...MjRweDsgZm9udC13ZWlnaHQ6IGJvbGQ7IG1hcmdpbjogMCAwIDVweCAwOyI+")
);

把这段 Base64 解码出来,截断的位置恰好卡在 <p style="color: #0050E6; font-size: 24px; font-weight: bold; margin: 0 0 5px 0;"> 后面原本紧跟着一个Emoji 👋 字符。Emoji 以及之后的所有内容全没了。


这个现象指向一个很经典的历史遗留问题。MySQL 等数据库后端早年的 utf8 字符集是个残缺实现,每个字符最多只存 3 个字节。这对绝大多数文字来说够用,但 Unicode 中的 Emoji 字符需要 4 个字节来编码。一旦数据库列的字符集是 utf8 而非后来补全的 utf8mb4,写入时碰到第一个 4 字节字符,就会直接在那里截断,后面的内容全部丢弃。

qrz.com 是一个运营了很久的老站,底层用的大概率正是这套配置。

把 Bio 里所有 Emoji 删掉,保存,立刻生效。

如果不去翻 Base64 编码的内容,浏览器又自动把截断的标签补上了,大概会一直以为是 HTML 过滤在作怪,然后在哪些标签被允许上兜圈子兜很久。

有时候问题的答案藏得比预期深一点,多翻一层就能看见。

使用 Hugo 构建
主题 StackJimmy 设计