我一直在尝试在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地址,增加故障最少)。有没有更好的方法来维护动态黑名单,也许每分钟写入一次缓存?

我确实看到Banning by IP with php/mysql与我的问题类似,但是我允许人们在一段时间内停止尝试登录的情况下将他们从黑名单中删除。这样,那些简单地忘记了自己的凭据的人所受到的影响要比那些试图强行获取凭据的人受到的影响要小。

最佳答案

以下查询不需要定期运行,可以移至cron作业:

DELETE FROM failures WHERE release_time < ?;


如果此人被列入黑名单,则此“布尔”查询将返回1,否则返回0:

SELECT
  COUNT(ip_address) as blacklisted
FROM blacklist
WHERE
  ip_address = ? AND
  release_time > ? AND
  failures > 5


由于您不使用PHP对行进行计数和比较数字,因此可能会加快速度:

if ($row['blacklisted']) { /* ... */ }


我认为您真的无法避免最后一个。

10-02 07:02