Last time I already tried to prove PHP can do anything when it comes to network protocols by implementing a DNS server. This time I’m doing it again with a server-side implementation of the SSH2 protocol.
You probably know SSH at least by its name. It’s a of secure telnet replacement which also allows many other things such as port forwarding, remote file management (with sftp) and more.
With PHP I could write a fully working SSH server in only 3 days. Of course I didn’t implement every single extension there is to SSH, but I’ve implemented:
- SSH2 protocol only (no SSH1, anyway who uses that anymore?)
- Encryption protocols: aes128-cbc,blowfish-cbc,serpent256-cbc,cast128-cbc,3des-cbc (via mcrypt)
- Message digests: hmac-sha1,hmac-sha1-96,hmac-md5,hmac-md5-96 (via hash)
- No compression as I cannot easily keep a compression context active (the gzip extension in php is missing a way to create a compression context)
- Password and public key (ssh-dss and ssh-rsa) identification
- Ability to program an interactive shell in PHP (there are send and recv functions in a separate class, anyone can have some fun and write something out of that. Should be possible to make a wrapper to communicate with a shell launched with proc_open)
- Support for multiple channels
- SFTP subsystem
- Can be easily extended to add support for custom channels or re-use the ssh protocol for something else
My goal when writing this was to provide a replacement for the FTP protocol for the customers of my hosting service. FTP has many drawbacks, including no encryption (you can with ftps or ftpes) and the way ftp transmits data (another connection has to be opened on a different port, leading most of the time to some problems for people behind a NAT and/or firewalled servers).
With this ssh server supporting sftp, I finally got the replacement I was looking for. Of course it uses more CPU than a C ssh server (about 3 times more) but the difference isn’t that big. Next steps will include fork()’ing to open channels (will allow the SFTP server to chroot) and maybe support for some SSH extensions.
To implement the SSH protocol the following PHP extensions were used:
- OpenSSL: used to generate secure bits when negociating the key, and used to generate the host signature on connection. I was hoping to use openssl_verify() to verify the key used when logging in, but I couldn’t manage to convert the ssh-rsa key to something openssl would understand, so I re-implemented signature verification with gmp.
- MCrypt: The ssh protocol is encrypted (usually with something like AES128). mcrypt has the required functions to handle encryption in block mode
- Hash: each packet transmitted over SSH is optionally signed with a HMAC signature. In order to generate and verify those signatures I used hash_hmac()
- And finally the most important: GMP. As I was missing some functions to properly handle the initial Diffie-Hellman key exchange (and later to implement publickey authentication) I had to re-implement those in PHP. Of course working with 1024 bits integers is not something we can use the native int type for. GMP (and bc) allows such calculations (and I used them). I was missing the ability in gmp to read from/convert to binary values, so I had to add the use of bin2hex() and pack(‘H*’, …) to be able to work with binary values easily. GMP computations are only used when negociating keys (the ssh rfc recommands doing this once an hour, or every gigabyte of data transmitted) or when using the publickey authentification.
What did I create a ssh server for? The same thing I created a DNS server for fun and for KalyHost. In order to provide services updated in realtime I wrote a database-backed dns server a while ago, and now a ssh server (which can be database-backed too by extending the “Base” class).
The sourcecode can be downloaded from the SVN: http://ookoo.org/svn/pinetd2/trunk/code/classes/Daemon/SSHd/ this depends on pinetd2, a framework I wrote which allows to easily create daemons in PHP, and which I already used to create various things (FTP, Mail server, etc).
People willing to help on pinetd2 (code and/or documentation) are welcome. If you do not mind being called crazy because you make something else than webpages in PHP, you can contact me by mail or on IRC (or by leaving a comment on this post too if you wish to).

