本文目录导读:

在PHP中实现IP封禁有多种方法,以下是几种常见的实现方式:
基于文件存储的简单封禁
<?php
class IPBan {
private $banFile = 'banned_ips.txt';
private $cacheFile = 'banned_cache.php';
// 获取用户IP
public function getClientIP() {
$ipAddress = '';
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ipAddress = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ipList = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$ipAddress = trim($ipList[0]);
} else {
$ipAddress = $_SERVER['REMOTE_ADDR'];
}
return $ipAddress;
}
// 检查IP是否被封禁
public function isBanned($ip) {
$bannedIps = $this->getBannedIPs();
return in_array($ip, $bannedIps);
}
// 获取所有被封禁的IP列表
private function getBannedIPs() {
if (file_exists($this->cacheFile)) {
$cached = include($this->cacheFile);
if (isset($cached['expire']) && $cached['expire'] > time()) {
return $cached['ips'];
}
}
$ips = [];
if (file_exists($this->banFile)) {
$lines = file($this->banFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
if (!empty($line)) {
$ips[] = trim($line);
}
}
}
// 缓存结果,减少文件读取
$this->cacheBannedIPs($ips);
return $ips;
}
// 缓存封禁IP列表
private function cacheBannedIPs($ips) {
$cacheData = [
'ips' => $ips,
'expire' => time() + 300 // 5分钟缓存
];
file_put_contents($this->cacheFile, '<?php return ' . var_export($cacheData, true) . ';');
}
// 添加IP到封禁列表
public function banIP($ip, $reason = '') {
$bannedIps = $this->getBannedIPs();
if (!in_array($ip, $bannedIps)) {
$line = $ip . ($reason ? ' # ' . $reason : '') . PHP_EOL;
file_put_contents($this->banFile, $line, FILE_APPEND);
// 清除缓存
if (file_exists($this->cacheFile)) {
@unlink($this->cacheFile);
}
return true;
}
return false;
}
// 解除IP封禁
public function unbanIP($ip) {
$bannedIps = $this->getBannedIPs();
if (in_array($ip, $bannedIps)) {
$newList = array_diff($bannedIps, [$ip]);
file_put_contents($this->banFile, implode(PHP_EOL, $newList) . PHP_EOL);
// 清除缓存
if (file_exists($this->cacheFile)) {
@unlink($this->cacheFile);
}
return true;
}
return false;
}
}
// 使用示例
$banner = new IPBan();
$clientIP = $banner->getClientIP();
if ($banner->isBanned($clientIP)) {
http_response_code(403);
die('您的IP已被封禁!');
}
// 封禁IP示例
// $banner->banIP('192.168.1.100', '恶意攻击');
// $banner->unbanIP('192.168.1.100');
?>
基于数据库的封禁(推荐)
<?php
class IPBanDB {
private $pdo;
public function __construct($host, $dbname, $user, $pass) {
try {
$this->pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $user, $pass);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->createTable();
} catch (PDOException $e) {
die('数据库连接失败: ' . $e->getMessage());
}
}
// 创建封禁表
private function createTable() {
$sql = "CREATE TABLE IF NOT EXISTS banned_ips (
id INT AUTO_INCREMENT PRIMARY KEY,
ip VARCHAR(45) NOT NULL UNIQUE,
reason VARCHAR(255),
banned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at DATETIME DEFAULT NULL,
INDEX idx_ip (ip)
)";
$this->pdo->exec($sql);
}
// 获取用户IP
public function getClientIP() {
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
return $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
return trim(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0]);
}
return $_SERVER['REMOTE_ADDR'];
}
// 检查IP是否被封禁
public function isBanned($ip) {
$sql = "SELECT COUNT(*) FROM banned_ips
WHERE ip = :ip
AND (expires_at IS NULL OR expires_at > NOW())";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([':ip' => $ip]);
return $stmt->fetchColumn() > 0;
}
// 添加封禁
public function banIP($ip, $reason = '', $hours = null) {
$expires = $hours ? date('Y-m-d H:i:s', strtotime("+$hours hours")) : null;
$sql = "INSERT INTO banned_ips (ip, reason, expires_at)
VALUES (:ip, :reason, :expires)
ON DUPLICATE KEY UPDATE reason = :reason2, expires_at = :expires2";
$stmt = $this->pdo->prepare($sql);
return $stmt->execute([
':ip' => $ip,
':reason' => $reason,
':expires' => $expires,
':reason2' => $reason,
':expires2' => $expires
]);
}
// 解除封禁
public function unbanIP($ip) {
$sql = "DELETE FROM banned_ips WHERE ip = :ip";
$stmt = $this->pdo->prepare($sql);
return $stmt->execute([':ip' => $ip]);
}
// 清除过期记录
public function cleanExpired() {
$sql = "DELETE FROM banned_ips WHERE expires_at IS NOT NULL AND expires_at < NOW()";
return $this->pdo->exec($sql);
}
// 获取封禁信息
public function getBanInfo($ip) {
$sql = "SELECT * FROM banned_ips WHERE ip = :ip";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([':ip' => $ip]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
}
// 使用示例
$banner = new IPBanDB('localhost', 'mydb', 'user', 'password');
$clientIP = $banner->getClientIP();
if ($banner->isBanned($clientIP)) {
$banInfo = $banner->getBanInfo($clientIP);
http_response_code(403);
echo "您的IP已被封禁!";
if ($banInfo['expires_at']) {
echo " 封禁到期时间: " . $banInfo['expires_at'];
}
if ($banInfo['reason']) {
echo " 原因: " . $banInfo['reason'];
}
die();
}
// 封禁操作
// $banner->banIP('192.168.1.100', '恶意攻击', 24); // 封禁24小时
// $banner->unbanIP('192.168.1.100'); // 解除封禁
// $banner->cleanExpired(); // 清理过期记录
?>
基于Redis的高性能方案
<?php
class IPBanRedis {
private $redis;
private $prefix = 'ip_ban:';
public function __construct($host = 'localhost', $port = 6379, $password = null) {
$this->redis = new Redis();
$this->redis->connect($host, $port);
if ($password) {
$this->redis->auth($password);
}
}
// 获取用户IP
public function getClientIP() {
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
return $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
return trim(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0]);
}
return $_SERVER['REMOTE_ADDR'];
}
// 检查IP是否被封禁
public function isBanned($ip) {
return (bool)$this->redis->exists($this->prefix . $ip);
}
// 添加封禁
public function banIP($ip, $seconds = 3600) {
return $this->redis->set($this->prefix . $ip, time(), $seconds);
}
// 永久封禁
public function banIPForever($ip) {
return $this->redis->set($this->prefix . $ip, -1);
}
// 解除封禁
public function unbanIP($ip) {
return $this->redis->del($this->prefix . $ip);
}
// 获取封禁信息
public function getBanInfo($ip) {
$key = $this->prefix . $ip;
if ($this->redis->exists($key)) {
$value = $this->redis->get($key);
return [
'banned' => true,
'permanent' => $value == -1,
'banned_at' => $value == -1 ? null : date('Y-m-d H:i:s', $value)
];
}
return ['banned' => false];
}
}
// 使用示例
$banner = new IPBanRedis();
$clientIP = $banner->getClientIP();
if ($banner->isBanned($clientIP)) {
http_response_code(403);
die('您的IP已被封禁!');
}
// 封禁操作
// $banner->banIP('192.168.1.100', 3600); // 封禁1小时
// $banner->banIPForever('192.168.1.101'); // 永久封禁
// $banner->unbanIP('192.168.1.100'); // 解除封禁
?>
.htaccess方式(Apache服务器)
# .htaccess <IfModule mod_rewrite.c> RewriteEngine On # 封禁单个IP Deny from 192.168.1.100 # 封禁多个IP Deny from 192.168.1.101 192.168.1.102 192.168.1.103 # 封禁IP段 Deny from 192.168.1.0/24 # 封禁域名 Deny from bad-domain.com </IfModule>
综合使用的最佳实践
<?php
// ipban.php - 综合使用示例
require_once 'IPBanDB.php';
session_start();
class IPBanManager {
private $banManager;
private $maxAttempts = 5;
private $lockoutTime = 900; // 15分钟
public function __construct() {
$config = [
'host' => 'localhost',
'dbname' => 'mydb',
'user' => 'user',
'pass' => 'password'
];
$this->banManager = new IPBanDB(
$config['host'],
$config['dbname'],
$config['user'],
$config['pass']
);
}
public function checkAccess() {
$ip = $this->banManager->getClientIP();
// 检查是否被封禁
if ($this->banManager->isBanned($ip)) {
$this->denyAccess();
}
// 监控登录失败次数
if (isset($_SESSION['login_attempts'][$ip])) {
$attempts = $_SESSION['login_attempts'][$ip];
if ($attempts >= $this->maxAttempts) {
// 自动封禁
$this->banManager->banIP($ip, '多次登录失败', $this->lockoutTime / 3600);
unset($_SESSION['login_attempts'][$ip]);
$this->denyAccess();
}
}
}
public function recordFailedLogin() {
$ip = $this->banManager->getClientIP();
if (!isset($_SESSION['login_attempts'][$ip])) {
$_SESSION['login_attempts'][$ip] = 0;
}
$_SESSION['login_attempts'][$ip]++;
}
private function denyAccess() {
http_response_code(403);
die('访问被拒绝:您的IP已被封禁。');
}
// 管理员功能
public function adminUnban($ip) {
return $this->banManager->unbanIP($ip);
}
public function adminBan($ip, $reason, $hours = null) {
return $this->banManager->banIP($ip, $reason, $hours);
}
}
// 使用
$manager = new IPBanManager();
$manager->checkAccess();
?>
注意事项
- IP获取安全:
HTTP_X_FORWARDED_FOR可能被伪造,建议只信任已知代理的此字段 - IP格式验证:使用
filter_var($ip, FILTER_VALIDATE_IP)验证IP格式 - 性能优化:使用缓存或Redis避免频繁数据库查询
- 批量操作:管理员操作页面建议添加批量封禁/解封功能
- 日志记录:记录所有封禁/解封操作,便于审计
根据你的需求选择合适的方案,对于小型项目,文件存储即可;对于生产环境,建议使用数据库或Redis方案。