PHP中shell_exec执行scp失败,报Permission denied错误求助
解决scp Permission denied (publickey,password) 的常见原因与排查步骤
咱们一步步拆解这个问题,你遇到的错误提示说明SSH连接目标服务器时,既无法通过公钥认证,也没有提供密码(你用了-B批量模式,不会触发密码输入),所以被直接拒绝了。以下是最可能的几个原因和对应的排查方法:
1. Web服务器进程的权限与私钥访问问题
PHP代码是跑在web服务器进程(比如Apache的www-data、Nginx的php-fpm用户)下的,这个用户能不能正常访问你的$priv_key私钥文件是核心:
- 先手动切换到web服务器进程用户,比如执行
su -s /bin/bash www-data(不同系统用户可能不同,比如CentOS是apache),然后尝试用这个私钥连接目标服务器:ssh -i /path/to/your/priv_key $user@$host。如果这一步就失败,说明web用户要么读不到私钥,要么私钥权限不符合SSH的安全要求。 - SSH对私钥权限要求极严:私钥文件必须设为
600(执行chmod 600 /path/to/priv_key),所在目录权限建议设为700。如果权限太开放(比如777),SSH会直接拒绝使用这个私钥——这是内置的安全机制。
2. 密钥对与目标服务器的公钥配置不匹配
- 确认你的私钥对应的公钥已经正确添加到目标服务器上
$user用户的~/.ssh/authorized_keys文件里。你可以在web服务器的终端(用root或能访问私钥的用户)手动执行代码里的scp命令,如果手动都失败,那肯定是密钥对的问题——要么公钥没传过去,要么传的时候格式出错(比如换行符被截断)。 - 同时检查目标服务器上
$user用户的~/.ssh目录权限是700,authorized_keys文件权限是600,权限不对的话SSH也会拒绝公钥认证。
3. PHP命令中的变量转义问题
你直接把变量拼到命令里,如果变量包含空格、引号这类特殊字符,会导致命令解析错误,比如私钥路径有空格的话,-i $priv_key会被拆成多个参数,SSH根本找不到正确的私钥:
- 建议用PHP的
escapeshellarg()函数对每个变量进行转义,避免特殊字符破坏命令结构,修改后的代码示例:$priv_key_escaped = escapeshellarg($priv_key); $tmp_file_escaped = escapeshellarg($tmp_file); $user_escaped = escapeshellarg($user); $host_escaped = escapeshellarg($host); $filename_escaped = escapeshellarg($filename); $command = "scp -B -i $priv_key_escaped -o StrictHostKeyChecking=no $tmp_file_escaped $user_escaped@$host_escaped:/home/asterisk/etc/asterisk/$filename_escaped 2>&1"; $result = shell_exec($command); // 可以打印$result查看更详细的错误日志
4. 目标服务器的SSH配置限制
- 登录目标服务器,检查
/etc/ssh/sshd_config文件:确认PubkeyAuthentication设置为yes,AuthorizedKeysFile路径正确(默认是~/.ssh/authorized_keys)。 - 看看有没有
AllowUsers或DenyUsers配置,确保你用的$user在允许登录的列表里。 - 有些服务器会禁用密码认证,但你用的是公钥,所以
PasswordAuthentication设为no不影响,但如果公钥认证失败,就会直接拒绝连接。
快速排查思路
先从手动测试入手缩小范围:
- 在web服务器上,用root或能访问私钥的用户,执行代码里的scp命令,看是否能成功。如果成功,说明问题出在web进程用户的权限上;如果失败,说明是密钥对或目标服务器配置的问题。
- 切换到web进程用户,重复上述scp命令,进一步定位问题。
内容的提问来源于stack exchange,提问作者Богдан Глова
相关产品推荐
相关产品推荐

