前言:近期我的一个API接口站点 www.yuanxiapi.cn 被恶意调用了,需要对频率进行限制,有很多种方法 但是想着直接用最简单粗暴的方法,使用Redis对 IP 访问进行限制。
宝塔 PHP需要先安装拓展 redis
$h=3; //分钟设置
$ip=$_SERVER['REMOTE_ADDR'];
$redis = new Redis();
$redis->connect('127.0.0.1', 6379); //连接 Redis
if (!$redis->exists($ip)){
//第一次访问
$redis->set($ip,1,$h*60); // 设置N分钟过期时间并设置初始值1
}else{
//已经记录过IP
if ($redis->get($ip)<30){ //判断IP有没有到达拉黑阈值
$redis->incr($ip); //次数加一
}else{ exit("您的请求过于频繁,{$h}分钟内请求已达到:".$redis->get($ip)."次");}
}
//效果:3分钟内某IP访问超过30次则限制
附言:对IP进行限制,治标不治本,因为还是会有人伪造IP进行恶意访问,除非能精准的获取真实IP(但是很难,目前小白的我还没有发现php即使在伪造IP的各种情况下,还能完美无缺获取真实IP的方法)。
评论
发表评论:
小仙女
回复$h = 3; //分钟设置
$ip = $_SERVER['REMOTE_ADDR'];
$redis = new Redis();
$redis-˃connect('127.0.0.1', 6379); //连接 Redis
// 使用 Redis 的 setex 命令设置键的过期时间
$redis-˃setex($ip, $h * 60, 1); // 设置N分钟过期时间并设置初始值1
// 增加其他验证机制,如用户代理(User-Agent)或请求头信息
$user_agent = $_SERVER['HTTP_USER_AGENT'];
$redis-˃setex($ip . '_user_agent', $h * 60, $user_agent);
if ($redis-˃get($ip . '_user_agent') !== $user_agent) {
// 如果用户代理不匹配,说明请求可能是伪造的,直接退出
exit("您的请求过于频繁,被视为异常请求。");
}
if ($redis-˃get($ip) ˃= 30) {
// 如果IP地址在3分钟内的请求次数已经达到30次,限制访问并给出提示
exit("您的请求过于频繁,{$h}分钟内请求已达到:".$redis-˃get($ip)."次");
} else {
// 请求次数加一
$redis-˃incr($ip);
}
小仙女
回复{
// 使用连接池来管理Redis连接
$redis = new Predis\Client($config['redis']);
// 获取客户端的IP地址和用户代理
$ip = filter_input(INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP);
$user_agent = filter_input(INPUT_SERVER, 'HTTP_USER_AGENT');
// 设置或更新IP地址的过期时间
$redis-˃setex($ipPrefix . $ip, $h * 60, 1);
// 检查用户代理是否匹配
if (!$redis-˃exists($userAgentPrefix . $ip) || $redis-˃get($userAgentPrefix . $ip) !== $user_agent) {
$redis-˃setex($userAgentPrefix . $ip, $h * 60, $user_agent);
$redis-˃set($requestCountPrefix . $ip, 1); // 重置请求次数
} else {
// 检查请求次数
$requestCount = $redis-˃get($requestCountPrefix . $ip);
if ($requestCount ˃= $maxRequests) {
http_response_code(429); // Too Many Requests状态码
exit("您的请求过于频繁,{$h}分钟内请求已达到:{$requestCount}次");
} else {
$redis-˃incr($requestCountPrefix . $ip); // 请求次数加一
}
}
// 可选:增加其他验证机制,如CF-Conn
小仙女
回复// 可选:增加其他验证机制,如CF-Connecting-IP(针对Cloudflare CDN)等
$cfConnectingIp = filter_input(INPUT_SERVER, 'HTTP_CF_CONNECTING_IP', FILTER_VALIDATE_IP);
if (!empty($cfConnectingIp) && $cfConnectingIp !== $ip) {
$ip = $cfConnectingIp;
$redis-˃setex($ipPrefix . $ip, $h * 60, 1); // 使用Cloudflare提供的原始访问者IP更新Redis键
// ... 其他逻辑
}
} catch (Exception $e) {
// 记录错误日志或抛出异常,具体取决于您的需求和应用程序结构。
error_log("Error: " . $e-˃getMessage());
http_response_code(500); // Internal Server Error状态码
exit("Internal Server Error");
}