数据库管理面板别暴露公网:Adminer 与 phpMyAdmin 收口、SSH 隧道端口转发与 DBeaver 连接实战

为什么不该把 phpMyAdmin 直接暴露在公网

个人站长管数据库,十有八九用过 phpMyAdmin:点点鼠标就能看表、改字段、导出 SQL,比命令行友好太多。于是很多人图省事,直接把 phpMyAdmin 装到网站根目录,挂个 /pma 路径,公网随便访问。这件事的风险远超大多数人想象——phpMyAdmin 是历史上被扫描和爆破最狠的 Web 应用之一,几乎每一轮 CVE 出来,全网自动化扫描器几小时内就会覆盖一遍。一个装在公网、密码不强的 phpMyAdmin,等于把数据库的钥匙挂在门上。

正确做法不是不用它,而是让它只对你自己可见。这篇文章讲三件事:怎么用 SSH 隧道把数据库管理工具「藏」在 localhost 后面;phpMyAdmin 与更轻量的 Adminer 怎么选、怎么装;以及一套不暴露端口仍然能在任意地点管理的连接方案。

核心思路:管理入口永远只听 127.0.0.1

先建立一条心智模型。数据库管理工具(phpMyAdmin / Adminer / 桌面客户端)本质上是一个「客户端」,它需要能访问 MySQL 的 3306 端口。

如果直接在 Nginx 里给它配个公网域名,那就是「全网都能访问这个客户端」;而如果我们让它只绑在 127.0.0.1,那就只有「已经能登录这台服务器的人」才能访问它。而「已经能登录服务器」本身就是一道强得多的认证——SSH 有密钥、有 fail2ban、有二次验证。所以把管理入口退回到 localhost,再用 SSH 隧道按需打通,是安全性与便利性兼得的标准答案。

同样的逻辑适用于 MySQL 本身:bind-address = 127.0.0.1 应该永远保持,绝不要在防火墙里给 3306 开一个「临时」的公网白名单——那个白名单最后通常会变成永久。

方案一:Adminer 单文件方案(个人站首选)

Adminer 是一个 PHP 单文件数据库管理工具,只有几百 KB,把文件扔进目录就能用,不需要 Composer、不需要一堆 PHP 扩展。相比 phpMyAdmin,它暴露面小、更新简单(就是覆盖一个文件),非常适合个人站。

mkdir -p /srv/adminer
cd /srv/adminer
wget -O index.php https://github.com/vrana/adminer/releases/latest/download/adminer-4.8.1.php
# 也可以直接用官方英文版:https://www.adminer.org/latest.php

# 关键:目录权限收紧,禁止被 web 用户之外的人读
chown -R www-data:www-data /srv/adminer
chmod 750 /srv/adminer

Nginx 里配置一个只监听本机的 server 块:

server {
    # 只监听 127.0.0.1,公网完全访问不到
    listen 127.0.0.1:8081;
    server_name _;
    root /srv/adminer;
    index index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php-fpm.sock;
        include fastcgi_params;
        # Adminer 若连本机 MySQL,这里必须传对 socket 或 127.0.0.1
    }
}

注意 listen 127.0.0.1:8081; 这一行——只要绑定的是 127.0.0.1,即使服务器防火墙全开,外部也连不上这个端口。这是整个方案安全性的根基,比任何密码都可靠。

方案二:phpMyAdmin 如果一定要用

如果团队协作或功能需求必须用 phpMyAdmin,也可以装,但务必按下面的顺序收口。安装用系统包最省心:

apt install phpmyadmin
# 安装时会问是否用 dbconfig-common 配置数据库,个人站选 No 手动配置更可控

它默认会把配置放到 /etc/phpmyadmin/,Apache 会自动挂载。如果你用的是 Nginx,需要手动加一个只监听本机的 server 块,并把 PHP 转发到 php-fpm:

server {
    listen 127.0.0.1:8082;
    server_name _;
    root /usr/share/phpmyadmin;
    index index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php-fpm.sock;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
    # 禁止访问配置目录
    location ~ ^/(libraries|setup)/ { deny all; }
}

三板斧收口:一是 listen 127.0.0.1;二是删掉或禁用 setup/ 向导(跑完初始配置后直接移除,否则是个未授权写配置的入口);三是把 /usr/share/phpmyadmin 的属主设为 root,web 用户只读,防止被上传木马改写。

方案三:ssh -L 端口转发(本文重点)

服务已经只监听 127.0.0.1,接下来怎么从自己电脑访问?答案是 SSH 本地端口转发。原理很简单:SSH 在你本机和服务器之间建一条加密隧道,把「你本机某个端口」映射到「服务器上的某个地址端口」。

最基本的命令:

# 把本机 8081 映射到服务器的 127.0.0.1:8081
ssh -N -L 8081:127.0.0.1:8081 alice@your-server.com

参数说明:-N 表示只建隧道不执行远程命令;-L 本地端口:目标地址:目标端口,注意目标地址是从服务器视角解析的——写 127.0.0.1 指的是服务器自己的本机。执行后挂住不退出,然后浏览器打开 http://127.0.0.1:8081,看到的其实是服务器上的 Adminer。

更常见的场景是直接连数据库端口,配合桌面客户端(DBeaver、HeidiSQL、Navicat)使用:

# 把本机 13306 映射到服务器 127.0.0.1:3306
ssh -N -L 13306:127.0.0.1:3306 alice@your-server.com

# 客户端连 127.0.0.1:13306,用户名密码还是数据库自己的账号
# 好处:MySQL 从不暴露公网,中间还有一层 SSH 加密

这样配置的好处是:你在 DBeaver 里保存的连接信息永远是 127.0.0.1:13306,不用随服务器 IP 变动而改;而且3306 从始至终没有对公网开放,扫描器扫不到任何东西。DBeaver 还内置了 SSH 隧道选项,可以在连接设置里直接填 SSH 主机和密钥,效果等价,省去手动敲命令。

把隧道写进 SSH Config,一条别名搞定

每次都敲一堆参数太累,写进 ~/.ssh/config:

Host db-tunnel
    HostName your-server.com
    User alice
    IdentityFile ~/.ssh/id_ed25519
    LocalForward 13306 127.0.0.1:3306
    LocalForward 8081 127.0.0.1:8081
    # 断线自动保活,防止隧道静默死掉
    ServerAliveInterval 30
    ServerAliveCountMax 3
    ExitOnForwardFailure yes

之后只需要 ssh db-tunnel 就同时打通了数据库和管理面板两个端口。ExitOnForwardFailure yes 很有用——如果本地端口被占用导致转发失败,SSH 会直接退出报错,而不是悄悄连上却不转发,让你对着一个连不上的端口排查半天。

还有一个稳定性的坑:长连接隧道经常会在网络切换(比如笔记本从 Wi-Fi 换到 4G)后静默失效。ServerAliveInterval 30 会定期发心跳,超时自动断开重连(配合 autossh 可自动重连)。如果只是临时用,手动重敲一次也行;如果要长期挂,用 autossh -M 0 -f -N db-tunnel。

反向场景:从服务器连你本机数据库

远程转发 -R 有时也有用,比如临时把服务器上跑的测试脚本连到你家本机的开发数据库,或者把内网服务的端口暴露出来做联调:

# 把服务器上的 9000 端口反向映射到你本机的 3306
ssh -N -R 9000:127.0.0.1:3306 alice@your-server.com

注意反向转发的目标地址同样是「从建立隧道的那一端(你的本机)视角」解析的。出于安全考虑,默认情况下服务器上的 9000 只绑定在服务器的 127.0.0.1,外部访问不到;如果需要让外部可达,要在服务端 sshd_config 里设 GatewayPorts yes——除非明确需要,不要开这个选项。

进阶:动态转发当一个轻量代理用

除了 -L 与 -R,SSH 还有第三种转发模式 -D,它会在本机开一个 SOCKS5 代理端口,把浏览器所有流量按需转发到服务器出口:

# 本机 1080 端口成为一个 SOCKS5 代理
ssh -N -D 1080 alice@your-server.com

浏览器设置 SOCKS5 代理为 127.0.0.1:1080 后,所有请求都会从服务器的 IP 出去。这个技巧在运维里非常实用:比如某个后台面板做了 IP 白名单,只允许服务器自己的 IP 访问,你就可以用动态转发把请求「借道」服务器;又或者要验证某个地区能否正常访问你的站点时,用它做出口对比。它比装一套代理软件轻量得多,用完关掉进程即可,不留任何持久配置。

三种转发模式可以简单记忆:-L 是「我连别人」——本机端口映射到远端;-R 是「别人连我」——远端端口映射到本机;-D 是「动态按需」——本机开 SOCKS 代理,目标由请求决定。三者都建立在那一条已经认证加密的 SSH 连接之上,这也是为什么说 SSH 是运维里「最被低估的瑞士军刀」。

SSH 隧道排错四个常见问题

  • 转发失败 bind: Address already in use:本地端口被占用,换一个端口或先查 lsof -i:13306。
  • 连上了但浏览器打不开:多半是转发的目标地址写错。记住 -L 的目标是从服务器视角解析的,写 localhost 可能解析成 IPv6 的 ::1 而服务只监听 IPv4,改成显式 127.0.0.1。
  • 隧道时通时断:加 ServerAliveInterval;如果是云厂商的跳板,可能中间 NAT 超时,心跳间隔要小于 NAT 超时时间。
  • 能连上但提示 Access denied:那是数据库自身的账号权限问题,不是隧道问题。检查该 MySQL 账号的 host 是否为 localhost,从隧道转过来的连接对 MySQL 而言源地址就是本机。

把管理面板彻底锁死的收尾清单

  • MySQL 的 bind-address 保持 127.0.0.1,永远不要为了图方便开公网 3306。
  • Adminer / phpMyAdmin 的 server 块一律 listen 127.0.0.1,公网域名解析不指向它。
  • 删除 setup/ 目录、限制 libraries/ 访问、web 目录属主设为 root。
  • 数据库账号按最小权限建:只给需要的库、只给需要的操作,不要用 root 登录面板。
  • SSH 侧用密钥登录、关掉密码认证、fail2ban 防爆破。
  • 面板目录加一条 IP 白名单或 HTTP Basic 二重认证(虽然本机监听时已非必需,但多一层不亏)。
  • 定期升级 Adminer/phpMyAdmin 版本,单文件方案升级成本极低,没有理由拖着。

小结

数据库管理这件事,安全性不来自「密码设得多复杂」,而来自「入口有没有暴露在公网」。把 Adminer 或 phpMyAdmin 绑到 127.0.0.1,再用 ssh -L 按需打通隧道,你就同时拿到了两种好处:操作体验和装在公网上完全一样,攻击面却缩小到接近零。个人站长的运维哲学其实就这么简单——能不暴露的端口就不要暴露,能藏在本机后面的入口就藏起来,剩下的交给 SSH 的加密与认证。

Last modification:September 30th, 2026 at 09:24 pm

Leave a Comment