浅析 天涯论坛 回复验证策略

对于现在 POST 技术满天飞的时代,防机器人确实是很头疼的一件事情,类似流量精灵这样的东西,他可以做到 100%
的真实信息,大批量的访问。
当然今天不谈这些,只是分析下 天涯论坛 回复时的验证策略。

昨天谈到 packer 压缩,今天我们来看个实例吧。
http://bbs.tianya.cn/m/reply.jsp?item=funinfo&id=4339425
这个是天涯论坛手机端的回复帖子页面,里面有一个关于回复验证的js,就是用的
packer压缩。
http://static.tianyaui.com/global/ty/util/TY.util.userAction.js?v=201404111018

真心不知道他们是怎么想的,1秒还原大法。。。


jQuery(function() {
function i(B) {
var D, l, n, C = document.cookie.substr(document.cookie.indexOf("&id=") + "&id=".length);
C = C.substr(0, C.indexOf("&")), C = "" == C ? 8980291 : C, D = jQuery(B.target), l = (new Date).getTime(), n = "focusout" == B.type ? "blur" : "focusin" == B.type ? "focus" : B.type, n = n.replace("key", "").substring(0, 1), 0 == c && (c = l), d >= f && (d = 2), a["c" == n ? 1 : "f" == n ? 0 : d++] = n + e+++"." + ("c" == n ? B.pageX + ":" + B.pageY : "b" == n ? d : "f" == n ? c : B.which) + "." + (l - ("c" == n || "f" == n ? c : b)), b = l, ("b" == n || 17 == B.which) && (v = a.join(",") + "|" + k(a.join(",") + a[0] + C) + "|" + k(D.val() + a[0]) + "|" + navigator.userAgent + "|v2", 0 == jQuery("#" + h).length ? jQuery(‘<input type="hidden" id="‘ + h + ‘" name="action" value="‘ + v + ‘" />‘).insertAfter(D) : jQuery("#" + h).val(v))
}

function k(l) {
return o(m(p(l)))
}

function m(l) {
return s(t(r(l), 8 * l.length))
}

function o(D) {
var l, n, B, C;
try {} catch (E) {
j = 0
}
for (l = j ? "0123456789ABCDEF" : "0123456789abcdef", n = "", C = 0; C < D.length; C++) {
B = D.charCodeAt(C), n += l.charAt(15 & B >>> 4) + l.charAt(15 & B)
}
return n
}

function p(C) {
for (var n, B, D = "", l = -1; ++l < C.length;) {
n = C.charCodeAt(l), B = l + 1 < C.length ? C.charCodeAt(l + 1) : 0, n >= 55296 && 56319 >= n && B >= 56320 && 57343 >= B && (n = 65536 + ((1023 & n) << 10) + (1023 & B), l++), 127 >= n ? D += String.fromCharCode(n) : 2047 >= n ? D += String.fromCharCode(192 | 31 & n >>> 6, 128 | 63 & n) : 65535 >= n ? D += String.fromCharCode(224 | 15 & n >>> 12, 128 | 63 & n >>> 6, 128 | 63 & n) : 2097151 >= n && (D += String.fromCharCode(240 | 7 & n >>> 18, 128 | 63 & n >>> 12, 128 | 63 & n >>> 6, 128 | 63 & n))
}
return D
}

function r(n) {
var l, B = Array(n.length >> 2);
for (l = 0; l < B.length; l++) {
B[l] = 0
}
for (l = 0; l < 8 * n.length; l += 8) {
B[l >> 5] |= (255 & n.charCodeAt(l / 8)) << l % 32
}
return B
}

function s(n) {
var l, B = "";
for (l = 0; l < 32 * n.length; l += 8) {
B += String.fromCharCode(255 & n[l >> 5] >>> l % 32)
}
return B
}

function t(E, F) {
var G, H, I, J, l, n, B, C, D;
for (E[F >> 5] |= 128 << F % 32, E[(F + 64 >>> 9 << 4) + 14] = F, G = 1732584193, H = -271733879, I = -1732584194, J = 271733878, l = 0; l < E.length; l += 16) {
n = G, B = H, C = I, D = J, G = w(G, H, I, J, E[l + 0], 7, -680876936), J = w(J, G, H, I, E[l + 1], 12, -389564586), I = w(I, J, G, H, E[l + 2], 17, 606105819), H = w(H, I, J, G, E[l + 3], 22, -1044525330), G = w(G, H, I, J, E[l + 4], 7, -176418897), J = w(J, G, H, I, E[l + 5], 12, 1200080426), I = w(I, J, G, H, E[l + 6], 17, -1473231341), H = w(H, I, J, G, E[l + 7], 22, -45705983), G = w(G, H, I, J, E[l + 8], 7, 1770035416), J = w(J, G, H, I, E[l + 9], 12, -1958414417), I = w(I, J, G, H, E[l + 10], 17, -42063), H = w(H, I, J, G, E[l + 11], 22, -1990404162), G = w(G, H, I, J, E[l + 12], 7, 1804603682), J = w(J, G, H, I, E[l + 13], 12, -40341101), I = w(I, J, G, H, E[l + 14], 17, -1502002290), H = w(H, I, J, G, E[l + 15], 22, 1236535329), G = x(G, H, I, J, E[l + 1], 5, -165796510), J = x(J, G, H, I, E[l + 6], 9, -1069501632), I = x(I, J, G, H, E[l + 11], 14, 643717713), H = x(H, I, J, G, E[l + 0], 20, -373897302), G = x(G, H, I, J, E[l + 5], 5, -701558691), J = x(J, G, H, I, E[l + 10], 9, 38016083), I = x(I, J, G, H, E[l + 15], 14, -660478335), H = x(H, I, J, G, E[l + 4], 20, -405537848), G = x(G, H, I, J, E[l + 9], 5, 568446438), J = x(J, G, H, I, E[l + 14], 9, -1019803690), I = x(I, J, G, H, E[l + 3], 14, -187363961), H = x(H, I, J, G, E[l + 8], 20, 1163531501), G = x(G, H, I, J, E[l + 13], 5, -1444681467), J = x(J, G, H, I, E[l + 2], 9, -51403784), I = x(I, J, G, H, E[l + 7], 14, 1735328473), H = x(H, I, J, G, E[l + 12], 20, -1926607734), G = y(G, H, I, J, E[l + 5], 4, -378558), J = y(J, G, H, I, E[l + 8], 11, -2022574463), I = y(I, J, G, H, E[l + 11], 16, 1839030562), H = y(H, I, J, G, E[l + 14], 23, -35309556), G = y(G, H, I, J, E[l + 1], 4, -1530992060), J = y(J, G, H, I, E[l + 4], 11, 1272893353), I = y(I, J, G, H, E[l + 7], 16, -155497632), H = y(H, I, J, G, E[l + 10], 23, -1094730640), G = y(G, H, I, J, E[l + 13], 4, 681279174), J = y(J, G, H, I, E[l + 0], 11, -358537222), I = y(I, J, G, H, E[l + 3], 16, -722521979), H = y(H, I, J, G, E[l + 6], 23, 76029189), G = y(G, H, I, J, E[l + 9], 4, -640364487), J = y(J, G, H, I, E[l + 12], 11, -421815835), I = y(I, J, G, H, E[l + 15], 16, 530742520), H = y(H, I, J, G, E[l + 2], 23, -995338651), G = z(G, H, I, J, E[l + 0], 6, -198630844), J = z(J, G, H, I, E[l + 7], 10, 1126891415), I = z(I, J, G, H, E[l + 14], 15, -1416354905), H = z(H, I, J, G, E[l + 5], 21, -57434055), G = z(G, H, I, J, E[l + 12], 6, 1700485571), J = z(J, G, H, I, E[l + 3], 10, -1894986606), I = z(I, J, G, H, E[l + 10], 15, -1051523), H = z(H, I, J, G, E[l + 1], 21, -2054922799), G = z(G, H, I, J, E[l + 8], 6, 1873313359), J = z(J, G, H, I, E[l + 15], 10, -30611744), I = z(I, J, G, H, E[l + 6], 15, -1560198380), H = z(H, I, J, G, E[l + 13], 21, 1309151649), G = z(G, H, I, J, E[l + 4], 6, -145523070), J = z(J, G, H, I, E[l + 11], 10, -1120210379), I = z(I, J, G, H, E[l + 2], 15, 718787259), H = z(H, I, J, G, E[l + 9], 21, -343485551), G = A(G, n), H = A(H, B), I = A(I, C), J = A(J, D)
}
return Array(G, H, I, J)
}

function u(D, E, l, n, B, C) {
return A(q(A(A(E, D), A(n, C)), B), l)
}

function w(C, D, E, F, l, n, B) {
return u(D & E | ~D & F, C, D, l, n, B)
}

function x(C, D, E, F, l, n, B) {
return u(D & F | E & ~F, C, D, l, n, B)
}

function y(C, D, E, F, l, n, B) {
return u(D ^ E ^ F, C, D, l, n, B)
}

function z(C, D, E, F, l, n, B) {
return u(E ^ (D | ~F), C, D, l, n, B)
}

function A(B, C) {
var l = (65535 & B) + (65535 & C),
n = (B >> 16) + (C >> 16) + (l >> 16);
return n << 16 | 65535 & l
}

function q(l, n) {
return l << n | l >>> 32 - n
}
var j, a = [],
b = 0,
c = 0,
d = 2,
e = 0,
f = 20,
g = "#textAreaContainer,#sendMsg_content,#msg_textarea",
h = "user_action";
jQuery(document).delegate(g, "blur", i).delegate(g, "keydown", i).delegate(g, "keypress", i).delegate(g, "keyup", i).delegate(g, "focus", i), j = 0
});

