PHP Conference Japan 2024

ssh2_connect

(PECL ssh2 >= 0.9.0)

ssh2_connect连接到 SSH 服务器

描述

ssh2_connect(
    字符串 $host,
    整数 $port = 22,
    数组 $methods = ?,
    数组 $callbacks = ?
): 资源|false

建立到远程 SSH 服务器的连接。

连接后,客户端应使用 ssh2_fingerprint() 验证服务器的主机密钥,然后使用密码或公钥进行身份验证。

参数

主机

端口

方法

methods 可以是关联数组,最多包含四个如下所述的参数。

methods 可以是关联数组,包含以下任意或所有参数。
索引 含义 支持的值*
kex 要公布的密钥交换方法列表,以逗号分隔,按优先级排序。 diffie-hellman-group1-sha1diffie-hellman-group14-sha1diffie-hellman-group-exchange-sha1
hostkey 要公布的主机密钥方法列表,以逗号分隔,按优先级排序。 ssh-rsassh-dss
client_to_server 关联数组,包含从客户端发送到服务器的消息的加密、压缩和消息认证码 (MAC) 方法首选项。  
server_to_client 关联数组,包含从服务器发送到客户端的消息的加密、压缩和消息认证码 (MAC) 方法首选项。  

* - 支持的值取决于底层库支持的方法。有关更多信息,请参阅 » libssh2 文档。

client_to_serverserver_to_client 可以是关联数组,包含以下任意或所有参数。
索引 含义 支持的值*
crypt 要公布的加密方法列表,以逗号分隔,按优先级排序。 [email protected]aes256-cbcaes192-cbcaes128-cbc3des-cbcblowfish-cbccast128-cbcarcfournone**
comp 要公布的压缩方法列表,以逗号分隔,按优先级排序。 zlibnone
mac 要公布的 MAC 方法列表,以逗号分隔,按优先级排序。 hmac-sha1hmac-sha1-96hmac-ripemd160[email protected]none**

注意加密和 MAC 方法 "none"

出于安全原因,除非在构建时使用适当的 ./configure 选项明确启用,否则底层 » libssh2 库会禁用 none。有关更多信息,请参阅底层库的文档。

回调

callbacks 可以是关联数组,包含以下任意或所有参数。

回调参数
索引 含义 原型
忽略 接收 SSH2_MSG_IGNORE 数据包时要调用的函数的名称 void ignore_cb($message)
调试 接收 SSH2_MSG_DEBUG 数据包时要调用的函数的名称 void debug_cb($message, $language, $always_display)
macerror 接收数据包但消息认证码失败时要调用的函数的名称。如果回调返回 true,则将忽略不匹配,否则将终止连接。 bool macerror_cb($packet)
断开连接 接收 SSH2_MSG_DISCONNECT 数据包时要调用的函数的名称 void disconnect_cb($reason, $message, $language)

返回值

成功时返回资源,错误时返回 false

示例

示例 #1 ssh2_connect() 示例

打开连接,在发送数据包时强制使用 3des-cbc,在接收数据包时使用任何强度的 aes 密码,在两个方向上都不进行压缩,并使用 Group1 密钥交换。

<?php
/* 如果服务器终止连接,则通知用户 */
function my_ssh_disconnect($reason, $message, $language) {
printf("服务器断开连接,原因代码为 [%d],消息为:%s\n",
$reason, $message);
}

$methods = array(
'kex' => 'diffie-hellman-group1-sha1',
'client_to_server' => array(
'crypt' => '3des-cbc',
'comp' => 'none'),
'server_to_client' => array(
'crypt' => 'aes256-cbc,aes192-cbc,aes128-cbc',
'comp' => 'none'));

$callbacks = array('disconnect' => 'my_ssh_disconnect');

$connection = ssh2_connect('shell.example.com', 22, $methods, $callbacks);
if (!
$connection) die('连接失败');
?>

参见

添加注释

用户贡献的注释 8 条注释

