一个接口,浏览器每请求 100 次,弹出一次验证码,要求服务器端不记录浏览器对应 ip 的请求次数,该怎么实现?

2019-12-27 09:11:19 +08:00
 kisshere

如果通过服务器端记录客户端对应 ip 的请求次数,数据库会撑爆的,如何从验证客户端浏览器提供的数据下手,让这个接口,浏览器每请求 100 次,就弹出一次验证码?

$_COOKIE['requestCount']=$_COOKIE['requestCount']+1;
if($_COOKIE['requestCount']>100){show_captcha();}//这种逻辑就算了哈,小学生都能修改 cookie 破解
9454 次点击
所在节点    程序员
89 条回复
westoy
2019-12-27 15:39:08 +08:00
服务端不做记录没用

Cookie 计数的前提是客户端会更新 Cookie, 客户端会更新 Cookie 的前提是用会自动托管 Cookie 状态的类库, 问题是...........对方要只是很单纯的把抓到的 Cookie 放 header 里万年不变呢..........
locoz
2019-12-27 15:43:09 +08:00
「客户端提交的一切信息都不可信」这一点都不知道吗…
kisshere
2019-12-27 15:46:27 +08:00
@zhttty 我上面都说了,你这个不可行,我每次都给服务器提供相同的加密 cookie,这个 cookie 是我抓包 chrome 得到的,你服务器每次都只能放行我通过
ncwtf
2019-12-27 15:50:33 +08:00
这也不行那也不行?
ip 放到缓存里加个失效时间有什么不行的。数据库撑爆。。一天访问一次,第一百天访问就要输入验证码?不得加个时间清除么。
次数+时间+签名 加密放到 cookie 里有什么不行的,就算他伪造 cookie,时间到了不是也失效了?
用 nginx 脚本应该也能实现需求啊


小学生都能修改。。有丶牛逼。。
eason1874
2019-12-27 15:52:56 +08:00
严格限制请求次数就不可能不在服务器记录,无论什么 token 什么 nonce 都一样,纯粹依赖客户端的 nonce 摆脱不了重放。

记录 100 次就重置了,这能有多少数据量,纯数据来说按 100 万个 token 算才不到 100mb
markgor
2019-12-27 15:57:00 +08:00
“最终我还是采用简单粗暴的方法:rand(1,100)>98 && show_captcha();”

看了下,你好像是想反爬,
你這個方法我直接棄用這次請求,再發起新的請求,不就繞過去了嗎?
rbuli
2019-12-27 15:57:23 +08:00
```
public boolean flowControl(String ip) {
String key = UrspReidsPrefixEnum.URSP_FREFIX.getCode() + UrspReidsPrefixEnum.FLOW_CONTROL_FREFIX.getCode() + ip;
// 限流时间区间
int seconds = 60;
// 限流最大允许值
int max = 100;
long total = 1L;
try {
if (jedisClient.get(key) == null) {
jedisClient.setex(key, "0", seconds);
} else {
total = jedisClient.incr(key).longValue();
if (jedisClient.ttl(key).longValue() == -1L) {
jedisClient.expire(key, seconds);
}
}
} catch (Exception e) {
LOGGER.error("流量控制组件:执行计数操作失败,无法执行计数");
}
long keytotaltransations = max;

// 判断是否已超过最大值,超过则返回 false
if (total > keytotaltransations) {
return false;
}
return true;
}
```
xuanbg
2019-12-27 16:48:48 +08:00
后端完全不存数据,那这个需求臣妾办不到啊!

但是,访问次数不存数据库可以存 Redis 嘛,key 设置一个合适的过期时间就行了。
mlboy
2019-12-27 17:58:12 +08:00
计数啊
mlboy
2019-12-27 17:58:59 +08:00
桶,令牌 了解一下
jsnjfz
2019-12-27 17:59:36 +08:00
nginx 里面写代码或许能搞
zgq3337
2019-12-27 18:42:39 +08:00
为了防重放,再加一个总体 ID 轮链
pinews
2019-12-27 18:59:38 +08:00
cookie 加密
mreasonyang
2019-12-27 20:34:40 +08:00
@wangxiaoaer header 里自定义字段或直接 403 就行了
yankebupt
2019-12-27 21:55:15 +08:00
@kisshere
发 100 个 token,99 层加密,每次请求解密一层返回下一个
hardcode 验证 token 的逻辑不读库
api limit rotate 复位的时候直接改下 hardcode
然后上个防 replay attack 的防火墙(估计比内存数据库还贵,手动狗头)

/\
||
||
||
……
话说一般外行的话会不会都以为逻辑这样就可以的?
yankebupt
2019-12-27 22:30:40 +08:00
如果去 TMD 用户体验的话,好像可以这样

第一次使用之前,需要调用量申请 api 返回 ip+请求准确时间+定制 salt 静态加密的一个 token
这个 token 十几秒(时间 1)就会过期,需要立刻使用,把后面请求的申请包在里面,同样返回静态验证过期时间不等(时间 2)的下次使用加密 token

时间 2 可以宽松一点,一小时 60 次的话可以 2-15 分钟。

服务器压力变大的时候缩短时间 2,然后把 spam 申请 api 的 ip 加入黑名单直接 ban 掉就是了。
这样防重放防火墙逻辑可以把窗口只保留 2-15 分钟……带宽不大的话两分钟 D 不满数据库……

记得之前一个外行还是不外行的站就是这么搞的,年代久远,想不起来是哪个了。
yankebupt
2019-12-28 01:37:00 +08:00
@kisshere 当然如果和压力完全无关单纯只是想恶心人,可能你想要的是
if (([minute of day] / [[typical interval of your api]) +- rand(md5(src-ip))*[random size] ) mod 100 >=98
captcha()
if fail
add-ip-to-must-captcha-list-for-24h-until-success()
fi
fi
happyz90
2019-12-28 07:11:34 +08:00
不是精确的 100 次可以接受不?
MoccaCafe
2019-12-28 07:31:52 +08:00
前端传来的数据是不可信任的,尤其是这种不带凭证又不带 ip,除非你对所有用户都做一样的限制
hulonghe
2019-12-28 07:53:58 +08:00
开放授权码(后端限制授权码使用次数,合理控制分发),前后端都记录授权次数,校验对比。授权码生成得有规律,但又不可解,就像是 uuid。
后端增加一个授权分发机制,如果达到异常点,控流,查来源,封访问权限(或者限制下次可使用时间)

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://ex.noerr.eu.org/t/632749

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX