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() 驗證伺服器的 hostkey,然後使用密碼或公開金鑰進行驗證。

參數

host

port

methods

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 要宣告的加密方法列表,以逗號分隔,按優先順序排列。 rijndael-cbc@lysator.liu.seaes256-cbcaes192-cbcaes128-cbc3des-cbcblowfish-cbccast128-cbcarcfournone**
comp 要宣告的壓縮方法列表,以逗號分隔,按優先順序排列。 zlibnone
mac 要宣告的 MAC 方法列表,以逗號分隔,按優先順序排列。 hmac-sha1hmac-sha1-96hmac-ripemd160hmac-ripemd160@openssh.comnone**

注意加密和 MAC 方法 "none"

基於安全考量,底層的 » libssh2 函式庫預設會停用 none,除非在建置期間使用適當的 ./configure 選項明確啟用。請參閱底層函式庫的文件以取得更多資訊。

callbacks

callbacks 參數可以是一個關聯式陣列,包含以下任何或所有參數。

回呼參數
索引 意義 原型
ignore 收到 SSH2_MSG_IGNORE 封包時要呼叫的函式名稱 void ignore_cb($message)
debug 收到 SSH2_MSG_DEBUG 封包時要呼叫的函式名稱 void debug_cb($message, $language, $always_display)
macerror 收到封包但訊息鑑別碼驗證失敗時要呼叫的函式名稱。如果回呼函式回傳 true,則會忽略此不符,否則連線將會終止。 bool macerror_cb($packet)
disconnect 收到 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 類別,用於連線到伺服器、使用公鑰驗證、驗證伺服器的指紋、發出指令並讀取其標準輸出,以及正確斷線。注意:您可能需要確保您的指令會產生輸出,以便提取回應。有些人認為,在您提取回應之前,指令是不會執行的。
<?php
class NiceSSH {
// SSH Host
private $ssh_host = 'myserver.example.com';
// SSH Port
private $ssh_port = 22;
// SSH Server Fingerprint
private $ssh_server_fp = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
// SSH Username
private $ssh_auth_user = 'username';
// SSH Public Key File
private $ssh_auth_pub = '/home/username/.ssh/id_rsa.pub';
// SSH Private Key File
private $ssh_auth_priv = '/home/username/.ssh/id_rsa';
// SSH Private Key Passphrase (null == no passphrase)
private $ssh_auth_pass;
// SSH Connection
private $connection;

public function
connect() {
if (!(
$this->connection = ssh2_connect($this->ssh_host, $this->ssh_port))) {
throw new
Exception('Cannot connect to server');
}
$fingerprint = ssh2_fingerprint($this->connection, SSH2_FINGERPRINT_MD5 | SSH2_FINGERPRINT_HEX);
if (
strcmp($this->ssh_server_fp, $fingerprint) !== 0) {
throw new
Exception('Unable to verify server identity!');
}
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('Autentication rejected by server');
}
}
public function
exec($cmd) {
if (!(
$stream = ssh2_exec($this->connection, $cmd))) {
throw new
Exception('SSH command failed');
}
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.net 的 danbrown 編輯:包含 AlainC 在 2012 年 6 月 26 日的使用者筆記 #109185(已移除)中建議的兩個錯誤修正。]
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

if (!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);
}

?>
jrdbrndt at gmail dot com
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 dot suribala dot com
19 年前
在 Sara 的幫助下,我有了以下這個彈性相當好的 SS2 類別。如果有人改進了它,請隨時告訴我。

<?php

// ssh protocols
// note: once openShell method is used, cmdExec does not work

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 .= "Connection failed !";
}

}

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 .= "Authorization failed !";
}

}

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 connection failed !";

}

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 年前
當你需要檢查遠端伺服器的真實性時,使用 'hostkey' 選項才有意義,例如透過 ssh2_fingerprint 函式。基於此目的,你需要指定一個以優先順序遞減排序的可能演算法清單。支援的值還包括 '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 警告: ssh2_connect(): 啟動 SSH 連線時發生錯誤(-5): 無法交換加密金鑰 (在 php shell code 的第 1 行)
PHP 警告: ssh2_connect(): 無法連線到 192.168.3.1 (在 php shell code 的第 1 行)
?>
rainerkrauss at googlemail dot com
10 年前
警告!如果你開啟一個 ssh 連線並執行一個開啟另一個 ssh 連線的外部程式,可能會導致非常奇怪的行為。

我使用了一個 sftp 連線來取得檔案清單,然後使用「exec」透過外部 sftp 下載檔案。lftp 沒有任何說明就下載了零位元組檔案,psftp 大多數時候會以錯誤碼 11 退出,但有時它也能正常運作 - 可能取決於 php 多快收集垃圾並先關閉未使用的連線。

由於沒有關閉連線的函式,因此你需要確保清除所有參考 (unset) 以關閉它。
To Top