Connecting
Basic Example with Signature Verification
As the "secure" part of Secure Shell (SSH) implies, SSH is designed to work over hostile networks. SSH encrypts your data so that eavesdroppers cannot read the data being sent back and forth and it provides a method that can be used to verify that the server you're connecting to hasn't been replaced with a hostile server. To facilitate the latter SSH servers have a host public key. Data that's unique to the SSH session is signed by the server and should be verified by the client with the host public key. Of course, simply verifying the signature is insufficient - you need to verify that the host public key is correct. X.509 / SSL / TLS does this with certificate authorities but in SSH, in theory, you'd get the host public key through some out-of-band method. In practice, however, people usually just cache the key the first time they connect to a server and assume all subsequent connections should be using that same key. How the expected host key is saved is up to the application designer (OpenSSH saves them in ~/.ssh/known_hosts
) but here is an example of how the host key would be retrieved from the SSH server (prior to authentication) and checked against the expected value ($expected
):
use phpseclib3\Net\SSH2;
$ssh = new SSH2('localhost', 22);
if ($expected != $ssh->getServerPublicHostKey()) {
throw new \Exception('Host key verification failed');
}
All subsequent code samples omit this part for brevity but if you're concerned about eavesdroppers (which isn't always a legit concern; eg. if you're connecting to localhost) it should not be skipped.
The port number, incidentally, is optional. If not specified it will be assumed to be 22.
Using an HTTP Proxy
use phpseclib3\Net\SSH2;
$fsock = fsockopen('127.0.0.1', 80, $errno, $errstr, 1);
if (!$fsock) {
throw new \Exception($errstr);
}
fputs($fsock, "CONNECT localhost:22 HTTP/1.0\r\n");
//fputs($fsock, "Proxy-Authorization: Basic " . base64_encode('user:pass') . "\r\n");
fputs($fsock, "\r\n");
while ($line = fgets($fsock, 1024)) {
if ($line == "\r\n") {
break;
}
//echo $line;
}
$ssh = new SSH2($fsock);
$ssh->login('username', 'password');
echo $ssh->exec('ls -latr');
Using a SOCKS5 Proxy
use phpseclib3\Net\SSH2;
// SSH connection info
$port = 22;
$address = 'localhost';
// SOCKS5 connection info
$fsock = fsockopen('127.0.0.1', 1080, $errno, $errstr, 1);
if (!$fsock) {
throw new \Exception($errstr);
}
$port = pack('n', $port);
$address = chr(strlen($address)) . $address;
$request = "\5\1\0";
if (fwrite($fsock, $request) != strlen($request)) {
throw new \Exception('Premature termination');
}
$response = fread($fsock, 2);
if ($response != "\5\0") {
throw new \Exception('Unsupported protocol or unsupported method');
}
$request = "\5\1\0\3$address$port";
if (fwrite($fsock, $request) != strlen($request)) {
throw new \Exception('Premature termination');
}
$response = fread($fsock, strlen($address) + 6);
if (substr($response, 0, 2) != "\5\0") {
echo bin2hex($response) . "\n";
throw new \Exception("Unsupported protocol or connection refused");
}
$ssh = new SSH2($fsock);
$ssh->login('username', 'password');
echo $ssh->exec('ls -latr');
Binding to a Specific IP Address
use phpseclib3\Net\SSH2;
// http://php.net/manual/en/context.socket.php
$opts = [
'socket' => [
'bindto' => '127.255.255.255:0',
],
];
$context = stream_context_create($opts);
$socket = stream_socket_client('tcp://localhost:22', $errno, $errstr, ini_get('default_socket_timeout'), STREAM_CLIENT_CONNECT, $context);
$ssh = new SSH2($socket);
$ssh->login('username', 'password');
echo $ssh->exec('ls -latr');
Using a Custom Cipher Suite
You can tell phpseclib which algorithms you'd like to use by doing $ssh->setPreferredAlgorithms($methods)
. $methods
should be an associative array with any or all of the following parameters (inspired by ssh2_connect):
client_to_server
and server_to_client
should be an associative array with any or all of the following parameters.
Note that a given algorithm will only be used if it's supported by both phpseclib and the server. The algorithms that the server supports can be determined by doing $ssh->getServerAlgorithms()
. The algorithms that ultimately wind up being used can be determined by doing $ssh->getAlgorithmsNegotiated()
.
Using a custom cipher suite is not recommended. phpseclib's prioritization of algorithms is intended to maximize speed and security. For example, if OpenSSL is installed and you're using PHP >= 7.1.0 then aes128-gcm@openssh.com will be the preferred algorithm. If (1) OpenSSL is not installed or you're using PHP < 7.1.0 BUT (2) libsodium is installed, then aes256-gcm@openssh.com will be the preferred algorithm. You can make either of those the preferred algorithms even if neither OpenSSL or libsodium are installed but your connection will be slowed down because the pure-PHP implemenation of both of those is not nearly as fast as OpenSSL / libsodium.
chacha20-poly1305@openssh.com is the latest hotness in the cryptographic community but it is not prioritized higher because (1) while OpenSSL supports ChaCha20, it doens't support Poly1305 and (2) libsodium doesn't use Poly1305 in the same way that SSH uses it. Despite that, chacha20-poly1305@openssh.com is still pretty fast but not as fast as some of the other available algorithms.
[1] zlib@openssh.com and zlib support were introduced in phpseclib v3.0.11, require PHP 7.0+ and that the zlib extension be installed (until such time that a shim can be written).