再次说说Blog的人身安全

上次写了一篇文章叫“Blog的人身安全”。今天月光博客又发表了一篇文章,提到了Trakcback的问题。今天就再次来谈谈我在反SPAM上的一些小招数。

1.首先要做到的,也是最简单能做到的,就是将Trackback地址复杂化。
回顾今年3月份的Donews Trackback SPAM事件,正是因为Donews的Trackback地址采用了连续的ID作为Trackback的URL,所以才会使系统变得易被攻击。(话说Donews现在的TB URL貌似依然是连续ID,比如:http://tb.donews.net/TrackBack.aspx?PostId=930641)
一个方法是在TB URL中加入验证字串,比如tb.php?id=123&auth=78q34f,验证字串的算法可以使用标题或者文章内容的MD5 Hash中的一部分来充当。然而,这样增加了URL的长度和本来不应该有的信息量,似乎并不是最好的办法。那么,我们为什么不连ID一起加密掉呢?
我的做法是采用一个可逆加密算法(目前Waki-Blog使用的是我自己写的z-Enc),加密URL中的ID,Trackback程序收到字符串之后,解密、验证,若合格则继续,不合格则拒绝。同时起到了提高URL复杂度、隐藏ID、验证提交的作用。我的Blog中的TB URL中类似“response.php?tb::0a2MTc%3D”的部分,就是z-Enc编码的结果。
z-Enc的详细说明和源代码都可以在上面的链接中找到,希望能给Blog程序的作者们提供一丁点思路。

2.进行敏感关键词过滤。
为了进一步防止通过人工方式对某个Blog(甚至某个Blog系统)进行某些有“特殊目的”的SPAM,需要对Trackback以及留言评论的内容进行过滤。具体到程序上,就是检查接收到的信息中是否有规定的词汇。再细分,则是检查敏感词汇出现的次数是否超过了规定的次数(即敏感度,或者说宽容度)。如果出现违规的词汇,则予以拒绝。
为什么要采用一个“宽容度”设定呢?请看下面两句话:“Google Earth是Google提供的免费地图服务”、“免费激情小电影 观看免费电影”。同样是“免费”这个词语,出现在正常的句子当中和出现在广告SPAM中的频率是不同的。经过观察,在正常句子中“免费”出现的频率较小,而出现在广告中的时候则很可能大量出现。若没有宽容度,只要出现“免费”就一律杀掉,未免错杀无辜;若不过滤,则很可能漏掉广告。
下面是Waki-Blog的过滤代码:
function bad_word_detect($str){
include_once("./include/badword.php");
//英语垃圾词检查
$message_for_check_en = str_word_count(strtolower(preg_replace('~[^A-Z ]~i', '', $str)),1);
if(count(array_intersect($message_for_check_en,$bad_word_en)) > 0) return true;

//中文垃圾词检查
$message_for_check_cn = str_replace($bad_sign,"",preg_replace('~[A-Za-z0-9 ]~i', '', $str));
for ($i=0;$i<count($bad_word_cn);$i++){
if (substr_count($message_for_check_cn, $bad_word_cn[$i][0]) >= $bad_word_cn[$i][1]) {
return true;
}
}
}
Waki-Blog分别对英文和非英语敏感词汇进行过滤。敏感词汇列表储存在badword.php中,可以随时扩充。
(1)英文关键字过滤:
首先使用正则表达式过滤掉除大小写英文字母外的全部字符。之后使用PHP内建函数strtolower将全部字母转换为小写(防止使用类似pORn的乱序大小写来蒙混过关)。之后用str_word_count来分词,全部词语转换为数组$message_for_check_en,之后用array_intersect求其与$bad_word_en数组的交集,若存在交集,则判断内容中包含敏感词语,函数返回true。
(2)中文关键词过滤:
比起英文关键词过滤,因为中文关键词过滤引入了宽容度问题,所以要稍微复杂一些。
首先将所有的英文字母、0~9的阿拉伯数字、英文符号、中文符号都过滤掉组成纯非英语字符串$message_for_check_cn。之后进入循环,逐个检查每个敏感关键词在字符串中的含量。若发现超过宽容度的出现次数,则立即返回true中止循环。
这种方法虽然有些花费系统资源,但是过滤效果和灵活度方面都好一些。而且,这种方式的应用不仅限于此,如果将某些经常发广告的网站的网址列入敏感字符列表中,将容忍度设置为1(只要出现一次就block),那么敏感字符列表便充当了黑名单的作用。
当然,这种过滤方式当然也存在一些不足。比如无法防止汉字同音字代替,偏旁部首拼字等等的比较极端的办法,但防护一般的SPAM还是足够的。因为语言天生的复杂性,除非使用机器分词、语法语义分析、统计学分析过滤等等的技术,SPAM是没办法从根本上过滤的。然而遗憾的是,目前这些技术本身就不是很成熟,应用在SPAM过滤上就更有难度了。

越高级复杂的过滤与防护只能催生出来更加变本加厉的SPAM办法。在想办法不断对付没完没了的SPAM的同时,为什么不倡导响应中央“文明办网”的号召,让广大网站的发展都“文明”起来,不要SPAM呢?

Waki-Blog继续升级

最近对Waki-Blog进行了一些改动。
首先是6月24日增加了Blog的评论功能,主要有以下几个方面:
1.敏感词汇过滤。可以分别指定中、英文关键词,及其宽容度。关键词出现等于或超过宽容度所限制的次数即视为垃圾留言不予接受。
2.记录留言者IP。
3.支持记忆个人信息功能,可选在COOKIE中记录自己的名字、E-mail和URL。
4.留言不解析UBB代码和HTML代码。
5.若留言人留下了网站地址,现实是对其添加nofollow属性,使搜索引擎(主要是Google、MSN、Yahoo!)不对其进行追踪,防止广告目的的链接。

6月25日:
1.增加了Blog的阅读统计,可以统计某篇Blog的阅读次数。并在首页、存档页和Blog浏览页显示浏览次数。
2.是为了加快程序执行速度,首页和存档页只显示文章摘要,并去掉首页和存档页的UBB代码解析,需要进入浏览业才能全文浏览。
3.侧边拦更加细化,将导航栏与订阅栏分开。增加“最新评论”栏,现时最近的10条评论。

6月26日:
1.在评论显示时,取消发表人IP显示。
2.E-mail设定为可选项目。
3.对E-mail地址进行替换,替换掉@和.,防止E-mail地址被收集。

Blog的“人身安全”

今天晚上闲着没事,又修改了一下Waki-Blog。本次修改主旨在于提高前台程序的执行效率。首先分离了前台和后台的语言包文件,因为Waki-Blog的前台远比后台来的简单,所以需要的语言包的数量比较少,为前台专门做一个语言包可以加快前台的程序加载速度。由于Waki-Blog程序结构的原因,PHP代码与语言、模板完全分离(PHP代码中看不到一个字、一点HTML代码,当然,BBCDOE解析除外),所以程序更加依赖于语言包和模板,要提高效率,只要从语言包和模板入手即可。
此外还调整了用户身份验证部分。只在后台的时候才验证用户身份,这样就在前台减少了一次数据库查询。

今天启动了Waki-Blog的图片防盗链功能。
Waki-Blog的图片管理依赖于站内图库功能。在编辑器中可以上传图片,并指定一个该图片的索引。比如我给某副图片取名叫“feiron”(《天高く、雲は流れ》男主角的名字-0-),就可以在编辑器内用BBCODE贴上[image|feiron],BBCDOE解析的时候,就会插入索引为feiron的图片,与wiki原理相似。不仅如此,索引在BBCODE解析的时候,会通过加密的方式传递给程序,这样,图片地址就变成了类似response.php?image:show::a44c2FpdG91的形式,隐藏了图片的真实路径和文件名,达到了保护图片的目的。
当response.php接到请求的时候,会首先检查1.加密了的索引是否合法、2.检查请求来源是否在指定的白名单之内。如果没有问题,就会解码图片索引、查询数据库找到图片的真实路径、由GD库打开图片、PHP输出HTTP头、之后同样通过GD库来将图片呈现。任何一项不符合要求,都会拒绝请求。

这只是防盗链的第一步,也是最基本的一步,即试图从本站做起防止他人盗链。但是,来源检查并不一定经常有效,遇上某些丧心病狂的人通过伪造HTTP头的方式来认为改变请求来源的时候,只有在图片上加入含有本站标志的水印来标明图片来源。这只能说是属于下下策的方式了,因为在图片上添加水印,不能直接起到防止盗链的作用,只能是一种类似“舆论施压”的作用。
过两天我会在Blog上贴一篇关于PHP图片水印添加的心得的文章,届时会公开Z-AutoWartermark 2.0的代码。

Waki-Blog诞生!

按照我写程序的习惯,程序都是以z开头的词命名的。这个习惯最早要追溯到zArticle时期。因为我的第一个网站叫“足迹工作室”,我写文章系统取名zArticle是要纪念这个网站的。写到最后写着写着就变成了习惯,其后写的比较大型的文档系统zDoc、加密/解密算法zQueryEncode/Decode、图片自适应水印添加程序z-AutoWaterMark、验证码程序z-Captcha,同样都以z开头。
但是事情到了Blog程序这里似乎就不太行得通了。因为已经有一个ASP的Blog系统叫Z-Blog了,虽然人家没说别的程序不许叫Z-Blog,但是我还是自觉自愿地躲开了这个名字。
至于为什么要叫Waki-Blog呢?Waki这个名字来源于日语中的汉语词“和気(わき)”,意为“和气、和蔼、和睦的气氛”,正好和我的性格相符。感谢jOSH.yU帮忙想了一个这么好的名字。

因为开发的时候是以z-Blog为名字进行开发的,所以要将所有程序中的zblog的痕迹抹去还需要一段时间。今天只替换掉了前台的zblog,因为后台程序和模板数量众多,只能等以后一边维护一边改了。

今天的更新主要有:

  • 增强了防止SPAM的功能。不但使用z-QueryEncode加密TrackBack查询字符串杜绝连续ID攻击,还设置了TrackBack限额,即每300秒系统只接受一条TrackBack请求。这样就算有人SPAM,24小时不断地发,每天也只能发288条。而删除这些TrackBack也只是一条SQL命令就能解决的事情。不过设置限额也有缺陷……那就是有些正常的请求不能响应了。
  • 纠正了生成存档列表缓存时的BUG。当存在较老日期、ID却比较新的BLOG的时候,缓存程序会没办法生成缓存,导致首页、存档列表的SQL语句出现错误。虽然这个BUG一般情况下不会显现,但是今天我在把老BLOG的文章导入进来的时候却遇到了这个问题。顺便修正。
  • 修改了首页、存档页、RSS生成中文章排序的方法,由依据ID排序改为依据时间排序。
  • 增加了在写新Blog、修改Blog的时候更改发布时间的功能。上面两处修改就是为了适应这个功能而进行的。

z-Blog 主体工程全部完工

经过又一下午的努力,z-Blog主体功能全部完工。
今天新加入的功能包括:

  • TrackBack引用报告发送与接收
  • RSS输出
  • 存档功能

修正昨天的BUG:

  • 时间戳错误
  • 首页模板中的一处错误

两天的时间就赶完了一个程序,成就感还是蛮高的~HOHO~之后就剩下一些后续的DEBUG工作与防SPAM的加强了。因为z-Blog根本没有留言和评论功能,所以重点在防止TrackBack被攻击上。

自己的Blog程序 第一篇Blog

新网互联虚拟主机的MySql竟然只有3.23.54,装不了Bo-Blog2。于是最终下定决心自己写一个Blog程序。
正好前些日子看到了Google黑板报,于是决定以最简约的风格写一个Blog程序,只实现最基本的功能即可。编写过程是从今天上午10:00开始的,首先确定好数据库结构,最开始计划的时候是两个表,分别存储用户和Blog,到下午的时候又加了一个管理附件的表。专门为用户建立一个表是为了实现多人撰写Blog,个人觉得这个功能还是很必要的。好不容易把数据库结构确定下来了,却犯懒看起了小说,这一看就是两个小时,吃过午饭,喝了杯咖啡,战胜睡意继续写程序。
下午的进展比较顺利,因为有zDoc的基础,模板引擎、基础程序结构等等的基本全是现成的,基本上也就是改改模板、改改程序里的数据库查询就OK,bbcode解析部分也是用zDoc里面现成的,基本不用改就能移植过来,下午到晚上7:30左右就完成了前台、后台的基本功能。
现在还剩下的是一些Blog特有的功能,比如:

  • TrackBack的发送和接收
  • RSS输出
  • 存档

这些是之后的事情了,今天先完成基础部分即可 🙂