生产环境 vendor 目录必须设为只读,因PHP运行时仅需读取代码,写权限会引发Webshell注入和部署不一致风险;正确做法是chown归部署用户、chmod 755目录、find -type f设644文件权限、单独chmod +x vendor/bin/*,并确保www-data属deploy组且有组读执行权。

生产环境 vendor 目录必须设为只读,但不能简单 chmod -R 555 —— 否则 autoload 会失败,PHP 进程连 opendir() 都被拒。
为什么 vendor/ 设为只读是硬性要求
PHP 运行时(如 Nginx + PHP-FPM)只需读取 vendor 中的代码,写权限既无必要,又引入两类确定性风险:
- 攻击者利用未过滤的缓存路径、日志注入或反序列化漏洞,向
vendor/composer/或vendor/{pkg}/src/写入 Webshell - 误执行
composer dump-autoload或composer install(比如部署脚本漏删 dev 命令),破坏已验证的部署一致性
Laravel、Symfony 等框架明确将 vendor/ 视为不可变资产,storage/ 和 bootstrap/cache/ 才是运行时可写目录。
chmod -R 555 vendor/ 是典型错误操作
它会让 PHP 进程无法进入子目录(缺执行位),报错如:Warning: require(/var/www/app/vendor/autoload.php): failed to open stream;;更糟的是,某些框架会在 vendor/composer/ 下尝试写 autoload_static.php 或 installed.json 快照,直接触发 Permission denied。
正确做法是分层控制:
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
-
chown -R deploy:deploy vendor/→ 归属部署用户,避免 root 残留 -
chmod 755 vendor/→ 目录需执行位(x),否则无法遍历 -
find vendor/ -type f -exec chmod 644 {} \;→ 所有文件去写权限,保留读+执行组权限 -
chmod +x vendor/bin/* 2>/dev/null || true→ 仅放行明确需要执行的二进制(如phinx),其余删掉 - 若应用依赖动态类映射(如某些插件机制),需单独保留
vendor/composer/autoload_classmap.php可写,否则应预生成并设为 644
CI/CD 构建后同步到生产环境的权限陷阱
rsync 或 cp -r 会继承源端 umask 或挂载点权限,常见现象:
-
file_put_contents(/var/www/app/vendor/composer/autoload_static.php): Failed to open stream: Permission denied→ 实际是vendor/composer/目录属主为root,而 PHP 进程以www-data运行,且未加入deploy组 -
Could not scan for classes inside "app/Console"→composer dump-autoload在 CI 中执行成功,但生成的autoload_*.php文件权限为600,部署后www-data无读权
修复关键点:
- 确保
www-data属于deploy组:usermod -aG deploy www-data - CI 构建阶段应在生成 autoload 文件后统一修正权限:
find vendor/composer/ -name "autoload_*.php" -exec chmod 644 {} \; - 部署脚本中禁止使用
sudo rsync直接覆盖,应先chown -R deploy:deploy再chmod,或用rsync --chown=deploy:deploy --chmod=D755,F644
只读代理层不是靠文件权限兜底,而是靠运行时隔离
单纯限制 vendor/ 权限只能防写入,不能防敏感包被意外 require 或泄露。真正收紧企业敏感代码仓库访问,需在 Composer 层加代理控制:
- 私有仓库 URL 必须走带鉴权的 HTTPS 地址,禁用裸 Git URL(如
git@)——后者凭据易被ps aux或进程树捕获 - 所有私有包
require必须显式声明repositories,且顺序置顶;禁用默认 packagist.org 的方式是:{"packagist.org": false},而非删掉字段 - CI/CD 中禁用非官方源:执行
composer config --global repositories.0 false和composer config repositories.0 false,确保输出为[] - 敏感项目禁止
composer global安装任何工具,防止~/.composer/auth.json泄露凭证;改用COMPOSER_AUTH环境变量传入临时 token
最易被忽略的一点:即使 vendor/ 全只读、仓库全鉴权,只要项目里存在 composer.json 中未声明却实际 require 的私有包名,Composer 仍会 fallback 到 packagist.org 尝试解析——这可能暴露包名结构。务必用 composer validate --strict 检查配置完整性。

















