关键不是“删掉”,而是“根本别让它进去”。Docker镜像每一层不可变,即使RUN rm也无法抹除历史;必须前置防护:禁用ENV/RUN硬编码密码、避免curl/echo含凭证命令,改用BuildKit --secret临时注入,结合多阶段构建只复制安全产物,并严格配置.dockerignore排除敏感文件。

关键不是“删掉”,而是“根本别让它进去”。Docker 镜像的每一层都不可变,一旦写入,即使后续 RUN rm -f 也抹不掉历史记录。所以防护必须前置到构建逻辑本身。
禁用 ENV 和 RUN 中硬编码密码
ENV DB_PASSWORD=abc123 或 RUN export API_KEY=xxx 这类写法会把明文直接存进镜像元数据,docker inspect 或 docker history 一眼可见。这类操作必须禁止。
- 敏感值一律不通过 ENV、ARG(除非确认非敏感)、RUN echo 等方式写入
- 非敏感参数(如版本号、开关标志)可用 ARG,但不能用于传递密钥或密码
- 避免在 RUN 命令中拼接含凭证的 curl/wget/echo 命令
用 BuildKit --secret 安全注入构建时凭据
当构建过程确实需要访问私有仓库、下载带认证的依赖时,应使用 Docker BuildKit 的 secret 挂载机制,它只在构建容器内存中临时存在,进程退出即销毁,不会落盘、不写入任何镜像层。
- 启用 BuildKit:环境变量 DOCKER_BUILDKIT=1
- 构建命令示例:docker build --secret id=ssh_key,src=$HOME/.ssh/id_rsa .
- Dockerfile 中使用:RUN --mount=type=secret,id=ssh_key ssh-add /run/secrets/ssh_key && git clone ...
多阶段构建剥离中间敏感产物
即使用了 --secret,如果在 builder 阶段生成了含密码的配置文件(如 application.yml),又没清理干净,该文件仍可能被 COPY 到最终镜像里。
- 构建阶段生成配置后,不要直接 COPY 整个目录;应只 COPY 必需的二进制、静态资源或脱敏后的配置
- 可动态生成运行时配置,例如:RUN cat <<EOF > config/app.conf\ndb.url=${DB_URL}\n# 不写 password 字段\nEOF
- 最终 runtime 阶段不继承 builder 的文件系统,只用 COPY --from=builder 显式复制安全内容
严格管控构建上下文与 .dockerignore
很多泄露源于 COPY . . 把本地 .env、secrets.json、config.local.yml 一并打包进去。.dockerignore 是第一道过滤网,必须显式排除所有风险项。
- 典型需排除项:.env, *.local, secrets/**, config/*_prod.*, **/id_rsa*, .git, node_modules
- 注意通配符行为:以 / 开头表示从构建上下文根匹配;无 / 是路径内模糊匹配
- 验证是否生效:用 docker build --no-cache -f - . < /dev/null 测试空构建上下文是否仍有意外文件被识别


