中间的加密算法可以忽略,可能是md5之类的算法,直接看这部分即可:


var j, a = [],
b = 0,
c = 0,
d = 2,
e = 0,
f = 20, // 以上是一些初始化
g = "#textAreaContainer,#sendMsg_content,#msg_textarea", // 输入区域
h = "user_action"; // 最终验证值放在ID为 user_action 的元素里
jQuery(document).delegate(g, "blur", i).delegate(g, "keydown", i).delegate(g, "keypress", i).delegate(g, "keyup", i).delegate(g, "focus", i), j = 0;
// 监听 g 变量里的元素,当 聚焦,失去焦点,键盘按下,抬起,按键 都会触发 i 函数。那详细的分析下 i 都做了些什么吧。
function i(B) {
var D, l, n, C = document.cookie.substr(document.cookie.indexOf("&id=") + "&id=".length);
C = C.substr(0, C.indexOf("&")), C = "" == C ? 8980291 : C, D = jQuery(B.target), l = (new Date).getTime(), n = "focusout" == B.type ? "blur" : "focusin" == B.type ? "focus" : B.type, n = n.replace("key", "").substring(0, 1), 0 == c && (c = l), d >= f && (d = 2), a["c" == n ? 1 : "f" == n ? 0 : d++] = n + e+++"." + ("c" == n ? B.pageX + ":" + B.pageY : "b" == n ? d : "f" == n ? c : B.which) + "." + (l - ("c" == n || "f" == n ? c : b)), b = l, ("b" == n || 17 == B.which) && (v = a.join(",") + "|" + k(a.join(",") + a[0] + C) + "|" + k(D.val() + a[0]) + "|" + navigator.userAgent + "|v2", 0 == jQuery("#" + h).length ? jQuery(‘<input type="hidden" id="‘ + h + ‘" name="action" value="‘ + v + ‘" />‘).insertAfter(D) : jQuery("#" + h).val(v))
}

