本文目录导读:

针对PHP项目的镜像安全,核心在于构建一个最小、不可变、经过安全加固的容器镜像,以下是从基础镜像选择、依赖管理、运行时配置到持续扫描的全流程安全实践指南。
选择可靠的基础镜像
- 官方与轻量级优先:
- 首选
php:8.3-fpm-alpine或php:8.3-cli-alpine,Alpine Linux 体积小(约5MB),攻击面远小于 Debian/Ubuntu。 - 避免使用
latest、slim或stretch等模糊或过时的标签,应始终指定具体版本号(如3.13)。
- 首选
- 验证镜像签名:
- 使用
docker trust inspect --pretty php:8.3-fpm-alpine检查镜像是否被签名和推送。 - 或者直接对比官方提供的 SHA256 校验和。
- 使用
依赖与文件管理(最小化原则)
- 安装最小依赖:
- 只安装运行时必需的 PHP 扩展(如
pdo_mysql、redis、opcache)。--no-dev选项排除 Composer 开发依赖。 - 使用
apt-get或apk add时,务必合并命令并清理缓存。关键命令:RUN apk add --no-cache --virtual .build-deps $PHPIZE_DEPS \ && docker-php-ext-install pdo_mysql \ && apk del .build-deps
- 只安装运行时必需的 PHP 扩展(如
- 清理构建产物的正确做法:
- 使用多阶段构建,第一阶段安装所有编译工具(如
gcc、make),第二阶段仅复制最终产物(composer install --no-dev后的vendor目录和应用代码)。 - 不要在运行镜像中保留 Git 历史、
.env.example、测试文件、文档或调试工具(如xdebug)。
- 使用多阶段构建,第一阶段安装所有编译工具(如
- 避免根用户运行:
- 创建非 root 用户运行 PHP-FPM。核心安全改动:
RUN addgroup -g 1000 -S appuser && adduser -u 1000 -S appuser -G appuser USER appuser
- 创建非 root 用户运行 PHP-FPM。核心安全改动:
PHP 运行时安全配置
通过环境变量或 php.ini 配置进行加固,可在 Dockerfile 或 docker run 中使用 -e 设置。
- 禁用危险函数:
- 在
php.ini中添加:disable_functions = exec,system,passthru,shell_exec,popen,proc_open,pcntl_exec,show_source,highlight_file,readfile
- 在
- 限制资源与关闭信息泄露:
expose_php = Off # 隐藏 PHP 版本号 display_errors = Off # 生产环境关闭错误显示 allow_url_fopen = Off # 如非必要关闭远程文件包含 cgi.fix_pathinfo = 0 # 防止路径解析漏洞(nginx场景必需)
镜像构建与扫描(CI/CD 集成)
- 使用安全扫描工具:
- Trivy:最推荐,免费、快、支持多种语言。
trivy image your-image:tag - Snyk:
docker scan your-image:tag(需登录) - Grype:Anchore 出品,与 Syft 配合良好。
- 集成到 CI(GitHub Actions/GitLab CI):构建后立即扫描,高危漏洞数 > 0 则构建失败。
- Trivy:最推荐,免费、快、支持多种语言。
- 避免 Hardcoded Secrets:
- 绝对不要在 Dockerfile 中写
ENV DB_PASSWORD=xxx。 - 使用 Docker Secrets(Swarm)、Kubernetes Secrets 或外部 Vault(如 HashiCorp Vault)。
- 推荐做法:运行时通过
--env-file .env.prod注入,并将.env文件加入.dockerignore。
- 绝对不要在 Dockerfile 中写
部署与运行时防护
- 镜像标签策略:
- 使用不可变标签:
v1.2.3-b430(应用版本 + 构建号)。 - 禁止覆盖
latest
- 使用不可变标签:
- 只读文件系统:
docker run --read-only --tmpfs /tmp --tmpfs /var/run使容器根文件系统只读,只允许写入临时目录,这能有效阻止 Webshell 写入文件。
- 使用非 root 用户运行(再次强调):
- 在 Docker Compose 或 K8s Pod 的
securityContext中设置runAsUser: 1000。
- 在 Docker Compose 或 K8s Pod 的
- 漏洞持续监测:
在镜像仓库(Docker Hub/ECR/Harbor)定期扫描已部署的镜像,许多仓库支持定时自动扫描。
完整 Dockerfile 示例(结合上述要点)
# 1. 多阶段构建
FROM composer:2.7 AS composer
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-interaction --no-progress
# 2. 安全运行镜像
FROM php:8.3-fpm-alpine
# 3. 创建非 root 用户
RUN addgroup -g 1000 -S appuser && adduser -u 1000 -S appuser -G appuser
# 4. 安装最小必需 PHP 扩展(MySQL/MariaDB 支持)
RUN set -eux; \
apk add --no-cache --virtual .build-deps \
$PHPIZE_DEPS \
libzip-dev \
; \
docker-php-ext-install pdo_mysql opcache; \
apk del .build-deps; \
rm -rf /var/cache/apk/*
# 5. 复制生产代码(从 composer 阶段复制 vendor,从本地复制源代码)
COPY --chown=appuser:appuser --from=composer /app/vendor/ /var/www/html/vendor/
COPY --chown=appuser:appuser . /var/www/html/
# 6. 安全配置 PHP 生产设置(覆盖默认 php.ini)
COPY docker-php.ini /usr/local/etc/php/conf.d/docker-php.ini示例:
# disable_functions = exec,system,passthru,shell_exec,popen,proc_open,pcntl_exec,show_source,highlight_file,readfile
# expose_php = Off
# allow_url_fopen = Off
# 7. 移除不必要的文件(非必须,但建议在 COPY 后执行)
RUN rm -rf /var/www/html/.env.example /var/www/html/tests /var/www/html/docs
# 8. 切换到非 root 用户
USER appuser
EXPOSE 9000
CMD ["php-fpm"]
总结关键点
- 基础镜像最小化:Alpine 且指定确切版本。
- 依赖最小化:通过多阶段构建和
--no-dev减少攻击面。 - 权限最小化:非 root 用户 + 只读文件系统。
- 配置与扫描自动化:在 CI 中集成 Trivy,构建失败阈值设为高危0。
- 秘密管理:永远不要硬编码,使用环境变量和运行时注入。
遵循以上策略,你的 PHP 镜像将具备生产级别的安全韧性,能有效防御常见的注入、文件包含和权限提升攻击。