结论:5分钟内可完成基础加固,只需修改config.inc.php和容器启动参数。具体包括:Nginx反代限制IP、禁用UploadDir/SaveDir、关闭空密码登录、禁用创建数据库权限、正确配置2FA及专用数据库账号权限。
直接上结论:只改 config.inc.php 和容器启动参数,不碰镜像层,5 分钟内可完成基础加固。 其他“重写 dockerfile”“自建镜像”“编译 php 扩展”等做法,在生产快速响应场景下属于过度设计,反而增加维护负担和出错概率。
限制访问来源:用 Nginx 反向代理比改 Apache 配置更稳
phpMyAdmin 官方镜像默认用 Apache,但它的 <directory></directory> 访问控制在容器里容易失效(尤其配合 --link 或网络模式时)。更可靠的做法是:在容器前加一层 Nginx,把 phpMyAdmin 容器当作后端服务代理。
- 用
nginx:alpine镜像起一个反代容器,只暴露 443 端口 - 在 Nginx 配置中硬编码允许的 IP 段:
allow 203.0.113.0/24;,最后加deny all; - 禁用对
/setup、/libraries、/templates的直接访问,防止路径遍历或配置泄露 - 别依赖容器内 Apache 的
AllowOverride—— 它在官方镜像里默认关着,且.htaccess文件根本没挂载进去
关闭危险功能:删掉 config.inc.php 里的 upload 功能开关
phpMyAdmin 默认允许上传 SQL 文件($cfg['UploadDir'])和导出为可执行格式(如 excel、latex),这些在多数运维场景下纯属冗余,还可能被用于 RCE 或 SSRF。
- 在
config.inc.php中显式禁用:$cfg['UploadDir'] = '';和$cfg['SaveDir'] = ''; - 删掉或注释掉所有
$cfg['Servers'][$i]['AllowNoPassword'] = true;行 —— 即使你用环境变量传了密码,这条也会让空密码登录绕过验证 - 禁用创建数据库权限:
$cfg['Servers'][$i]['DisableIS'] = true;(禁用 information_schema 探测) +$cfg['ShowDatabasesCommand'] = 'SHOW DATABASES WHERE `Database` NOT IN ("mysql", "information_schema", "performance_schema", "sys")'; - 别信“只读用户就安全”—— phpMyAdmin 的 UI 层本身就有逻辑漏洞(如 CVE-2018-12613),限制功能面比依赖 DB 权限更有效
启用 2FA 前必须确认三件事:版本、扩展、配置位置
phpMyAdmin 4.9+ 才支持原生 2FA,但装完扩展、升级完版本,仍大概率失败。问题几乎全出在三个被忽略的细节上。
- 确认实际运行的 phpMyAdmin 版本:
docker exec -it phpmyadmin php -r "require('/var/www/html/libraries/Config.class.php'); echo PMAM_VERSION;"—— 不要看镜像 tag,要看容器内真实加载的代码 - 检查 CLI 和 Web 环境下的扩展是否一致:
docker exec -it phpmyadmin php -m | grep -E '^(gmp|openssl)$',再对比docker exec -it phpmyadmin curl -s http://localhost/index.php?lang=en | grep -o 'gmp.*enabled'(后者模拟 Web 请求) -
$cfg['TwoFactor'] = true;必须写在config.inc.php的全局作用域,不能嵌套在$cfg['Servers'][1]里,也不能拼错成$cfg['two_factor'](大小写敏感) - 绑定失败时先同步时间:
docker exec -it phpmyadmin apk add --no-cache ntpdate && ntpdate -s pool.ntp.org—— TOTP 对时间偏差超 30 秒即失效
别忽略数据库用户权限与连接方式的耦合风险
很多人以为“给 phpMyAdmin 容器配个只读账号就万事大吉”,但 phpMyAdmin 的某些操作(如刷新权限、查看进程列表、修改变量)会隐式触发高权限 SQL,一旦账号权限不足,页面报错反而暴露后端结构。
立即学习“PHP免费学习笔记(深入)”;
- 用
PMA_USER和PMA_PASSWORD启动容器时,该账号必须有SELECT、SHOW DATABASES、SHOW VIEW、LOCK TABLES,否则部分页面白屏或 JS 报错 - 禁止用 root 连接:
PMA_HOST指向的 MySQL 容器里,root 用户应仅监听127.0.0.1,对外暴露的只能是专用账号(如pma_app) - 如果 MySQL 容器开了
--bind-address=0.0.0.0,必须在 MySQL 侧限制该账号来源:CREATE USER 'pma_app'@'172.18.0.%' IDENTIFIED BY 'xxx';(对应 Docker 默认 bridge 网段) - 环境变量方式传密钥虽方便,但会出现在
docker inspect输出里 —— 敏感环境建议用docker secret或挂载加密配置文件
真正难的不是加功能,而是判断哪些配置项在容器生命周期里会动态覆盖(比如官方镜像启动脚本会重写 config.inc.php 里的 $cfg['Servers'] 段),以及哪些错误日志根本不会打到 stdout(比如 OpenSSL 初始化失败只写 Apache error log,而该日志默认不输出到控制台)。



















