--no-dev 是生产环境强制安全边界,漏用会导致 phpunit、symfony/var-dumper 等 dev 包误入线上,引发 RCE、未鉴权路由、autoload 性能下降等严重风险,而非仅体积或速度问题。

为什么生产环境必须加 --no-dev,而不是“建议”
漏掉 --no-dev 不是部署慢一点或体积大一点的问题,而是直接把 phpunit/phpunit、symfony/var-dumper、barryvdh/laravel-debugbar 这类包装进线上服务器。它们不是“无害的多余文件”,而是带反射、eval、Web 路由、命令行入口的真实攻击面。
- 某些包(如旧版
sebastian/environment)含已知 RCE 漏洞,Composer 会照常装入 autoload 映射,安全扫描工具可能根本扫不到——因为它们不在require里 -
doctrine/doctrine-fixtures-bundle会注册/fixtures/load这类未鉴权路由,上线后等于开后门 - 即使没被主动调用,只要类名出现在自动加载路径中,
class_exists('PHPUnit\Framework\TestCase')就能触发加载,带来额外开销和潜在风险
--no-dev 和 APP_DEBUG=true 是两回事,别以为关了调试就安全了
--no-dev 控制的是「包是否被安装」,APP_DEBUG=true 控制的是「框架是否输出堆栈」。二者完全解耦。你用了 --no-dev,但没改配置,symfony/debug-bundle 是不会装,可 Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer 依然会暴露完整变量结构——只要 APP_DEBUG=true 还开着。
- Laravel 的
APP_DEBUG=true会启用异常页面,哪怕laravel/framework是唯一装进来的包 - 某些项目在
config/app.php里硬编码了'debug' => env('APP_DEBUG', true),环境变量没设,就会 fallback 到true - 验证方法:上线前 curl 一个 404 路由,看响应头有没有
X-Debug-Mode: symfony或响应体是否含vendor/symfony/error-handler相关 HTML
CI/CD 流水线里最容易踩的三个坑
很多团队在 GitHub Actions 或 GitLab CI 里写了 composer install --no-dev,结果镜像里还是有 phpunit,问题往往不出在命令本身。
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
-
COMPOSER_NO_DEV=0或COMPOSER_NO_DEV=""环境变量会覆盖命令行参数,优先级更高;CI 中要显式 unset 或设为1 - Docker 构建时用了
COPY . .,但没删composer.json和composer.lock,攻击者拿到镜像后可直接运行composer show --dev探测技术栈 - CI 脚本里先跑了
composer update --no-dev,再跑composer install --no-dev——update会改composer.lock,破坏可重现性,生产部署应只用install
怎么快速确认线上环境真的干净了
别只信部署脚本,上线后进服务器查一次最实在。以下命令 10 秒内可完成验证:
-
composer show --dev:应该返回空或No packages,否则说明 dev 包被装进来了 -
ls vendor/ | grep -E "(phpunit|var-dumper|debugbar|pint|fixtures-bundle)":手动过滤高频风险包名 -
grep -r "class_exists.*TestCase" vendor/ --include="*.php" | head -3:检查是否有测试类被意外纳入 autoload 范围(虽然不装包,但某些 autoload-dev 配置仍可能生效)
真正难清理的不是文件,是那些被写死在 config 或 service provider 里的条件加载逻辑——比如某段代码在 APP_ENV === 'local' 时 require 了 symfony/var-dumper,但环境变量没传对,它就在生产里活下来了。

















