Web 应用程序词典中出现了一个新术语:“玩弄 (gaming)”。在此上下文中,玩弄 并不是指人们玩在线游戏。而是指有人将您的网站看作猎物,尝试对其施加影响。

最明显的一个可能发生玩弄的例子就是在线投票,例如,某人尝试通过新建帐户或其他玩弄手段 一个人进行多次投票,从而使自己的候选人排名领先。不过,这一概念适用于用户可能希望操纵网 站为自己谋利的任何情形。

为何会有人这样做?针对一篇文章快速发表大量评论就是其中一种情况,其目的是使页面充满胡 乱的评论,从而让人无从阅读真正的评论。他们可能频繁登录/注销以增加登录次数,或者不断重载 自己的页面,以使这些页面显得更受欢迎。最糟的情况是,有的人可能就是忌恨您的网站,希望让 网站的数据变得无效。无论如何,这总是您不希望暴露的漏洞。

在本文中,我将介绍一些有助于防止网站被玩弄的方法。在这些方法中,有些是靠关闭脚本,有 些则尝试找出手动执行此行为的人。

早期的防垃圾信息方法

在我的文章“打击垃圾信 息,构建更健康的网站”中,介绍了一些确保用户评论有效的方法。出于同一目的,这些 方法也可用作防玩弄措施。因为替代方法(如创建并登录多个帐户)冗长乏味,所以大多数玩弄系 统的尝试都是自动进行的。如果设置的障碍使脚本无效,或者至少给编写增加些难度,都可能使人 们放弃编写脚本。

限制速度

还有一条非常有效的防玩弄措施:限制操作执行的速度。通常,如果有人编写脚本在您的网站上 执行操作,他们不会考虑让它像人一样去“行为”。因此,这个脚本一般会向您密集提 出请求。对于完全用 JavaScript 编写以自动执行浏览器操作的 GreaseMonkey 脚本,尤为如此。

在这种情况下,只需跟踪操作发生的时间,并手动测试人做出明智的决定并单击相关链接的速度 。然后设置一个阈值,这样,如果投票的速度高于人工可能的投票速度,就知道这些投票是脚本生 成的。然后,您可以不计所有这些投票的有效性。

例如,您在网站的某页中列出多项(如电影),让用户按照 1 至 5 五个等级对每一项进行打分 。在 MySQL 中,此 “电影打分”表的原始模式可能类似如下所示:

CREATE TABLE `ratings` ( 
`id`         INTEGER UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
`user_id`    INTEGER UNSIGNED NOT NULL,
`movie_id`   INTEGER UNSIGNED NOT NULL,
`rating`     TINYINT UNSIGNED NOT NULL
) ENGINE=innodb DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

CREATE UNIQUE INDEX `user_movie` ON `ratings`(`user_id`, `movie_id`);


有人可能会编写一个脚本,试图给每项打“1”分。为帮助对电影打分应用速 度限制,应修改此表以存储一些额外的数据:

