防敏感词检测 - 应用特殊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的建议,建了个仓库,把网页放了进去。

Github地址新的网页链接

更多可能

前面讲了两种方法,实际上还有更多的方法,只是我还没有实现

这里我说下原理,如果有大佬实现了告诉我

正反转交替法

还是顾名思义,这个方法就是在一行中交替使用正转和反转。要用到一个与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/