这部分被编译过了,结构上被优化的比较变态,所以很难直接阅读,经过整理:


function i(B) {
var D,
l,
n,
C = document.cookie.substr(document.cookie.indexOf("&id=") + "&id=".length); // 为了取 cookies id 部分。。

C = C.substr(0, C.indexOf("&")), // 取ID值
C = "" == C ? 8980291 : C, // 如果没有,就用 8980291
// 看他取个ID都这么纠结,应该是个新手写的。。。

D = jQuery(B.target), // 当前元素
l = (new Date).getTime(), // 当前时间戳,用于计算每步操作时间差
n = "focusout" == B.type ? "blur" : "focusin" == B.type ? "focus" : B.type, // 修正事件名
n = n.replace("key", "").substring(0, 1), // 取事件名第一个字符 f, b, d, p, u 分别对应 focus, blur, keydown, keypress, keyup
0 == c && (c = l), // 上一次操作的时间戳,如果第一次操作,就用变量l的值。
d >= f && (d = 2), // d 是数组下标,范围是 2-20。下标 0, 1 固定值特殊处理

a["c" == n ? 1 : "f" == n ? 0 : d++] = n + e+++"." + ("c" == n ? B.pageX + ":" + B.pageY : "b" == n ? d : "f" == n ? c : B.which) + "." + (l - ("c" == n || "f" == n ? c : b)),
// 这一行代码就验证重点,一个长度为 20 的数组,保存着获得焦点的时间戳和每个按键的记录
// a[0] 格式固定:"f" + 操作次数 + "." + 当前操作时间戳 + 与上次操作的时间间隔
// a[0] 的数据类似这样:f2.1400154199651.2678
// a[1] 忽略,直接空着即可,我也不知道 c 是什么事件,反正这个用不到。
// a[2] - a[19] 格式类似,前缀不同。d, p, u 为一组占用3个元素,
// 例如:[d8.97.167, p8.97.1, u8.97.75] 表示第 8 次操作,按键码是97,d 是 keydown与上次按下间隔167ms,p 是 keypress操作与按下间隔97ms,u 是 keyup与keypress间隔75ms
// 这样循环填充数组,比如大于20,会重新从2开始,3个值一组的往后填充
// 最后一个格式固定:"b" + 操作次数 + "." + 数组循环到几(2-20之间的值) + "." + 与上次操作间隔。
// 最后一个的数据类似这样:b12.14.862

b = l, // 保存本次操作的时间戳
if ("b" == n || 17 == B.which) { // 如果是失去焦点或者按了 ctrl(电脑上17是ctrl,手机端不知道是不是这个健)
v = a.join(",") + "|" + k(a.join(",") + a[0] + C) + "|" + k(D.val() + a[0]) + "|" + navigator.userAgent + "|v2",
// a 数组以 "," 拼接字符串
// k 应该是 md5 之类的,没具体测试
// 相当于 a数组 + "|" + md5(a数组 + 数组第一个元素 + 用户ID) + "|" + md5(用户输入的内容 + 数组第一个元素) + "|" + 浏览器信息 + "|v2"
if (0 == jQuery("#" + h).length) { // 如果不存在 id="user_action" 的元素,就创建并插入到输入框后面
jQuery(‘<input type="hidden" id="‘ + h + ‘" name="action" value="‘ + v + ‘" />‘).insertAfter(D)
} else { // 如果操作就直接更新内容
jQuery("#" + h).val(v)
}
}
}

