Phpseclib: Securely Communicating with Remote Servers via PHP

PHP has an SSH2 library which provides access to resources (shell, remote exec, tunneling, file transfer) on a remote machine using a secure cryptographic transport. Objectively, it is a tedious and highly frustrating task for a developer to implement it due to its overwhelming configuration options and complex API with little documentation.

The phpseclib (PHPSecure Communications Library) package has a developer friendly API. It uses some optional PHP extensions if they’re available and falls back on an internal PHP implementation otherwise. To use this package, you don’t need any non-default PHP extensions installed.

Installation

composer require phpseclib/phpseclib

This will install the most recent stable version of the library via Composer.

Use-cases

Before diving in blindly, I’d like to list some use-cases appropriate for using this library:

Executing deployment scripts on a remote server

Downloading and uploading files via SFTP

Generating SSH keys dynamically in an application

Displaying live output for remote commands executed on a server

Testing an SSH or SFTP connection

Connecting to the Remote Server

Using phpseclib, you can connect to your remote server with any of the following authentication methods:

RSA key

Password Protected RSA key

Username and Password (Not recommended)

RSA Key

We will assume that you have a secure RSA key already generated. If you are not familiar with generating a secure RSA key pair, you can go through this article. For a video explanation, you can refer to Creating and Using SSH Keys from Servers For Hackers.

Executing Multiple Commands on Remote Server

In real life applications, we rarely execute a single command. We often need to traverse around the server using cd and execute many other commands. If you try to execute multiple commands on the remote server as below, it won’t give you the desired output:

Exiting on First Error

In the above script, the whole set of commands is executed as a single shell script. Every command will be executed, even if some of them fail, just like in a regular shell script. As a default, this is fine, but if we need to exit on the first error, we have to alter our bash script. This is not something specific to phpseclib, it is related to bash scripting.

If you put a set -e option at the beginning of the script, the script will terminate as soon as any command in the chain returns a non-zero value.

For example, the modified version of the above deployment script would be

Other Configuration Options

It’s also possible to set many other available configuration options. You can call them as $ssh->{option}.

For example: $ssh->setTimeout(100).

All the options we haven’t covered yet are in the table below:

Option

Use case

setTimeout($seconds)

$ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it’ll timeout. Setting $timeout to false or 0 will mean there is no timeout.

write($cmd)

Inputs a command into an interactive shell.

read()

Returns the output of an interactive shell

isTimeout()

Return true if the result of the last $ssh->read() or $ssh->exec() was due to a timeout. Otherwise it will return false.

isConnected()

Returns true if the connection is still active

enableQuietMode()

Suppresses stderr so no errors are returned

disableQuietMode()

Includes stderr in output

isQuiteModeEnabled()

Returns true if quiet mode is enabled

enablePTY()

Enable request-pty when using exec()

disablePty()

Disable request-pty when using exec()

isPTYEnabled()

Returns true if request-pty is enabled

getLog()

Returns a log of the packets that have been sent and received.

getServerPublicHostKey()

Returns the server public host key. Returns false if the server signature is not signed correctly with the public host key.

getExitStatus()

Returns the exit status of an SSH command or false.

getLanguagesClient2Server()

Return a list of the languages the server supports, when receiving stuff from the client.

getLanguagesServer2Client()

Return a list of the languages the server supports, when sending stuff to the client.

getCompressionAlgorithmsServer2Client()

Return a list of the compression algorithms the server supports, when sending stuff to the client.

getCompressionAlgorithmsClient2Server()

Return a list of the compression algorithms the server supports, when receiving stuff from the client.

getMACAlgorithmsServer2Client()

Return a list of the MAC algorithms the server supports, when sending stuff to the client.

getMACAlgorithmsClient2Server()

Return a list of the MAC algorithms the server supports, when receiving stuff from the client.

getEncryptionAlgorithmsServer2Client()

Return a list of the (symmetric key) encryption algorithms the server supports, when sending stuff to the client.

getEncryptionAlgorithmsClient2Server()

Return a list of the (symmetric key) encryption algorithms the server supports, when receiving stuff from the client.

getServerHostKeyAlgorithms()

Return a list of the host key (public key) algorithms the server supports.

getKexAlgorithms()

Return a list of the key exchange algorithms the server supports.

Alternatives

LIBSSH2 – The SSH library – The library provides similar functionality, but is a little less intuitive to use, and it requires you to have libssh2 installed on the server, which most shared hosts don’t have.

Process component – Symfony’s component for writing your own script for connecting and executing commands – as you would do in a normal terminal. This limits us in the configuration options that we might need to set. Achieving the same functionality the above configuration methods provide us with with Process would require in-depth bash knowledge. If your use-case involves only running a local script, however, this might prove to be a useful component.

Summary

In this article, we introduced phpseclib, a package which provides an alternative for SSH2. We have covered the configuration options necessary to get started, and the table above should help you fill in the gaps and give an overview of other configuration options available to you.

For an in-depth implementation of key-based communication, see our previous tutorial.

How do you execute remote commands? Can you think of any advanced use cases for this library? What are they? Let us know in the comments!