PHP使用Session与IP限制实现表单防重复提交
在Web开发中,防止恶意脚本或用户快速连续刷新页面/提交表单是常见的安全需求。本文详述如何利用PHP的Session功能结合客户端IP地址,构建一个轻量级的防重复提交验证模块。
核心逻辑与目标
该方案的目标是确保同一个来源IP在同一时间段内,对同一组业务数据的提交操作不超过指定频率。其核心原理如下:
- 强制POST方式:拒绝直接通过浏览器地址栏输入URL进行的GET请求,防止随意刷新触发业务逻辑。
- 唯一标识生成:结合访客的真实IP和提交的具体业务参数,通过MD5加密生成唯一的Session键名。
- 时间戳校验:首次提交时记录当前时间存入Session;后续请求时计算时间差,若小于设定的最小间隔时间,则判定为非法刷新。
准备工作
在开始之前,请确保您的PHP环境已开启Session支持。本示例包含两个文件:
a.html(用于发起提交的入口)和 b.php(用于处理提交并实施防护的核心逻辑)。
第1步:获取访客真实IP
为了准确定位请求来源,首先需要编写一个兼容多层代理环境的IP获取函数。许多现代网站背后可能有CDN或负载均衡器,直接使用 $_SERVER['REMOTE_ADDR'] 可能只能获取到代理服务器的地址。
// 定义获取真实IP的全局函数
function GetIP() {
static $ip = NULL;
if ($ip !== NULL) return $ip;
// 优先检查 HTTP_X_FORWARDED_FOR (常见于CDN后方)
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
foreach ($arr as $xip) {
$xip = trim($xip);
if ($xip != 'unknown') {
$ip = $xip;
break;
}
}
} elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
// 其次是 HTTP_CLIENT_IP
$ip = $_SERVER['HTTP_CLIENT_IP'];
} else {
// 最后是标准的 REMOTE_ADDR
if (isset($_SERVER['REMOTE_ADDR'])) {
$ip = $_SERVER['REMOTE_ADDR'];
} else {
$ip = '0.0.0.0';
}
}
// 正则过滤确保获取的是合法的IPv4地址格式
preg_match("/[\d\.]{7,15}/", $ip, $onlineip);
$ip = !empty($onlineip[0]) ? $onlineip[0] : '0.0.0.0';
return $ip;
}
第2步:创建防护逻辑代码 b.php
此代码应放置在需要防止被刷新的页面顶部。它包含了请求方法校验、IP提取、Session管理及防刷新判定。
<?php
// 1. 严格限制只能通过 POST 方式访问
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
header('HTTP/1.1 404 Not Found');
die('亲,页面不存在');
}
// 2. 启动 Session
session_start();
// 3. 接收业务参数
$fs1 = $_POST['a'];
$fs2 = $_POST['b'];
// 4. 设置防刷新的时间间隔(单位:秒),例如 30 秒
$allowTime = 30;
// 5. 获取当前访客IP
$reip = GetIP();
// 6. 生成唯一标识:IP + 业务参数的组合哈希
// 这确保了不同用户(不同IP)或不同数据提交都有独立的计数器
$allowT = md5($reip . $fs1 . $fs2);
// 7. 校验时间间隔
if(!isset($_SESSION[$allowT])) {
// 如果 Session 中无记录,视为第一次请求,存入当前时间戳
$_SESSION[$allowT] = time();
} else if(time() - $_SESSION[$allowT] > $allowTime) {
// 如果时间差大于允许的间隔,说明上一次操作早已结束,重置时间为当前时间
$_SESSION[$allowT] = time();
} else {
// 否则,判定为刷新过快,拒绝服务
header('HTTP/1.1 404 Not Found');
die('来自' . $reip . '的亲,您刷新过快了');
}
// --- 此处可以继续执行业务逻辑代码 ---
echo "操作成功!";
?>
第3步:准备前端测试页面 a.html
创建一个简单的HTML表单来模拟提交过程。注意隐藏域的值必须与 b.php 中对应的参数名一致。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>测试防刷新</title>
</head>
<body>
<!-- action指向处理逻辑的PHP文件 -->
<form action="b.php" method="post">
<!-- 模拟业务数据参数 -->
<input type="hidden" id="a" name="a" value="data_value_a"/>
<input type="hidden" id="b" name="b" value="data_value_b"/>
<button type="submit">点击提交</button>
</form>
</body>
</html>
完成判断
- 正常流程:在浏览器访问
a.html并提交表单。如果这是该IP的第一次请求,或者距离上次请求已超过$allowTime秒,系统将输出“操作成功!”。 - 拦截流程:在未到达时间间隔的情况下,立即再次刷新页面
b.php或直接提交表单。此时页面将显示空白或返回404,并在控制台/页面上看到提示:“来自{IP}的亲,您刷新过快了”。 - 安全校验:直接在浏览器地址栏输入
b.php的地址进行访问。由于非POST请求,系统将直接拦截并显示404错误。
注意事项与局限性
- Referer防御:原文提及可通过HTTP Referer头部判断请求来源以防止跨站伪造提交(CSRF)。然而,在实际应用中,Referer可以被用户伪造,且部分浏览器或网络环境下(如HTTPS跳转至HTTP)可能会导致Referer丢失,因此作为单一防线并不稳固,建议结合Token机制更佳。
- Session存储:本方案依赖服务器端Session。如果网站开启了分布式架构且未配置统一的Session存储(如Redis集群),需确保用户的请求路由到同一台服务器,否则
$_SESSION中的状态可能无法同步。 - 参数设计:
$fs1和$fs2是为了区分不同的业务操作。如果多个操作共用同一个接口但业务含义不同,务必在生成$allowT的MD5源中加入区分字段,否则会导致A操作被B操作的防刷计数器拦截。
简要总结
通过PHP内置的Session功能和IP识别,我们可以以极低的代码成本实现针对特定场景的防刷新保护。虽然它不能解决所有形式的安全问题,但对于防范普通的爬虫刷量或用户误触造成的服务器压力具有显著效果。
以上就是PHP环境下利用Session防止页面重复刷新的详细内容,更多关于PHP安全防护的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













