对于刚接触 Docker 的开发者来说,COPY和ADD可能是最容易被混用的两条指令——它们都能将文件从宿主机复制到 Docker 镜像中。但你可能没意识到:
看似功能重叠的它们,隐藏着截然不同的安全风险
。本文将从底层逻辑出发,拆解两条指令的设计差异,结合真实场景的安全隐患,告诉你为什么在大多数情况下,COPY才是更安全的选择。
一、基础认知:COPY 与 ADD 的功能边界
在 Dockerfile 中,COPY和ADD的核心目标都是“将文件/目录复制到镜像中”,但官方文档对它们的定义有明确区分:
-
COPY:仅支持本地文件/目录的复制,行为是“纯拷贝”——不会对源文件做任何额外处理(如解压、校验)。
-
ADD:除了本地文件复制,还支持两个“扩展功能”:
-
自动解压:若源文件是压缩包(如.tar、.gz等),ADD会自动解压到目标路径;
-
远程下载:若源文件是 URL 链接,ADD会从网络下载文件到镜像中(需注意:此功能已被标记为“过时”)。
二、ADD 的隐性风险:自动解压与远程下载的“双刃剑”
ADD的“扩展功能”看似方便,却埋下了两大安全隐患。我们通过两个真实场景来理解:
场景 1:自动解压可能触发恶意代码执行
假设你从第三方获取了一个压缩包data.tar,想将其复制到镜像中。如果使用ADD data.tar /app,Docker 会自动解压data.tar到/app目录。但如果这个压缩包被恶意构造——比如内部包含一个evil.sh脚本,且解压后脚本获得了可执行权限,那么后续镜像运行时,这个脚本可能被意外触发。
关键问题:ADD的自动解压是“静默”的。开发者可能误以为只是复制了一个压缩包,实则压缩包内的文件已被释放到镜像层中,且权限可能与预期不符。而用COPY data.tar /app则完全不同:压缩包会被原样复制到镜像中,不会自动解压。后续若需要解压,开发者可以显式执行RUN tar -xf /app/data.tar -C /app,此时对解压后的文件权限、内容可完全可控。
场景 2:远程 URL 下载的“不可控性”
早期ADD支持从 URL 下载文件(如ADD https://example.com/file.zip /tmp),但这一功能已被 Docker 官方标记为“过时”(deprecated)。原因在于:
-
中间人攻击:下载过程中若网络被劫持,可能获取到被篡改的文件(如植入恶意代码的二进制文件);
-
依赖链不透明:镜像构建时无法追溯文件的原始来源和完整性(除非手动校验哈希值);
-
缓存污染:若 URL 内容变化,可能导致不同构建的镜像层不一致,增加调试难度。
即使不考虑安全性,ADD的远程下载功能也不如curl或wget命令灵活——后者支持断点续传、自定义请求头、校验哈希等操作,更适合作为构建流程的一部分。
三、COPY 的安全优势:“显式”即“可控”
与ADD相比,COPY的设计哲学是“最小化隐式行为”。它只做一件事:
将本地文件/目录原样复制到镜像中。这种“显式”特性带来了三重安全保障:
1. 避免意外的文件修改
COPY不会修改源文件的内容或属性(如权限、时间戳)。例如,若宿主机上的config.ini是只读文件,COPY后镜像中的文件仍保持只读;而ADD若触发了自动解压,可能因解压逻辑修改文件权限(如默认赋予可执行权限)。
2. 清晰的构建上下文
Docker 构建时依赖“构建上下文”(即docker build命令指定的目录)。COPY的源文件必须位于构建上下文中,这避免了因路径错误导致的“意外复制”(比如误将宿主机敏感文件加入上下文并被复制到镜像)。
3. 符合“最小权限原则”
安全领域的核心原则之一是“最小权限”——只赋予必要的能力。COPY仅提供复制功能,不额外开放解压或远程下载的权限,从根本上减少了攻击面。
四、何时该用 ADD?例外场景与安全实践
当然,ADD并非完全无用。在以下场景中,它仍有存在的价值,但需格外注意安全:
场景 1:需要自动解压的合法压缩包
如果你明确知道压缩包来源可靠(如官方发布的应用包),且需要快速解压到镜像中,可以使用ADD。但建议:
-
提前校验压缩包的哈希值(如 SHA256);
-
解压后检查文件列表,确认无异常文件;
-
避免直接解压到系统关键目录(如/usr/bin)。
场景 2:遗留镜像的兼容性维护
若维护的老项目 Dockerfile 已大量使用ADD,且无安全风险,可暂时保留。但新功能开发时,应逐步替换为COPY+ 显式解压命令(如RUN tar -xf)。
五、总结:安全从“显式”开始
回到最初的问题:
为什么 COPY 比 ADD 更安全? 答案很简单——COPY用“显式的简单”规避了ADD“隐式的复杂”带来的风险。对于大多数开发者而言,除非明确需要ADD的自动解压功能(且能控制风险),否则应优先使用COPY。这不是教条,而是 Docker 设计者对“安全”与“简洁”的权衡。下次写 Dockerfile 时,不妨多问自己:“我真的需要 ADD 的‘额外能力’吗?还是说,COPY 已经足够?”——安全,往往藏在细节的选择里。
附:Dockerfile 最佳实践示例 # 推荐:用 COPY 复制本地文件(显式、可控)
COPY ./src /app/src
COPY config.ini /app/config/
# 特殊场景:需要解压时,用 COPY + 显式解压命令
COPY data.tar.gz /tmp/
RUN tar -xzf /tmp/data.tar.gz -C /app/data && \
rm -f /tmp/data.tar.gz # 解压后删除原压缩包,减少镜像体积
# 避免使用:ADD 的远程下载(已过时且不安全)
# 不推荐:ADD https://example.com/file.zip /tmp/
通过这篇文章,希望你能理解COPY与ADD的本质差异,并在实践中养成更安全的镜像构建习惯。