搜索全站
安全防护 17 分钟阅读

WordPress 登录页定制:修改 Logo、链接与自定义登录界面

默认登录页暴露了 WordPress 特征,也是撞库攻击的入口。本文给出定制登录页的完整代码:改 Logo 与跳转链接、自定义 CSS 样式、登录后的重定向规则,并说明修改登录地址的真实安全收益与风险。

U
UFO_PF 2017-08-21 · 阅读 17 分钟
Wo 安全防护
阅读提示
本文约 6,640 字,预计阅读 17 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:登录页是站点最该收口的地方

/wp-login.php 是全网被扫描最多的路径之一。任何一台自动化扫描器,发现 WordPress 站点后第一件事就是 POST 到这个地址做撞库。

所以登录页有两个层次的需求:

  1. 品牌化:客户看到千篇一律的 WordPress Logo 会觉得不专业,换成自己的 Logo 和配色;
  2. 安全收口:隐藏或保护登录入口,降低被爆破的概率。

这两件事都能用代码实现,不需要装插件(装插件反而多一个攻击面)。

原理分析:登录页为什么可以被改

wp-login.php 输出时提供了丰富的钩子:

  • login_head —— 在 <head> 里输出内容;
  • login_headerurl —— Logo 的链接地址(默认指向 wordpress.org);
  • login_headertext —— Logo 的 title 属性;
  • login_enqueue_scripts —— 加载自定义 CSS/JS;
  • login_message —— 登录表单上方的提示文字;
  • login_redirect —— 登录后跳转地址;
  • login_footer —— 页脚区域。

默认 Logo 是 CSS 背景图,来自 wp-admin/images/w-logo-blue.png(部分版本是 SVG)。所以”改 Logo”本质是覆盖 CSS 的 background-image。

注意一个技术细节:这些钩子只在 wp-login.php 页面触发,所以在主题 functions.php 里写是可以的,但登录页不会加载主题的常规 CSS。登录页加载的是 wp-admin/css/login.css(核心样式)+ 你通过钩子加的样式。

保姆级实操

一、改 Logo 和链接

把下面代码放进子主题 functions.php(或自建功能插件):

// 改 Logo 图片(建议用站点 logo,尺寸 84x84 左右,透明底 PNG)
add_action( 'login_enqueue_scripts', function() { ?>
<style type="text/css">
    #login h1 a, .login h1 a {
        background-image: url(<?php echo esc_url( get_stylesheet_directory_uri() . '/assets/images/login-logo.png' ); ?>);
        background-size: contain;
        background-position: center;
        width: 180px;
        height: 84px;
        background-repeat: no-repeat;
        padding-bottom: 20px;
    }
    body.login {
        background: #f4f6f8;
    }
    .login form {
        border-radius: 8px;
        box-shadow: 0 2px 12px rgba(0,0,0,.06);
    }
    .wp-core-ui .button-primary {
        background: #c0392b;
        border-color: #c0392b;
    }
    .wp-core-ui .button-primary:hover {
        background: #a5301f;
        border-color: #a5301f;
    }
    .login #backtoblog a, .login #nav a {
        color: #555;
    }
</style>
<?php } );

// Logo 链接指向站点首页
add_filter( 'login_headerurl', function( $url ) {
    return home_url();
} );

// Logo 的 title 文字
add_filter( 'login_headertext', function( $text ) {
    return get_bloginfo( 'name' );
} );

二、登录后按角色跳转到不同页面

add_filter( 'login_redirect', function( $redirect_to, $request, $user ) {
    if ( isset( $user->roles ) && is_array( $user->roles ) ) {
        if ( in_array( 'administrator', $user->roles, true ) ) {
            return admin_url();                 // 管理员进后台
        }
        if ( in_array( 'subscriber', $user->roles, true ) ) {
            return home_url( '/member/' );      // 订阅者进会员中心
        }
        return home_url( '/account/' );         // 其他进账户页
    }
    return $redirect_to;
}, 10, 3 );

三、登出后回首页(而不是登录页)

add_action( 'wp_logout', function() {
    wp_safe_redirect( home_url() );
    exit;
} );

四、登录页加提示文字

add_filter( 'login_message', function( $message ) {
    $notice = '<p class="message">请使用本站账号登录,遇到问题请联系管理员。</p>';
    return $notice . $message;
} );

五、登录失败时不提示具体原因(防爆破信息泄露)

默认登录失败会提示”用户名无效”或”密码错误”,等于告诉攻击者用户名是否存在。统一成模糊提示:

add_filter( 'login_errors', function( $error ) {
    return '用户名或密码不正确。';
} );

六、彻底自定义登录页(独立页面)

如果要做完全独立的登录页(带品牌背景、注册引导),建议做一个页面模板:

/*
 * Template Name: 自定义登录
 */
get_header();

if ( is_user_logged_in() ) {
    echo '<p>您已登录,<a href="' . esc_url( wp_logout_url( home_url() ) ) . '">退出</a></p>';
    get_footer();
    return;
}

