WordPress登录邮件提醒:异地登录 1 分钟内知道(附代码)
本站部分链接为联盟推广链接。通过这些链接购买,我可能获得佣金...
这不仅不会增加您的实际费用,通常还能让您享受到专属折扣。所有上榜推荐均基于真实项目环境下的自费实测,排名完全由客观测评数据决定。详细规则请查阅《免责声明》。
上次帮朋友排查被黑的站,我翻用户列表时发现多了一个管理员账号,创建时间是三周前。三周里,对方随时进出后台,客户一点都不知道。
如果那时候有一封「有人从新 IP 登录了你的后台」的邮件,事情可能在第一天就被掐断了。
登录邮件提醒就是干这个的。它不能挡住黑客,但能让你第一时间知道有人进来了。很多时候,早一天知道和晚三周知道,差的是一次改密码,和一整台服务器推倒重来。
网上流传最广的那段代码,问题出在哪?
搜「WordPress 登录邮件提醒」,排在前面的教程基本都在用同一段老代码,写于二零一几年左右。原理没问题:挂在 wp_login 和 wp_login_failed 两个钩子上,登录成功或失败时调用 wp_mail() 发邮件。
但我不建议你照抄,它至少有四个坑。
第一个坑最致命:邮件里写着明文密码。 那段代码把 $_POST['pwd'] 原样拼进了邮件正文。登录成功时,这就是你的真实密码。登录失败时,这往往是你少打一个字符的真实密码,或者你在别的网站用过的密码。
邮件会在服务器上、SMTP 服务商那里、你的邮箱里各存一份。等于在三个地方备份了你的后台密码,谁的邮箱被盗,谁就能进你的后台。
第二个坑:每次登录失败都发一封。 平时没感觉,一遇到暴力破解,脚本一小时试几千次,你的邮箱就被几千封告警淹没了。更麻烦的是,SMTP 服务商一般都有每日发信上限。额度被告警邮件吃光后,客户的询盘通知、订单邮件也发不出去了。参考文章评论区就有人吐槽过邮箱被塞满。
第三个坑:改了 PHP 全局时区。 代码开头那行 date_default_timezone_set('PRC'),会把整个 PHP 进程的时区改掉。WordPress 内部默认按 UTC 处理时间,再按后台设置的时区换算显示。全局时区被改,有可能让定时发布、计划任务的时间算偏。显示时间,用 WordPress 自带的 wp_date() 就行,它会自动用你在「设置 → 常规」里选的时区。
第四个坑:接了 Cloudflare,IP 全是假的。 代码读的是 $_SERVER['REMOTE_ADDR']。网站接入 Cloudflare 后,这个值是 Cloudflare 节点的 IP,不是访客的真实 IP。邮件里写的 IP 全都差不多,根本看不出是谁。
动手前,先确认 WordPress 能正常发邮件
不管用代码还是插件,登录提醒都靠 WordPress 发信。很多主机默认的 PHP mail 函数发不出去,或者发出去直接进垃圾箱。
验证方法很简单:去「用户 → 个人资料」,点「发送密码重置链接」给自己发一封,看能不能收到。收不到的话,先配好 SMTP。
我自己用的是 FluentSMTP,免费,还能配一个备用发信服务器,主线路挂了自动切换。配置步骤看这篇:FluentSMTP 配置多个 SMTP 发送服务器教程。
另外,收提醒的邮箱就是「设置 → 常规」里的「管理员电子邮件地址」。确认它是你每天都会看的邮箱,别填一个早就不用的旧地址。
改进版代码:只在该提醒的时候提醒
我按「少打扰、不泄密」重写了一版,逻辑是这样的:
- 登录成功: 只盯管理员账号,而且只有从没用过的 IP 登录时才发邮件。你在家、在公司天天登,不会收到任何邮件。哪天换了个陌生地方登录,邮件马上到。
- 登录失败: 不再一次一封,改成累计计数。短时间内失败超过 20 次才发一封汇总告警,之后一小时内不再重复发。攻击一直持续的话,每小时最多收到 1 封。
- 任何邮件里都不出现密码。
代码如下。如果你用 Code Snippets 插件,直接粘进去就行,不要带开头的 <?php:
/**
* WordPress 登录邮件提醒(改进版)
* 1. 管理员从新 IP 登录时发邮件
* 2. 登录失败过多时发汇总告警,1 小时最多 1 封
* 邮件里不包含任何密码
*/
// 获取访客 IP。网站接入了 Cloudflare 的话,把 false 改成 true
function ctz_login_client_ip() {
$behind_cloudflare = false;
$ip = isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
if ( $behind_cloudflare && ! empty( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) {
$ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
}
$ip = filter_var( wp_unslash( $ip ), FILTER_VALIDATE_IP );
return $ip ? $ip : '未知';
}
// 一、管理员新 IP 登录提醒
add_action( 'wp_login', 'ctz_notify_admin_new_ip_login', 10, 2 );
function ctz_notify_admin_new_ip_login( $user_login, $user ) {
if ( ! user_can( $user, 'manage_options' ) ) {
return; // 只关心管理员
}
$ip = ctz_login_client_ip();
$known = get_user_meta( $user->ID, '_ctz_known_login_ips', true );
if ( ! is_array( $known ) ) {
$known = array();
}
if ( in_array( $ip, $known, true ) ) {
return; // 常用 IP,不打扰
}
// 记住最近 10 个登录过的 IP
$known[] = $ip;
update_user_meta( $user->ID, '_ctz_known_login_ips', array_slice( $known, -10 ) );
$site = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
$ua = isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '未知';
$body = sprintf(
"管理员账号 %s 刚刚从一个新的 IP 登录了后台。\n\n网站:%s\n时间:%s\nIP:%s\n浏览器:%s\n\n是你本人就忽略这封邮件。\n不是你的话,马上修改密码,并检查用户列表里有没有多出陌生的管理员。",
$user_login,
home_url(),
wp_date( 'Y-m-d H:i:s' ),
$ip,
$ua
);
wp_mail( get_option( 'admin_email' ), "[{$site}] 管理员新 IP 登录提醒", $body );
}
// 二、登录失败过多时发汇总告警
add_action( 'wp_login_failed', 'ctz_alert_login_failures', 10, 1 );
function ctz_alert_login_failures( $username ) {
$threshold = 20; // 累计失败多少次才提醒
$count = (int) get_transient( 'ctz_login_fail_count' ) + 1;
set_transient( 'ctz_login_fail_count', $count, 15 * MINUTE_IN_SECONDS );
if ( $count < $threshold || get_transient( 'ctz_login_fail_alerted' ) ) {
return;
}
set_transient( 'ctz_login_fail_alerted', 1, HOUR_IN_SECONDS );
$site = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
$body = sprintf(
"后台登录失败已经累计 %d 次,很可能有人在批量试密码。\n\n网站:%s\n最近一次尝试的用户名:%s\n最近一次尝试的 IP:%s\n时间:%s\n\n这封告警一小时内不会重复发送。\n建议确认登录次数限制和双重验证都已开启。",
$count,
home_url(),
sanitize_text_field( $username ),
ctz_login_client_ip(),
wp_date( 'Y-m-d H:i:s' )
);
wp_mail( get_option( 'admin_email' ), "[{$site}] 后台登录失败告警", $body );
}
有几处细节,我解释一下为什么这么写。
邮件用纯文本,不用 HTML。 登录失败邮件里的「用户名」是攻击者随便填的,什么字符都可能有。老代码直接把它拼进 HTML 邮件,相当于让陌生人往你的邮件里写代码。纯文本最省心。
$behind_cloudflare 只有真接了 Cloudflare 才改成 true。 CF-Connecting-IP 这个请求头谁都能伪造。没走 Cloudflare 的站信任它,攻击者就能随便冒充一个 IP。
IP 只记最近 10 个。 家里宽带隔几天换一次 IP 的话,偶尔会收到「新 IP」提醒,这是正常的。嫌多的话可以把 -10 改大一点,比如 -30。
$threshold = 20 按自己的情况调。 小站平时几乎没人试密码,可以调低到 10。天天被扫的站调高一点,免得太频繁。
代码放哪里:Code Snippets 比 functions.php 安全
老教程都让你把代码贴进主题的 functions.php。这有两个问题:主题一更新,代码就没了;少一个分号,整站就白屏。
我更推荐 Code Snippets 插件。官方页面显示它有 100 万+ 活跃安装,当前版本 4.0.0,测试兼容到 WordPress 7.1.3。装好后:
- 在后台左侧找到 Snippets 菜单,新建一个片段。
- 标题随便起,比如「登录邮件提醒」。
- 把上面的代码粘进去。
还不会添加代码可以参考:Code Snippets插件教程
运行位置一定要选「everywhere(全站运行)」,别选「仅后台」。 原因是登录页 wp-login.php 在 WordPress 眼里不算「后台」。选了仅后台,代码在登录的那一刻根本不会加载,邮件自然也不会发。这个坑我自己踩过,排查了好半天。
另外,这个插件自带安全模式(Safe Mode)。真遇到片段出错进不了后台,可以靠它把片段停掉,比在 functions.php 里写崩了好救得多。保险起见,加代码前还是先备份一下,方法看 WordPress 自动备份的 3 种方案。
装好之后,怎么测试有没有生效?
测登录成功提醒:
- 退出后台。
- 把手机热点打开给电脑用,或者直接用手机流量打开后台。总之换一个从没用过的 IP。
- 登录后一两分钟内,邮箱应该收到「管理员新 IP 登录提醒」。
装好代码后的第一次登录也会收到一封,因为这时候它还一个 IP 都不认识,这是正常的。
测登录失败告警:先临时把 $threshold 改成 3,退出后故意输错 3 次密码,应该会收到一封告警。测完记得把数字改回去。
还有一点要知道:勾选了「记住我」保持登录状态的话,之后打开后台不算重新登录,不会触发提醒。提醒只在真正输入账号密码登录的那一刻发出。
收到一封不是你登录的提醒,该怎么办?
真收到了,别慌,按顺序做三件事:
- 立刻改密码。 能进后台就在个人资料里改。已经进不去了,可以在宝塔里改或者直接改数据库,三种方法都在这篇里:WordPress 管理员密码修改和重置的 3 种方法。
- 检查用户列表。 看有没有多出来的管理员账号,有就删掉。顺便看看现有的账号,有没有谁的权限给大了。各角色该给多大权限,参考 WordPress 用户角色和权限分配详解。
- 查最近改过的插件和文件。 对方登录后最常干的事,就是装个后门插件,或者改主题文件。
不过也别草木皆兵。我遇到过好几次,提醒是外包同事登录触发的。所以找人临时进后台干活,最好用临时免密登录链接单独开账号。收到提醒一看用户名,就知道是谁,不用一惊一乍。
至于登录失败:提醒只是看见,拦截才是正事
登录失败告警的作用是告诉你「有人在试」。但光知道没用,真正该做的是让对方试几次就被关在门外。
所以这段代码最好配合限制登录次数一起用。参数怎么设、用了 Cloudflare 以后要改哪个 IP 选项,都在这篇里:Limit Login Attempts 防暴力破解配置教程。两者配合起来,脚本被限制插件拦住,你每小时最多收到一封汇总邮件,心里有数就行。
不想加代码?两个插件替代方案
已经装了 Wordfence 的话,不用再加代码。 进入「Wordfence → All Options」,找到「Email Alert Preferences」,勾选这两项:
- 「Alert me when someone with administrator access signs in」:有管理员登录时提醒我。
- 「Only alert me when that administrator signs in from a new device or location」:只在该管理员从新设备或新地点登录时提醒。
第二项一定要一起勾上,不然你每次登录都会收到邮件。勾好后效果和上面的改进版代码差不多,免费版就能用。同一个位置还能开普通用户的登录提醒,会员站可以按需打开。
想看完整的登录记录,可以用 WP Activity Log。 它有 30 万+ 活跃安装,免费版就能记录登录、退出和登录失败,事后能查到谁在什么时间、从哪个 IP 进来过。但它的邮件、短信通知属于付费功能,免费版只能自己进后台看日志。
三种方案放一起对比:
| 方案 | 费用 | 新 IP 才提醒 | 登录失败告警 | 历史日志 | 适合谁 |
|---|---|---|---|---|---|
| 改进版代码 | 免费 | 支持 | 支持(汇总,不刷屏) | 不支持 | 不想多装插件、愿意粘贴代码的人 |
| Wordfence | 免费版可用(不确定) | 支持(新设备或地点) | 另有锁定通知 | 有实时流量记录 | 已经装了 Wordfence 的人 |
| WP Activity Log | 记录免费,通知付费 | 付费版支持 | 付费版支持 | 免费版完整 | 多人维护、需要审计记录的站 |
我自己的站用的是代码方案,轻、可控,邮件内容也能按自己的习惯改。客户站如果本来就装了 Wordfence,我就直接勾选项,不再叠代码,免得一次登录收到两封邮件。
邮件能不能稳定送达,说到底还跟主机的发信环境有关。有的主机直接封掉 25 端口,有的 IP 已经进了黑名单。还在选主机的话,可以看看 WordPress 主机推荐汇总里各家的情况。