Steve Kamerman
13 年前
由于缺少完整的示例,这里提供了一个简单的 SSH2 类,用于连接到服务器、使用公钥身份验证进行身份验证、验证服务器的指纹、发出命令并读取其 STDOUT 并正确断开连接。注意:您可能需要确保您的命令生成输出,以便可以提取响应。有些人认为,除非您将响应拉回,否则命令不会执行。
<?php
class NiceSSH {
// SSH 主机
private $ssh_host = 'myserver.example.com';
// SSH 端口
private $ssh_port = 22;
// SSH 服务器指纹
private $ssh_server_fp = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
// SSH 用户名
private $ssh_auth_user = 'username';
// SSH 公钥文件
private $ssh_auth_pub = '/home/username/.ssh/id_rsa.pub';
// SSH 私钥文件
private $ssh_auth_priv = '/home/username/.ssh/id_rsa';
// SSH 私钥密码短语(null == 无密码短语)
private $ssh_auth_pass;
// SSH 连接
private $connection;

public function
connect() {
if (!(
$this->connection = ssh2_connect($this->ssh_host, $this->ssh_port))) {
throw new
Exception('无法连接到服务器');
}
$fingerprint = ssh2_fingerprint($this->connection, SSH2_FINGERPRINT_MD5 | SSH2_FINGERPRINT_HEX);
if (
strcmp($this->ssh_server_fp, $fingerprint) !== 0) {
throw new
Exception('无法验证服务器身份!');
}
if (!
ssh2_auth_pubkey_file($this->connection, $this->ssh_auth_user, $this->ssh_auth_pub, $this->ssh_auth_priv, $this->ssh_auth_pass)) {
throw new
Exception('服务器拒绝身份验证');
}
}
public function
exec($cmd) {
if (!(
$stream = ssh2_exec($this->connection, $cmd))) {
throw new
Exception('SSH 命令失败');
}
stream_set_blocking($stream, true);
$data = "";
while (
$buf = fread($stream, 4096)) {
$data .= $buf;
}
fclose($stream);
return
$data;
}
public function
disconnect() {
$this->exec('echo "EXITING" && exit;');
$this->connection = null;
}
public function
__destruct() {
$this->disconnect();
}
}
?>

