我一直在尝试在 PHP 中实现一种 IP 黑名单,我将失败的登录尝试存储到具有以下架构的 MySQL 表中:
CREATE TABLE blacklist(
`ip_address` VARCHAR(35) NOT NULL,
`failures` INTEGER DEFAULT 0,
`release_time` BIGINT DEFAULT -1,
PRIMARY KEY(`ip_address`)
);
在我的登录检查中,我首先使用以下查询删除所有发布时间早于当前时间(如果发布时间已经过去)的黑名单条目:
/* I pass in time() */
DELETE FROM failures WHERE release_time < ?;
然后我执行以下操作:
/* I pass in $_SERVER['REMOTE_ADDR'] */
SELECT failures FROM blacklist WHERE ip_address=?
如果我没有检索到任何行或者我返回的 $row['failures'] 超过 5,我允许检查用户名和密码。否则,我完全拒绝登录。
对于每次失败的登录尝试(无论是通过黑名单规则还是通过无效的用户名/密码),我都会执行:
/* IP address is $_SERVER['REMOTE_ADDR'],
release_time is current time + (new failures * 90 minutes) */
INSERT INTO BLACKLIST(ip_address, failures, release_time) VALUES(?,?,?)
ON DUPLICATE KEY UPDATE failures=failures+1, release_time=?;
不幸的是,我至少访问了数据库 3 次(清除黑名单、获取 IP 地址、最少增加失败次数)。有没有更好的方法来维护动态黑名单,也许每分钟写入一次缓存?
我确实看到使用 php/mysql 的 IP 禁止与我的问题类似,但是如果人们在很长一段时间内停止尝试登录,我允许他们从黑名单中删除。这样一来,简单地忘记他们的凭证的人受到的影响比那些试图通过暴力方式获得凭证的人受到的影响要小。