目录
前文
通过自定义的字体实现字体加密的场景大家应该不陌生了吧。在反爬虫如影集评分、电商产品价格,反复制如文库等场景中可以见到。如下图示我的一个测试示例,在设定了特定字体后,原文中的
褙矹癦蓗僻燵嗵褙糬倃荗拲褦呉
在字体渲染后,显示却是
体跟上面几个字体不同的地方在
这个问题在第一次看到的时候就很感兴趣了,直到这个新年才开始从代码出发去研究一番。那么这是怎么做到的,而我有遇到哪些问题,今天和大家分享下。
原理
简单的说就是,修改字体文件中的字符代码(unicode)到字形索引的映射关系。比如:“体”的unicode 码为\u4e0a,而“褙”的unicode 码为\u7a14,在字体文件中“体”的字形对应的unicode 码 \u4e0a 修改为 \u7a14,那么后续在渲染字体字形时,“褙”只能印出“体”了。
所以思路就是:
- 解析字体
- 修改字体
- 提取子集
- 修改映射
- …
- 打包字体
- 转化格式
自己造轮子是不现实的,翻翻有什么可以用的吧。
尝试的几个工具
虽然已经知道在js中有font-spider类似工具,但还是想在php中尝试下,所以找到了 php-font-lib。
php-font-lib
php-font-lib是dompdf中用于替换字体的类库,也有打包子集的封装。
$tmp=tempnam(sys_get_temp_dir(), $strFontFamily);
$fontFile=\FontLib\Font::load($arrFont["path"]);
$fontFile->parse();
$fontFile->setSubset($strFontFamily);
$fontFile->reduce();
$fontFile->open($tmp, FontLib\BinaryStream::modeWrite);
$fontFile->encode(array("OS/2"));
$fontFile->close();
但是在打包后的字体文件可以在电脑中预览,却在浏览器中并不可用。见Chrome & Firefox errors when loading generated subset TTF fonts, for all fonts
我遇到的问题
-
OTS parsing error: hhea: misaligned table
在上面的issue中找到以下信息,问题就是在子表封装时偏移没有对齐。
https://searchcode.com/codesearch/view/26727670/
Line 460
// tables must be 4-byte aligned
if (tables[i].offset & 3) {
return OTS_FAILURE_MSG_TAG(“misaligned table”, &tables[i].tag);
}
-
OTS parsing error: camp: failed to parse table
通过在表结尾用 "\0"补齐后获得以上错误。到此放弃了 php-font-lib
php-font-lib-demo
php-font-lib-demo.zip,没有在说明里,在issue中找到的,有需要的话可以下载去看看。
方便查看字体信息和生成子集(也不建议了 见补充)
fontkit
PDFKit的字体引擎,但很巧和php-font-lib的错误是一样的。跳过
OTS parsing error: hhea: misaligned table
fontmin 系
font-spider、fontmin、fonteditor-core、fonteditor 的关系
- font-spider
主要用于生成页面字体子集,字体处理基于fontmin。
- fontmin
主要用于压缩字体、子集提取和格式转化,基于fonteditor-core封装
- fonteditor-core
字体文件的读取和打包,要研究字体的格式可以看这个项目
- fonteditor
基于fonteditor-core的字体在线编辑工具,
fontmin 提取子集
以下代码会将 fonts目录下的*.ttf 提取"天地玄黄 宇宙洪荒"为字体子集,存放在build/fonts目录,并生成成woff2格式。
var Fontmin = require('fontmin');
var fontmin = new Fontmin()
.use(Fontmin.glyph({
text: '天地玄黄 宇宙洪荒',
hinting: false
}))
.use(Fontmin.ttf2woff2())
.src('fonts/*.ttf')
.dest('build/fonts')
;
fonttools
Python的一个库,本人未作尝试,但按照这几个项目的活跃度,重来的话我会选择这个项目。
“加密”字体实践
demo 页面
字体加密demo
源码
整理中……
“加密”字体分析
用途
- 网页加密、内容混淆
- 网页内容防拷贝
- 网站反爬虫
- SEO(搜索引擎优化)
- 敏感词过滤
- 简易验证码
其中的敏感词过滤个人感觉不太行,叫做敏感字过滤吧。
破解
- 反推映射
这个方法在网上还是毕竟多的,其中主要的难点是字型识别。
- 静态的加密字体
大部分都是打开字体识别工具手动列出映射关系,然而这只在字体内容数量少且静态的加密字体才生效。
- 动态识别字体
如果加密原字体为已知字体,一一对比字型数据就可以进行反推。如果是未知字体,除了找一个字形对比算法(我不知道有没有)外也只能用下一个方法了。
- ORC
这属于降维打击,字体加密只是掩耳盗铃。
个人思考
当我在思考如果动态识别字型时,突然好奇一个事,手写输入法是怎么做到的呢?我强烈怀疑 字形对比算法、手写识别和ORC中的识别部分是不是一样的。有空再去学习研究研究。
总结
字体加密虽然不是万无一失的方法,但可以增加攻击者门槛。毕竟一丁点门槛就可以过滤到大部分攻击者。当然这取决于数据的价值,当获取的价值远不如获取过程付出的代价,自然可以劝退攻击者。
补充
字体必要的 table
cmap:字符代码到字形索引之间的映射关系,字符代码也就是字符的 Unicode,获得索引也就可以根据索引从字体中加载这个字形。
head:字体的各种基本信息,如版本、创建、修改时间,还包括基本字体数据,如 unitsPerEm、xMin, yMin 等。
hhea:水平排列信息,如 ascender、descender、lineGap 等水平排列时候的布局信息。
hmtx:水平参数,如间距,如果是字形之间是等距的,那只需要一个间距就可以了。
maxp:最大需求表,包含字形数量,表示字形的内存需求情况。
name:命名内容,如字体名,授权信息等等。
post:PostScript 表,用于打印。
glyf:字形数据,也是最重要的一个表了。
loca:偏移和字符索引映射关系表。
2022年2月17日
今天再去翻了php-font-lib的cmap表的处理,对比了下fonteditor-core中cmap部分,两者差距还是比较大的。php-font-lib过于简化了。并且,对于其他表的解析上,php-font-lib会缺少部分信息,同样的问题也出现在打包上。
2024年2月16日
php-font-lib 生成字体规范性还未完成 Improve TrueType spec compliance for generated font #123
字体版权问题
如果使用字体加密,字体版权应该是绕不过去的问题。即便是免费商用字体,提取子集修改映射,甚至抹去字体信息,这在法律上该如何界定,又是一个复杂的问题。
例如:免费商用字体《站酷庆科黄油体》的使用声明
★【站酷庆科黄油体】版权归站酷(ZCOOL)所有,全体造字者保留署名权,【站酷高端黑】将永久免费授权所有人使用(可免费商用)。
禁止在任何场景下改变站酷免费字体的原有名称,禁止任何人将站酷免费字体作为商品进行转售及牟利。
★版本信息:Version 3.000 2018年3月最新版
应用和部分工具页面
谷雨解字 在线压缩、加密字体
transfonter 字体转换工具
fonteditor 在线编辑字体,可以修改字形
相关项目
fontmin
fonteditor-core
fonteditor
fontkit
php-font-lib
fonttools
参考
Web 中文字体性能优化实践
可变字体探索与 require 扫盲记