ALTER TABLE `ratings`
   ADD COLUMN `invalid` TINYINT UNSIGNED NOT NULL DEFAULT 0,
   ADD COLUMN `created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;


现在我们可以跟踪链接何时被单击,并且有办法将打分标记为无效。完成之后,假定我 们运行某些手动测试,发现一个人最快的速度是 5 秒钟内单击 5 个链接。考虑到有些人的单击速 度可能比您要快一点,我们假定 4 秒钟内单击 5 个链接。现在,在添加新的电影打分之前,可以 运行以下 SQL 来检测是否超过单击速度限制:

SELECT `id` FROM `ratings`
WHERE `user_id` = 42
   AND `created` > DATE_SUB(NOW(), interval 4 second);


此代码将返回过去 4 秒钟内产生的所有条目。如果有 4 项或更多项(因为要添加的是 第 5 项),那么在插入当前的电影打分行时,需要设置无效字段。还需要回头将原来的所有打分设 为无效。

检查来源

您可以在调查中开始使用的另一项信息是操作源。查看所有可用信息以跟踪是何人在执行操作。

最简单的办法显然是查看特定用户。如果像上例中那样已经在跟踪登录用户,则更容易做到这一 点。如果不要求使用登录,则可以通过在每个 DB 行中存储会话 ID 来跟踪操作。这样,您就可以 跟踪在网站中移动并执行不同操作的单个用户。

在按用户跟踪信息时可使用的最大指示器之一就是查看总体活动。例如,如果许多用户在系统中 留下的历史记录很少,不是新创建的,就是仅对一部电影打过分。. . 这一点可能值得怀疑。可能 有人创建了大量帐户,以尝试提升自己电影的分数。对此有一个较好的解决办法,就是在整体方案 中将新用户的投票数进行打折。

对于电影打分系统,这样做的一个简单版本就是在对一部电影的总体平均打分中,排除打分总数 少于 5 次的用户的所有打分。以下是执行此操作的 SQL 语句(警告:不要对大型数据库执行此操 作,那需要重写):

SELECT AVG(`rating`) FROM `ratings` 
WHERE `movie_id` = 1 
    AND `user_id` IN
        (SELECT `user_id` FROM `ratings` 
         GROUP BY `user_id` HAVING count(`id`) >= 5)


当然,跟踪执行操作的用户也只能帮您到这一步,因为玩弄网站的人可相当狡猾,他们 可能会料到这一点。为此,同时启动对任何操作的 IP 地址的跟踪和对该操作的 HTTP Referrer 的 跟踪可能很有帮助。这些都可以为分析操作提供不同的视角。首先来看看 IP 地址。

查看 IP 地址时,主要是找出单个地址活动次数不同寻常的情况,通常这种情况下使用了多个用 户帐户。您要尝试发现某个人是否使用了大量帐户、登录/注销每个帐户并试图通过这些帐户影响投 票。这种情况确实需要谨慎。公司员工或学校里的学生都可能看上去使用同一 IP 地址。当然,这 本身也是一项有意义的统计信息。如果有大量人员从单个 IP 地址(或 IP 地址范围)对某个特定 对象进行投票,您可能遇到了串通的情况。(在电影打分的情况中,也许是电影公司要求员工将自 己的电影评为 5 星。这不应被视为有效数据。)

跟踪操作的 HTTP referrer 的情况与此类似。如果知道是哪个原始 HTTP 地址将它们带到您的 网站,您还可以找到串通的来源。也许它只是某个公共网页,上面写着:“大家都去为这个投 一票”。尽管其目的可能是单纯的,但通常这种情况会导致盲目投票,而不是真实、诚实的输 入。应注意,要想正确地做到这一点,您需要跟踪的不仅仅是“最后一个”HTTP referrer,因为它通常就是您自己的网站(或者在某些 AJAX 情况下,它甚至为空白)。而是需要 在用户第一次访问来到您网站的时候,将 referrer 作为会话的一部分捕获。然后将其与所有操作 记录在一起。

可以考虑将此数据添加到电影打分级数据库,如下所示:

ALTER TABLE `ratings`
    ADD COLUMN `ip_addr` INTEGER UNSIGNED NULL,
    ADD COLUMN `refer` VARCHAR(1048) NULL;


下面您需要考虑的一个问题有点复杂。您将注意到,我将“refer”字段定义 为一个 1,048 字符的 VARCHAR。在此,您需要为应用程序做出某些重大牺牲。URL 有时可能长达 2,000 个字符。如果您的 VARCHAR 小于上面所列出的长度,您将丢失有可能对您非常重要的数据。 尽管在另一方面,对每个打分都记录所有这些信息可能会使数据库变得很大,这很痛苦。

一种解决办法是只记录 HTTP referrer 的 MD5 或 SHA1 散列值。这样,您仍然可以通过编程方 式检测是否有串通发生,同时又可以将字段限制为 32(或 40)个字符,从而大大减轻您的痛苦。 这么做,您将无法实际查看原始 URL,也无法为了满足好奇心而访问原始 URL。如果您仍然希望得 到该信息,也可以建立一个单独的查询表,将完整 URL 与其关联的散列值一起(或者在本例中,只 使用一个自动递增键)推入查询表中。但这样做的结果,还是增加了额外的数据库开销和维护工作 量。这些就是您尝试跟踪尽可能多的信息以击退网站玩弄时始终要做出的牺牲。

模式匹配

至此,我们已经详尽讲述了简单的基于“来源”和“它是否像脚本”来找 出玩弄系统者的方法。如果您希望了解更深奥的情形,则需要深入到模式匹配的层次。

在这个例子中,我们将在数据库中查找可疑模式。要找的较简单的模式是操作的计时和排序。例 如,在我们的电影打分系统中:您能否看出有 50 个相同的帐户以完全相同的顺序、同样的评分对 多部电影进行投票?如果是这样,这就是一种在正常网站使用中不会存在的模式,因此,您可以假 定要么是有一个脚本在执行这些操作,要么是有人在按照设置的帐户的电子表格依次登录每个帐户 。

类似地,可以查看操作的计时。前面我们讲过速度限制,但现在可以只看可疑计时。要是恰好有 一个帐户每隔 10 分钟准确地进行一次打分,会怎么样?或者,如果有人每隔 1 分钟随机从一组帐 户中选择一个留下评论,又会怎么样?类似这样的令人感兴趣的计时模式能够引导您发现可疑活动 。

在您的网站中,这些分析任务都不会实时发生。这些任务可能是代价相当高的查询,需要离线处 理,可以作为手动流程由搜索此活动的管理员来进行,也可以通过自动流程在夜间进行,对前一天 发现的任何可疑项进行标记。

友情

串通(无论是有意的还是无意的)的最大来源之一就是朋友关系。然而,如果您的网站有社交功 能(正如当前许多网站那样),您将拥有一个巨大的数据源来识别这一点。这些网站中的大多数都 支持帐户之间“交友”的概念。在这种情况下,您可以建立友情树来显示谁与谁有关联 。您可以着手检查整课树,看看是否出现模式。一个朋友圈内的人可能全部做出完全相同的行为, 对某部电影给出相同的评分。

表面上来看,其中某些显然会发生。真正的朋友会分享类似的兴趣,因此喜欢类似的事物。但这 有一个限度。在某些点,就会超越“常规”的类似品味,而进入串通的领域。如果一个 朋友圈中的人总是执行完全相同的操作,您将不会从他们之中得到有效的数据。

您可以进一步扩展这种模式。许多网站现在允许您链接自己的 Twitter、Facebook 或其他社交 媒体网站帐户。可能是作为一种登录的身份验证方法,也可能只是作为一种“找朋友” 的方式。如果这样做,您就有能力执行一些以前做不到的深入分析。(警告:您在此处开始跨越白 帽和黑帽之间的界线。)

例如,您可以跟踪网站的哪些帐户正好链接到 Twitter 帐户。可以调用 Twitter 的 API 获取 您每个用户的朋友列表。此时,您可以构建自己网站上可能未作为朋友链接但根据 Twitter 实际上 相关的帐户之间的关系图。这样一来,您可以将各种联系拼在一起,而这些联系您本来是看不见的 ,要么是因为对用户不怎么关心,要么就是用户有意向您隐藏串通企图并认为不在您的网站上将帐 户链接为朋友将隐藏他们之间的关系。

这会成为一种永无止境的数据挖掘源,您可以深入再深入地挖掘各种关系和操作,尝试找出原本 对您隐藏的模式。

设陷阱

深入讨论模式主题并讨论了如何在玩弄发生之后进行检测,现在我们来讲最后一步,这一步可能 稍轻松些,它绝对有效。

这就是设陷阱的概念,在刚发生的时候抓住玩弄者。这通常使用 cookie 来完成。这里,cookie 有两个特定用途,可以帮助我们跟踪和检测企图玩弄系统的人。

第一种用途我称之为“登录 cookie”。其思路是在浏览器访问网站时设置一个 cookie。以后无论用户登录或注销多少次,您都在原地保留此 cookie。(这不同于会话 ID,会话 ID 应在每次访问后重新生成。)然后您就可以在数据库查询表中保存帐户名和登录 cookie 值。这 直接允许您跟踪使用多个帐户登录的单个人(单个浏览器)。这可以与前面提到的 IP 地址跟踪结 合,从而可以更深入地窥视动态 IP 地址和网关背后。

cookie 的第二种用途本质上与此类似,但有根本区别,它就是帐户 cookie。在这种情况下,是 要在一个人实际创建多个帐户时进行跟踪。在执行中,其效果与前面类似。当浏览器首次访问您的 网站时,您将在帐户 cookie 中返回一个唯一标识符。不过在此情况下,会将帐户创建时的值记录 为帐户信息的一部分。巧妙之处在于无论用户何时登录,您都会重置 cookie 以便与原始帐户的 cookie 值匹配。

如果该用户马上(或在未来的任何时刻)通过注销再单击“添加帐户”来尝试创建其 他帐户,您都可以跟踪到它实际上是同一用户。

以此方式,可以将实际上是单个用户的帐户组链接在一起,并相应地根据该信息来操作。同样, 这也需要谨慎对待。如果只有几个帐户链接在一起,这种情况可能是一个家庭,每个人有一个帐户 。但当链接的帐户数上升时,这些帐户就比较值得怀疑了。

总结

希望您永远不会遇到玩弄的问题。遗憾的是,世界并不总是那么美好。检测玩弄从来就不是一件 轻松的事。您总是挣扎于尝试找出明目张胆的玩弄,同时又希望不是在错误地产生误报。本文所给 出的一套方法只是一个起点。您可以接受这些概念,在自己的应用程序中加以应用,然后检查关于 用户能不能有什么发现。