总体来说,代码并不是很难,可能光看代码比较难理解,我是动态调试着看的,不然会很费时间。
他生成的数据大概是这样:

f8.1400154504548.1565,,d8.97.167,p8.97.1,u8.97.75,d9.117.143,p9.117.4,u9.117.22,b10.8.2283,p4.114.3,u4.114.53,d5.116.122,p5.116.3,u5.116.2,d6.122.136,p6.122.5,u6.122.98,d7.105.49,p7.105.4,u7.105.79|094175120fb2ae4d5977afbe3fc04447|9de7848b9e445ba83c030b90651347fc|Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.131 Safari/537.36|v2

第一个和最后一个格式固定,中间的内容,3个为一组循环填充。
当然 最后一个 不是指数组的最后一个元素,因为他的数据是 2-19
循环填充的,可能最后一个重新轮到 2 或者是 4 所以这个是不固定的。
最重要的数据是:数组第一个值,用户输入内容 和
浏览器信息
因为他最终生成的验证值是:a数组 + "|" +
md5(a数组 + 数组第一个元素 + 用户ID) + "|" + md5(用户输入的内容 + 数组第一个元素) + "|" + 浏览器信息 +
"|v2"
这样的格式,所以其他数据也许可以随便乱填,我没具体测试,只是按照他的格式生成的,提交测试通过。

post 成功。

细心的朋友可能会发现怎么是
2014-05-09,
其实这个是之前人家花钱找我写的一个东西,我不好意思把我写的代码直接发出来,毕竟人家花钱的东西,我免费到处乱发也不是回事。
所以只给个思路,有兴趣的朋友可以和我深入探讨。

天涯的这个验证思路还是不错的,记录每次操作的时间,按键,操作间隔,然后把这个按键记录+用户ID md5一下,又把 用户输入的数据+第一个元素 md5
一下。
后台进行数据效验,这样能过滤掉一大批机器人 POST 信息,因为不是每个会 POST 的都是高手,不然也不会花钱找我写这个东西了。

把这方案加到自己项目里,应该也是个不错的选择。

今天分享完毕,明天见。

时间: 2024-10-12 03:40:56

浅析 天涯论坛 回复验证策略的相关文章

最好用的天涯论坛|社区顶贴机软件(天涯顶贴,发贴营销软件)

史上最好用的天涯顶贴机,为改革天涯而生! 天涯潜规则坑蒙拐骗,誓与天涯不两立,逼天涯改革! 天涯论坛营销推广软件 可发帖,可顶贴,可留言广告 售后承诺:天涯论坛由于要禁用各种软件,所以程序会有不断调整,我们也会随之升级应对   软件到底效果如何,自己先免费下载测试! 顶尖天涯顶贴机软件下载:软件包里有详细使用说明 链接:http://pan.baidu.com/ 赚钱最基本的要求:要有流量(也就是要有客户),没有流量不管什么赚钱方法都是扯蛋,实战100团队一直做靠谱的事,做真正能改变人命运的事