$err = '';
if ( 'POST' === $_SERVER['REQUEST_METHOD'] && isset( $_POST['mmf_login_nonce'] ) ) {
    if ( ! wp_verify_nonce( $_POST['mmf_login_nonce'], 'mmf_login' ) ) {
        $err = '请求已过期,请刷新重试。';
    } else {
        $creds = array(
            'user_login'    => sanitize_user( $_POST['log'] ),
            'user_password' => $_POST['pwd'],
            'remember'      => isset( $_POST['rememberme'] ),
        );
        $user  = wp_signon( $creds, false );
        if ( is_wp_error( $user ) ) {
            $err = '用户名或密码不正确。';
        } else {
            wp_safe_redirect( home_url( '/member/' ) );
            exit;
        }
    }
}
?>
<div class="mmf-login-wrap">
    <?php if ( $err ) : ?>
        <p class="error"><?php echo esc_html( $err ); ?></p>
    <?php endif; ?>
    <form method="post">
        <?php wp_nonce_field( 'mmf_login', 'mmf_login_nonce' ); ?>
        <p><input type="text" name="log" placeholder="用户名或邮箱" required></p>
        <p><input type="password" name="pwd" placeholder="密码" required></p>
        <p><label><input type="checkbox" name="rememberme"> 记住我</label></p>
        <p><button type="submit">登录</button></p>
    </form>
</div>
<?php get_footer();

七、修改登录地址(要不要做)

装插件 “WPS Hide Login” 或 “Rename wp-login.php”,把 /wp-login.php 改成 /my-secret-entry/。

真实收益:能减少 90% 以上的自动化撞库。安全通过隐藏(security by obscurity)不是真正的防护,但对付无差别扫描确实有效。

风险与注意:

  • 改完自己也要记住新地址,建议存密码管理器;
  • 插件停用了要能恢复,否则可能进不去后台;
  • 有些插件/主题硬编码了 wp-login.php 链接,改地址后这些链接会 404;
  • 不要以为改了地址就安全了,双因素认证和强密码才是根本。

更稳妥的替代方案:不改地址,而是加一层 HTTP 认证(Nginx/Apache 层),或者用 Cloudflare WAF 限制登录页的访问地区/IP。

location ~* ^/wp-login\.php$ {
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd;
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}

注意:加了 HTTP Basic Auth 后,部分 REST API 请求会被挡,导致区块编辑器”更新失败”。要在 auth 规则里放行 /wp-json/ 和 /wp-admin/admin-ajax.php。

常见踩坑与失败案例

  1. 案例一:改了 Logo 后图片不显示。原因是 get_stylesheet_directory_uri() 指向子主题,图片放错了目录。用浏览器开发者工具看实际请求路径就能定位。
  2. 案例二:用 WPS Hide Login 改了地址,结果自己也忘了,且插件被误停用,彻底进不去后台。恢复方法:通过 FTP 删除 wp-content/plugins/wps-hide-login 目录,地址自动还原为默认。改之前一定要记录新地址。
  3. HTTP Basic Auth 挡掉了 AJAX:后台一堆功能失效(尤其是 admin-ajax.php)。上面给的规则要拆开,只保护 wp-login.php,并放行 admin-ajax。
  4. 自定义登录页没做 nonce:上面代码里的 wp_nonce_field 不能省,否则有 CSRF 风险。
  5. 登录后重定向死循环:login_redirect 里返回的地址如果又要求登录(比如会员页设置成必须登录且跳转回登录页),会无限循环。加判断:

php
if ( strpos( $redirect_to, 'wp-login.php' ) !== false ) {
return home_url();
}

  1. login_enqueue_scripts 里直接 echo CSS 没生效:要用 wp_enqueue_style 或写在 login_head 里。上面的 <style> 写法在 login_enqueue_scripts 钩子里是可行的,但更规范的是:

php
add_action( 'login_enqueue_scripts', function() {
wp_enqueue_style(
'mmf-login',
get_stylesheet_directory_uri() . '/assets/css/login.css',
array(),
'1.0'
);
} );

  1. 缓存插件缓存了登录页:登录页绝对不能被缓存,否则用户 A 看到用户 B 的登录状态。所有缓存插件默认排除 wp-login.php,但如果用了自定义登录页,要手动排除那个页面 URL。

总结

登录页定制分两块:品牌化用 login_enqueue_scripts + login_headerurl + login_headertext 三个钩子就能搞定,代码量二十行;安全收口优先做”登录失败模糊提示 + 强密码 + 双因素”,改登录地址(WPS Hide Login)和 Nginx 层 HTTP 认证作为额外防线。两个必须记住的坑:改地址前把新地址存好并知道插件停用后的恢复方法;加 HTTP 认证时务必放行 /wp-json/ 和 admin-ajax.php,否则后台大面积功能失效。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。