[由 php DOT net 上的 danbrown 编辑:包含 'AlainC' 在用户注释 #109185(已删除)中于 2012 年 6 月 26 日建议的两个错误修复。]
thessoro at gmail dot com
9 年前
在提供特定主机密钥顺序时要小心。

<?php
ssh2_connect
('IP', 'port', array('hostkey'=>'ssh-rsa, ssh-dss'));
?>

只有当服务器的公钥为 RSA 而不是 DSA 时才有效,这与预期不符。这是由“ssh-dss”之前的空格引起的。

所以类似的代码

<?php
ssh2_connect
('IP', 'port', array('hostkey'=>'ssh-rsa,ssh-dss'));
?>

将起作用。HOSTKEY 方法使用您编写的完全相同的字符进行覆盖,因此不允许空格。

这花费了我一些时间,您可以节省这些时间;)
christophermjgray at gmail dot com
7 年前
此页面已过时。由于 Logjam (https://weakdh.org/logjam.html),密钥交换方法中 kex 部分中的任何 SHA-1 都应丢弃。如果您继续使用它们,连接将导致以下警告
"警告:ssh2_connect():启动 SSH 连接错误(-5):无法在...中交换加密密钥"

以下是有效示例。此外,通过完全删除“hex”部分,libssl (https://libssh2.org/) 将回退到发现用于身份验证的最强密码。



<?php

如果 (!function_exists("ssh2_connect")) die("函数 ssh2_connect 不存在");

function
ssh2_debug($message, $language, $always_display) {
printf("%s %s %s\n",$message,$language,$always_display);
}

/* 如果服务器终止连接,通知用户 */
function my_ssh_disconnect($reason, $message, $language) {
printf("服务器断开连接,原因代码为 [%d],消息为:%s\n", $reason, $message);
}

$methods = array(
'hostkey'=>'ssh-rsa,ssh-dss',
// 'kex' => 'diffie-hellman-group-exchange-sha256',
'client_to_server' => array(
'crypt' => 'aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,3des-cbc,blowfish-cbc',
'comp' => 'none'),
'server_to_client' => array(
'crypt' => 'aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,3des-cbc,blowfish-cbc',
'comp' => 'none'));

$callbacks = array('disconnect' => 'my_ssh_disconnect');

foreach (array(
'192.168.1.1') as $host) {
$connection = ssh2_connect($host, 22, $methods, $callbacks);
if (!
$connection) die("连接失败:");

ssh2_auth_password($connection, 'user', 'my_password') or die("无法进行身份验证");
$stream = ssh2_exec($connection, 'free -m');
stream_set_blocking($stream, true);
$stream_out = ssh2_fetch_stream($stream, SSH2_STREAM_STDIO);
echo
stream_get_contents($stream_out);
}

?>
[email protected]
4 年前
尝试在加密方法列表中包含“aes256-cbc”导致错误。此处的文档可能已过时,您可以通过检查 libssh2.org 上的 libssh2 文档来查找更准确的可接受值列表。
Trev White
11 年前
您好,
如果您在运行 ssh2 会话时遇到问题,并且在执行 stream_get_contents 期间无限期等待,则可能是因为远程系统已运行该命令,并且现在处于 # 提示符处等待下一个命令。我在 HP MSA 服务器上遇到了此问题,以下是解决此问题的代码。

假设您已使用身份验证方法连接,并且 $ssh 包含句柄。

<?php
$command
= "check disk";
// 打开一个较大的窗口以停止换行
$stream = ssh2_shell ($ssh, 'xterm', null, 200, 200, SSH2_TERM_UNIT_CHARS);

// 连接到错误流
$errorStream = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);

// 阻塞流,以便我们等待它们完成
stream_set_blocking ($stream, true);
stream_set_blocking($errorStream, true);

// 将命令发送到终端
fwrite ($stream, $command . PHP_EOL );

// 等待,让终端有机会接受并开始处理命令,毕竟这是一个慢速存储设备
sleep(2);

// 重要部分!!在等待流之前,向终端发送 exit 以关闭连接
fwrite ($stream, "exit" . PHP_EOL );
sleep (2);

// 打印输出
echo stream_get_contents($stream);
$errortext=stream_get_contents($errorStream);

if (
strlen($errortext) > 0) {
// 错误数据
echo "错误数据: $errortext";
exit (
1);
}

//一切正常
exit (0);

?>

您不能使用此方法使用 ssh2_exec(至少我不能),因为在执行第一个命令时,流会被阻塞,然后您无法运行 exit 命令,而终端似乎使用一个会话。

希望这对某人有所帮助。
suri.suribala.com
19 年前
在 Sara 的帮助下,我拥有以下非常灵活的 SS2 类。如果有人对其进行了改进,请随时告诉我。

<?php

// ssh 协议
// 注意:一旦使用 openShell 方法,cmdExec 将无法工作

class ssh2 {

private
$host = 'host';
private
$user = 'user';
private
$port = '22';
private
$password = 'password';
private
$con = null;
private
$shell_type = 'xterm';
private
$shell = null;
private
$log = '';

function
__construct($host='', $port='' ) {

if(
$host!='' ) $this->host = $host;
if(
$port!='' ) $this->port = $port;

$this->con = ssh2_connect($this->host, $this->port);
if( !
$this->con ) {
$this->log .= "连接失败!";
}

}

function
authPassword( $user = '', $password = '' ) {

if(
$user!='' ) $this->user = $user;
if(
$password!='' ) $this->password = $password;

if( !
ssh2_auth_password( $this->con, $this->user, $this->password ) ) {
$this->log .= "授权失败!";
}

}

function
openShell( $shell_type = '' ) {

if (
$shell_type != '' ) $this->shell_type = $shell_type;
$this->shell = ssh2_shell( $this->con, $this->shell_type );
if( !
$this->shell ) $this->log .= " Shell 连接失败!";

}

function
writeShell( $command = '' ) {

fwrite($this->shell, $command."\n");

}

function
cmdExec( ) {

$argc = func_num_args();
$argv = func_get_args();

$cmd = '';
for(
$i=0; $i<$argc ; $i++) {
if(
$i != ($argc-1) ) {
$cmd .= $argv[$i]." && ";
}else{
$cmd .= $argv[$i];
}
}
echo
$cmd;

$stream = ssh2_exec( $this->con, $cmd );
stream_set_blocking( $stream, true );
return
fread( $stream, 4096 );

}

function
getLog() {

return
$this->log;

}

}

?>
devoldemar
1 年前
当您需要检查远程服务器的真实性时(例如,通过 ssh2_fingerprint 函数),使用“hostkey”选项是有意义的。为此,您需要按降序指定可能算法的列表。支持的值还包括“ssh-ed25519”、“ecdsa-sha2-nistp256”、“ecdsa-sha2-nistp384”和“ecdsa-sha2-nistp521”。
例如
<?php
$ssh
= ssh2_connect('192.168.3.1', 22, ['hostkey' => 'ssh-ed25519,ssh-rsa']);
$md5 = ssh2_fingerprint($ssh); // 返回 ED25519 主机密钥的 MD5 哈希值,或者如果不存在先前类型的密钥则返回 RSA 密钥的 MD5 哈希值
?>

请注意,如果远程服务器没有相应类型的密钥,则连接将失败。
<?php
$ssh
= ssh2_connect('192.168.3.1', 22, ['hostkey' => 'ssh-dss']);
PHP Warning: ssh2_connect(): Error starting up SSH connection(-5): Unable to exchange encryption keys in php shell code on line 1
PHP Warning
: ssh2_connect(): Unable to connect to 192.168.3.1 in php shell code on line 1
?>
rainerkrauss at googlemail dot com
10 年前
警告!如果您打开了一个 ssh 连接并执行了一个打开另一个 ssh 连接的外部程序,可能会导致非常奇怪的行为。

我使用 sftp 连接获取文件列表,然后使用“exec”使用外部 sftp 下载文件。lftp 下载零字节文件且没有注释,psftp 大多数情况下以错误代码 11 退出,但有时它可以工作——可能取决于 php 收集垃圾并首先关闭未使用的连接的速度。

由于没有关闭连接的函数,您需要确保销毁所有引用(unset)以关闭它。
To Top