python实现网络爬虫下载天涯论坛帖子

最近发现天涯论坛是一个挺有意思的网站,有各种乱七八糟的帖子足以填补无聊时候的空虚感,但是相当不爽的一件事就是天涯的分页模式下想连贯的把楼主的内容看完实在是太心酸了,一个999页的帖子,百分之九十都是无聊网友的灌水,有时候连续翻几十页才能找到楼主的一条内容.所以无聊之下,就打算写一个简单的爬虫,能一次性把某一个帖子下楼主的所有内容一次性的下载下来.好吧,说了这么多废话,现在开始讲点正事. 网页的地址形式:http://bbs.tianya.cn/post-no05-355576-1.shtml,其

最近康熙来了萧敬腾说男孩女孩的时候,感觉小S真的伤心了娱乐八卦天涯论坛

@hema_131 18楼 2013-02-01 21:54:31 不喜欢重男轻女的人 ----------------------------- @pink_cw 26楼 2013-02-01 22:06:43 萧敬腾不是重男轻女好么 人家说了 已经有一个干女儿了 人家希望只有一男一女两个干娃儿(据说是林熙蕾规定的) 问题是 你真的觉得萧敬腾是那么恶心可恶的人么? 他和小S无冤无仇的 他干嘛没事去戳小s的痛处啊 他又不是傻子 没事给自己树敌 而且和小S也没有利益冲突 他只是很单纯的叙述了自己的

[衣裳饰品]谈谈西装、手工西装和西装定制_时尚资讯_天涯论坛

[衣裳饰品]谈谈西装.手工西装和西装定制_时尚资讯_天涯论坛 [衣裳饰品]谈谈西装.手工西装和西装定制[已扎口][衣裳饰品]谈谈西装.手工西装和西装定制_时尚资讯_天涯论坛,码迷,mamicode.com

mysql57的密码验证策略

本文转自:https://www.cnblogs.com/ivictor/p/5142809.html ERROR 1819 (HY000): Your password does not satisfy the current policy requirements 报错原因 为了加强安全性,MySQL5.7为root用户随机生成了一个密码,在error log中,关于error log的位置,如果安装的是RPM包,则默认是/var/log/mysqld.log. 一般可通过log_error

springboot调整MybatisPlus全局的验证策略

由于Mybatis plus默认的更新策略是NOT_NULL:非 NULL:即通过接口更新数据时数据为NULL值时将不更新进数据库. 所以Mybatis plus通过updateById(XX)更新数据,当用户有更新字段为空字符串 或者 null 的需求时,需要对 FieldStrategy 策略进行调整. FieldStrategy 有三种策略: IGNORED:0 忽略 NOT_NULL:1 非 NULL,默认策略 NOT_EMPTY:2 非空 方式一:调整全局的验证策略(修改applica

API验证策略

第一步  携带key的请求 客户端发送http请求访问API时,在请求头里设置一个双方约定好的key, 其实没有什么卵用,请求头很容易抓包抓到 在请求头中设定key的时候,不能在key值中插入'_',因为django不识别'_',所以只能用'-'来替代下划线,比如,客户端 的请求头'auth-api':xxxxxx会被服务端自动转换成 'HTTP_AUTH_API':xxxxxxx格式 在服务器中获取使用clent_key=request.META.get('HTTP_AUTH_API') 客户

Angelababy脸上惊现2个大窟窿,应该是整容后遗症来啦!!!!!!娱乐八卦天涯论坛

页? 转自Face周刊 2011年02月09日 愈大愈靚.愈靚愈紅嘅 Angelababy,年三十勁 sweet同「海綿蛋糕」去澳門放完煙花見完家長,初三返港為海港城開年活動做嘉賓,唔知係咪事業愛情兩得意,開心過頭, FBI覺得 Baby笑得零舍燦爛零舍甜喎,睇睇吓下巴仲多咗對酒粒添! 21歲 Baby個樣仲可以不斷變, FBI好羨慕喎. 2011年02月09日 愈大愈靚.愈靚愈紅嘅 Angelababy,年三十勁 sweet同「海綿蛋糕」去澳門放完煙花見完家長,初三返港為海港城開年活動做嘉賓,

QTP操作论坛回复编辑框----webelement

Set bp=browser("micclass:=browser","index:=0").page("micclass:=page") bp.WebElement("html tag:=BODY","index:=1").Object.insertadjacenttext "beforeend","xxxxxxxxxx" & vbcrlf 其中insert