防敏感词检测 - 应用特殊Unicode避开敏感词
本文仅作学习交流目的,禁止用于一切违法用途。
酷安318事件之后,关键词数量猛增,以至于许多本来正常的词句也遭到误屏蔽。为了能够愉快地交流(水帖和开车),遂研究(瞎搞)出了几种防关键词检测的方法,并写了个工具。
间隔法
关键词嘛,它一定是一个词。很容易就能想到的一种方法:把这些词的每个字间隔开。
比如,把ABCD间隔为A.B.C.D。(我就不拿真正的敏感词来演示了)
对于很多关键词,这个方法还是有效果的。但是这样会影响降低文本的阅读体验(逼.死.强.迫.症)
这时就要用特殊的Unicode字符了:
你说上面什么都没有? 这就是我要说的特殊字符——零宽间隔(U+200B)。
顾名思义,这就是一个没有宽度的间隔符,你可以把它看作是不占空间的空格。
它在HTML的转义为​,在JS中用'\u200b'表示。
把它插入到每两个字之间,就可以在不影响阅读体验的情况下避开一部分关键词了。(还有不少关键词检测会防间隔)
写成JS也很简单:
var text; //假设有个text
var result = '';
for (let i in text) {
result += ('\u200b' + text[i]);
}
console.log(result); //输出result
能避开一部分关键词,但这还不够
反转法
反转法,顾名思义,就是把文本从尾到头反过来。比如,把ABCD反转就是DCBA。这个方法能够避开更多的关键词检测。
我猜你现在想的是:谁会用这个垃圾方法,难道要让读者反过来读吗?
当然不是,我们可以用特殊Unicode来解决这个问题——强制从右至左(U+202E)
这也是一个看不见的符号,它的作用是,使那一行内在它之后的文字反转显示,比如:
"一二三四\u202e五六七八\n1234\n\u202eABCD"
//会显示为:
一二三四八七六五
1234 //第二行不受影响
DCBA
有了这个,我们可以先将每行文本反转,再在每行文本开头加上一个 强制从右至左符。由于“负负得正”,所以显示起来是正常的。
比如ABCDEF,就可以写为'\u202eFEDCBA',显示起来和ABCDEF是一样的,完美避开了大多数关键词检测。
真的就完美了吗?
No.如果你让反转后的一行文本一多行显示,就会发现问题:那几行的顺序也会反转。
一个问题
听着有点混乱:怎么又来个反转?,我还是用例子说明这个问题。
假设显示设备的宽度极窄,一行只能显示8个字符,那么它显示'ABCDEFGHIJK'会是这个样子:
ABCDEFGH
IJK在前面加上一个反转符,即'\u202eABCDEFGHIJK'会是这样:
HGFEDCBA
KJI是不是和想象的有点不同。它仍然是每行各自反转,而不是作为一行处理,尽管它没有换行符。
所以,实际使用时,你输入'\u202eKJIHGFEDCBA'。单行显示没问题:ABCDEFGHIJK,而显示为两行就会这样:
EFGHIJK
ABCD所以这并不是个完美的方法,仅适合短点的句子,或者强行把一句话分成很多行。否则屏幕窄的读者就只能从下往上读了。
写成JS也很简单,先将文本分行:
var text; //假设有个text
var rowList = text.split('\n');
然后遍历数组对每行分别处理:
var result = '';
for (let i in rowList) {
let row = rowList[i];
let newRow = '';
for (let j in row) { //反转
newRow = row[j] + newRow;
}
//再加上反转符和换行符
newRow = '\u202e' + newRow + '\n';
result += newRow;
}
console.log(result); //输出result
这样就完了吗?
其实还有个问题我差点忘记了
另一个问题
这个问题就是,用\u202e反转时,后面的括号(包括大、中、小等)也会进行对称变换,比如'\u202eAB(CD)'会反转为(DC)BA,本身没有问题,但是:
for (let j in row) { //反转
newRow = row[j] + newRow;
}
上面这个它不会对括号进行变换。
以至于运行上面的代码,输入'AB(CD)'会输出AB)CD(。
所以还要把上面的代码加上个左右括号互换的功能。涉及到的括号有点多,已知有中英文的大中小左右括号和大于小于号还有书名引号。(中文的左右引号似乎没有问题)
最终代码如下:
var text; //假设有个text
var rowList = text.split('\n');
var result = '';
for (let i in rowList) {
let row = rowList[i];
let newRow = '';
for (let j in row) { //反转
let y = row[j]
newRow =
//三元表达式应该不用说了吧
((y == '(') ? ')' :
(y == ')') ? '(' :
(y == '(') ? ')' :
(y == ')') ? '(' :
(y == '{') ? '}' :
(y == '}') ? '{' :
(y == '《') ? '》' :
(y == '》') ? '《' :
(y == '<') ? '>' :
(y == '>') ? '<' :
(y == '【') ? '】' :
(y == '】') ? '【' :
y) +
newRow;
}
//再加上反转符和换行符
newRow = '\u202e' + newRow + '\n';
result += newRow;
}
console.log(result); //输出result
至此,反转法有两个问题,解决了一个,另一个暂时无解
最后
有了这两个防检测方法,就可以封装一下,写个网页工具。其实我是先做的网页,再写的这篇文章
还是放上那个网页的链接
此外,那个网页工具还有个“避开中括号”的功能,用于兼容酷安的表情,这里我就不讲了。
4月16日更新
应酷友@ImBrighter2006的建议,建了个仓库,把网页放了进去。
更多可能
前面讲了两种方法,实际上还有更多的方法,只是我还没有实现
这里我说下原理,如果有大佬实现了告诉我
正反转交替法
还是顾名思义,这个方法就是在一行中交替使用正转和反转。要用到一个与U+202E对应的Unicode——强制从左至右符(U+202D)
它会将在它之后的文字强制从左至右显示(即正序)直到换行符或\u202e。这个要和\u202e一起用才能看到效果,还是举个例子:
'ABCD\u202eEFGH\u202dIJKL' 它会怎样显示?我赌一个酷币你猜不对
好吧,实际上,它会这样显示:
ABCDIJKLHGFE这是因为,\u202e反转的本质是让字符从右到左排列。反转的字符始终会在正序的字符的右边。
每行的排列过程是这样的:所有正序的字符加在一起,从左至右排列;所有反转的字符加在一起,从右至左排列。当一行放不下时,正反转的内容都会有一部分溢出到第二行,并在第二行做上述同样的排列。
运用这个特性,可以实现更多的文本顺序的变换。也可以实现更强的防检测效果,
比如,下面这个字符串:
'\u202eHG\u202dAB\u202eFE\u202dCD' 看似混乱无序,但实际显示为:
ABCDEFGH不过这个方法同时也存在和反转法相同 甚至更糟糕的问题:当一行文本(即没有换行符)以多行的形式显示时,顺序会混乱。
由于这个方法并没有解决反转法的问题,并且更为复杂,我就没有在那个网页工具中实现。实现起来应该也不是难事。
暂时写到这里吧,如果我再想到了别的好方法还会更新的。
下周就开学了,要等到中考之后才能真正有空了。
此博客所有文章默认采用署名—非商业性使用—相同方式共享 4.0 协议进行许可
本文链接:https://blog.texice.xyz/2020/Anti-Text-Detect/