#1 by Miguel on 2010/06/28 - 00:20
Quote
> Last time I already tried to prove PHP can do anything when it comes to network protocols
I don’t exactly get why anyone would writing an SSH server in PHP to be impossible. It’s just a matter of implementing a documented protocol. At worst, a tedious undertaking.
Now how about a traceroute client in PHP?
#2 by MagicalTux on 2010/06/28 - 00:46
Quote
Traceroute is easy too but you need a setuid php for that (you need to produce and send raw packets). Already did a ping in native php, doing a traceroute is just a matter of playing with ttl and see who replies with icmp unreach.
#3 by pd on 2010/06/28 - 01:35
Quote
So useless! -.-
#4 by Nanashi on 2010/06/28 - 05:41
Quote
Congratulations, you have made the least secure implementation ever. Even your D-H key exchange has several well known vulnerabilities.
#5 by MagicalTux on 2010/06/28 - 08:18
Quote
If you could tell me more about that I’d appreciate it. The only security problems I found in diffie-hellman are the non-secure random numbers (I use openssl’s random generator to get secure bits) and the fact you cannot authenticate the remote party with just a DH exchange (the ssh protocol works this out with the host key).
Now I searched a bit to find more about what you are telling me here but I couldn’t find anything specifically wrong with my DH implementation.
The only thing wrong with this is the fact I do not support CTR mode, I’ll have to see if we can use it from php, and implement it if not.
EDIT: while undocumented on PHP’s side, the ctr mode is indeed available via mcrypt. I added aes128-ctr which solves problems highlighted in RFC4344
#6 by vladh on 2010/06/28 - 18:19
Quote
#3 and #4, you suck. MagicalTux, good job, probably going to be using this one way or the other.
#7 by meow on 2010/06/28 - 18:39
Quote
interesting…
#8 by Adrian on 2010/06/28 - 20:27
Quote
Great! Respect!
#9 by mike on 2010/06/28 - 20:57
Quote
Regarding compression contexts:
http://php.net/manual/en/class.httpinflatestream.php
#10 by Richard on 2010/06/28 - 23:51
Quote
Good job! I hope you learned heaps doing this project
#11 by sbl2983 on 2010/06/29 - 03:16
Quote
the fact that something is possible doesn’t make it a good idea.
#12 by Hari K T on 2010/06/29 - 20:37
Quote
Great job. I don’t know why some are rude
, may be because they cannot do this
. Anyway I loved it , and happy to see you shared for people to learn it . Thanks once again .
#13 by aleks on 2010/07/03 - 11:45
Quote
#11: i couldn’t agree more. you might want to remember your own words next time you are facing a comment form.
regarding the sshd: currently i’d rather be interested in a php-based ssh client but i will definitely check what you came up with.
php definitely needs a lot more ‘libraries’, classes and projects like this instead of hundreds of frameworks that will die the next day.
#14 by MagicalTux on 2010/07/03 - 11:49
Quote
I’m planning on adapting this code as a ssh client library as it would be mostly identical (only the packet sequence is a bit different, as the client is “client”, it is the one initiating some parts of the protocol and not others).
Anyway it’d make a nice SSH2 client library in a class (SSH2Client) with a few other classes (Crypto for crypto-related stuff, and SFTPClient for sftp access).
Pingback: Max’ Lesestoff zum Wochenende – 26/2010 | PHP hates me - Der PHP Blog
#15 by aleks on 2010/07/03 - 15:27
Quote
sounds great! will definitely watch this space!
#16 by coacheach on 2010/07/14 - 04:18
Quote
Nice work!
That said, if you’re planning on making a php ssh client library it should be noted that there already is one out there:
http://phpseclib.sourceforge.net/
Seems like ya’ll could combine your efforts instead of creating two classes that are functionally the same.
#17 by MagicalTux on 2010/07/14 - 07:43
Quote
Unfortunately the guys working on phpseclib went way too far. Re-implementing RSA in pure PHP is not a bad thing. Re-implementing Rijndael in pure php was, especially “Does not use mcrypt, even when available, for reasons that are explained below.” (and the reason is mcrypt only supports 128/192/256 bits, and not 160/224. For SSH you don’t need RSA160 nor RSA224).
I’m not sure about the random generator either (it uses “/dev/urandom” as a trusted source for random bits, or use mt_rand() with a lot of weird stuff after that).
It looks also like this lib cannot handle running more than one process at a time, the exec() function uses a constant (Oo) for each channel type, and exec() will not return until program execution completes (not even sure how stderr is handled in there).
Well, the philosophy behind phpseclib of doing *everything* in pure-PHP is nice, but not what I’m looking for (especially implementing the block cipher in pure php is most likely going to be very costly in terms of CPU). It is better documented than mine (when writing a ssh client in a few hours only comments tend to become minimalistic) but the work there is impressive (it just doesn’t fit my needs).
#18 by coacheach on 2010/07/14 - 23:32
Quote
Criticizing phpseclib for not using mcrypt for Rijndael seems silly given that they do use mcrypt for AES. And it’s SSH implementation uses Crypt_AES (which does use mcrypt) – not Crypt_Rijndael (which does not use mcrypt).
As for the random number generator… I’ll concede that OpenSSL’s is better. OpenSSL can take on a whole lot more sources of entropy, among other things. As for the “weird stuff after that”… check out wikipedia.org’s article on CSPRNG’s:
http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives
Note the whole bit about running block ciphers on counter mode and using stream ciphers.
I’m not sure I agree with your objection to exec() either. So you can’t run more than one process at a time. That makes for a much simpler API. Consider curl. There’s curl_init() and curl_multi_init(). The latter lets you perform multiple HTTP requests simultaneously but at the expense of having a more complicated API. You have to use curl_multi_select() or else bog down the CPU while you constantly check for updates. And given that I’ve never yet seen a program that actually uses curl_multi_init() its power seems unnecessary.
I think the phpseclib guys took the right approach with only letting exec() do one command at a time.
As for stderr… a Google search for phpseclib stderr revealed this:
http://www.ohloh.net/p/phpseclib/commits/67946901
Pingback: Linkpool Nummer 10 | PHP Gangsta - Der PHP Blog
#19 by coacheach on 2010/07/20 - 04:52
Quote
A few quick comments.
phpseclib’s Net_SSH2 uses Crypt_AES – not Crypt_Rijndael. And Crypt_AES does use mcrypt. Crypt_Rijndael doesn’t, but then I suspect most people shouldn’t be using Crypt_Rijndael anyway.
The “weird stuff after that” in the random number generator is, I think, explained by this:
http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives
Note the whole bit about running block ciphers on counter mode and using stream ciphers.
The fact that you can only run one process at a time with exec() actually makes a lot of sense to me as it makes for a much simpler API. If you could run multiple processes simultaneously how would you know when one ended? Would you have to wait until all of them ended? Or maybe you could check once a second? Or maybe you could do something like select() and block until one process ends. But then you couldn’t do echo $ssh->exec(‘…’) – you’d have to do while ($ssh->block()) { echo $ssh->output(2); if ($ssh->num_open_streams == 0) break; }. Or something like that.
A Google search for stderr reveals this:
http://www.ohloh.net/p/phpseclib/commits/67946901
#20 by yasg on 2010/11/08 - 22:26
Quote
i have a question
how could i power on a server using php script ?
i have more than one serve connecting to one router
and i want to implement a php script that connect this router address
then power-on/off one of these servers .
sorry i don’t know much more on networks.
but I’ve tried to do this using the command through the terminal.
and now i want to function these commands to be executed through a php script .
i’ve tried the libssh2 and phpseclib but they stuck with the execution .
could you help me in some resources to get the idea and know whats going with the connecting and the executing process ?
thanks in advance
#21 by Robin on 2010/11/26 - 02:17
Quote
“the fact that something is possible doesn’t make it a good idea.”
It’s called being creative, you know, creating stuff for fun.
You should try it and quit being so soulless, you never know, your life might improve.
#22 by Joshua Thijssen on 2010/11/26 - 03:02
Quote
A traceroute tool / tutorial is already there:
http://www.adayinthelifeof.nl/2010/07/30/creating-a-traceroute-program-in-php/
#23 by Montreal Web Design on 2011/01/13 - 16:52
Quote
re: pd
Not useless, I can see me using it in some scenarios.
#24 by Chetan on 2011/01/24 - 02:17
Quote
What’s so special about SSH…? PHP can do a lot…
PHP isn’t really the best choice for something like this though…
And why should you create a server when there are plenty already… They maintain their code and provide many more features?
Develop a better protocol, and I’ll call you creative.
I agree: “the fact that something is possible doesn’t make it a good idea.”
#25 by Rafe Kettler on 2011/01/24 - 02:38
Quote
PHP can do anything but make sense.
Why is it
strstr($haystack, $needle)andarray_search($needle, $haystack)? Even seasoned experts have to consult the manual because of inconsistent naming, ordering of arguments, etc.And no namespaces.
I admire your resolve for accomplishing anything substantial in PHP.