My post from yesterday: https://stackoverflow.com/questions/14296006/phpseclib-sftp-port-number
Ok, so yesterday I started learning about SSH / SFTP with php. I searched a bunch of forum posts and surmised that i needed to download the phpseclib.
Being relatively new to php thus starting on php5 i was not aware of previous php4's non-use of the __constructor, hence the above question/post.
The responses were conflicting, and a little off topic to the original Q however has delivered me to a question that I feel needs answering before i continue:
Which is better to use, ssh2 pecl extension OR phpseclib?
This question: phpseclib vs libssh2 is the same but I feel a little outdated now as asked on Nov 5 '10 at 17:37
I say phpseclib. Personally, I think you get better support with phpseclib and that the API is better but there are less subjective reasons too:
More portable.
I tried to install libssh2-php on Ubuntu 12.04 and "sudo apt-get install libssh2-php" didn't work for me. And even if it did it likely wouldn't get the latest version. So I had to compile libssh2 and the PECL extension myself which is always a hassle and not something a lot of admins are going to be willing to do.
And even if you are willing to compile stuff let's say your hard drive fails and you have to rebuild the server. If you're willing to compile libssh2 you'll have probably compiled other stuff too. Which means you can't just fire up another box - you have to remember all the changes you made on your old box and reapply them. And what if you don't remember them? Or what if one of them hasn't been updated to work with the latest version of another?
phpseclib, in contrast, doesn't require much of anything other than PHP. It'll use mcrypt, gmp, bcmath or openssl if they're available but if they're not, that's okay, too. And it doesn't even require PHP5, although it certainly supports it.
Better public key support.
How you do it with libssh2:
<?php
$ssh = ssh2_connect('domain.tld');
ssh2_auth_pubkey_file($ssh, 'username', '/home/ubuntu/pubkey', '/home/ubuntu/privkey'/*, 'password'*/);
$stream = ssh2_exec($ssh, 'ls -la');
echo stream_get_contents($stream);
Both have to be of the right format too. If you didn't use ssh-keygen to generate your keys good luck in converting them.
With phpseclib:
<?php
include('Net/SSH2.php');
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
$rsa->loadKey('...');
$ssh = new Net_SSH2('domain.tld');
$ssh->login('username', $rsa);
//$ssh->setPassword('password');
echo $ssh->exec('ls -la');
Ignoring the API for the time being there are a few clear ways phpseclib comes out on top here:
phpseclib takes in strings - not file paths. If you want to do a file you can do file_get_contents.
phpseclib doesn't require a public key. Most private key formats have the public key embedded within them. And if they don't... phpseclib supports that too.
phpseclib can take in pretty much any standardized format, from PKCS#1 formatted keys, to PuTTY keys, to XML Signature keys
Interactive shell.
Let's try to do sudo on the remote system.
With phpseclib:
http://phpseclib.sourceforge.net/ssh/examples.html#password,sudo,
With libssh2? I have no clue. My best guess (doesn't work):
<?php
$ssh = ssh2_connect('domain.tld');
ssh2_auth_password($ssh, 'username', 'password');
$shell = ssh2_shell($ssh);
echo fread($shell, 1024*1024);
fwrite($shell, "sudo ls -la\n");
$output = fread($shell, 1024*1024);
echo $output;
if (preg_match('#[pP]assword[^:]*:#', $output)) {
fwrite($shell, "password\n");
}
echo fread($shell, 1024*1024);
I can't get top to work with libssh2 either but it works fine with phpseclib:
http://phpseclib.sourceforge.net/ssh/examples.html#password,top,
Diagnosing problems
Why didn't top or sudo work? On phpseclib you can get logs:
http://phpseclib.sourceforge.net/ssh/examples.html#password,oneoff,logging,
They look like this:
http://phpseclib.sourceforge.net/ssh/log.txt
You can also do print_r($ssh->getErrors()) or echo $ssh->getLastError().
Changing Directories
I don't see any cd or chdir functions at http://php.net/ssh2 . phpseclib, however, has it - Net_SFTP::chdir(...)
Speed
libssh2:
<?php
$ssh = ssh2_connect('domain.tld');
ssh2_auth_password($ssh, 'username', 'password');
$start = microtime(true);
$sftp = ssh2_sftp($ssh);
$fp = fopen('ssh2.sftp://'.$sftp.'/home/username/1mb', 'w');
fwrite($fp, str_repeat('a', 1024 * 1024));
$elapsed = microtime(true) - $start;
echo "took $elapsed seconds";
25.71 seconds.
phpseclib:
<?php
include('Net/SFTP.php');
$sftp = new Net_SFTP('domain.tld');
$sftp->login('username', 'password');
$start = microtime(true);
$sftp->put('1mb', str_repeat('a', 1024*1024));
$elapsed = microtime(true) - $start;
echo "took $elapsed seconds";
11.70 seconds.
So phpseclib is more than twice as fast.
Related
I have came across many PHP scripts for web FTP clients. I need to implement a SFTP client as a web application in PHP.
Does PHP support for SFTP? I couldn't find any samples.
Can anyone help me with this?
PHP has ssh2 stream wrappers (disabled by default), so you can use sftp connections with any function that supports stream wrappers by using ssh2.sftp:// for protocol, e.g.
file_get_contents('ssh2.sftp://user:pass#example.com:22/path/to/filename');
or - when also using the ssh2 extension
$connection = ssh2_connect('shell.example.com', 22);
ssh2_auth_password($connection, 'username', 'password');
$sftp = ssh2_sftp($connection);
$stream = fopen("ssh2.sftp://$sftp/path/to/file", 'r');
See http://php.net/manual/en/wrappers.ssh2.php
On a side note, there is also quite a bunch of questions about this topic already:
https://stackoverflow.com/search?q=sftp+php
The ssh2 functions aren't very good. Hard to use and harder yet to install, using them will guarantee that your code has zero portability. My recommendation would be to use phpseclib, a pure PHP SFTP implementation.
I found that "phpseclib" should help you with this (SFTP and many more features). http://phpseclib.sourceforge.net/
To Put the file to the server, simply call (Code example from http://phpseclib.sourceforge.net/sftp/examples.html#put)
<?php
include('Net/SFTP.php');
$sftp = new Net_SFTP('www.domain.tld');
if (!$sftp->login('username', 'password')) {
exit('Login Failed');
}
// puts a three-byte file named filename.remote on the SFTP server
$sftp->put('filename.remote', 'xxx');
// puts an x-byte file named filename.remote on the SFTP server,
// where x is the size of filename.local
$sftp->put('filename.remote', 'filename.local', NET_SFTP_LOCAL_FILE);
Install Flysystem v1:
composer require league/flysystem-sftp
Then:
use League\Flysystem\Filesystem;
use League\Flysystem\Sftp\SftpAdapter;
$filesystem = new Filesystem(new SftpAdapter([
'host' => 'example.com',
'port' => 22,
'username' => 'username',
'password' => 'password',
'privateKey' => 'path/to/or/contents/of/privatekey',
'root' => '/path/to/root',
'timeout' => 10,
]));
$filesystem->listFiles($path); // get file lists
$filesystem->read($path_to_file); // grab file
$filesystem->put($path); // upload file
....
Read:
https://flysystem.thephpleague.com/v1/docs/
Upgrade to v2:
https://flysystem.thephpleague.com/v2/docs/advanced/upgrade-to-2.0.0/
Install
composer require league/flysystem-sftp:^2.0
Then:
//$filesystem->listFiles($path); // get file lists
$allFiles = $filesystem->listContents($path)
->filter(fn (StorageAttributes $attributes) => $attributes->isFile());
$filesystem->read($path_to_file); // grab file
//$filesystem->put($path); // upload file
$filesystem->write($path);
After messing around with PECL ssh2 I decided to take a look at phpseclib 3 and it worked out of the box. No installation on the server. I used composer to install it and put the code in. It has a ton of useful things and it's free. These are the steps:
Run this composer install on your PHP app folder. I used VS Code and I opened a terminal window (need Composer to be installed on your machine first):
composer require phpseclib/phpseclib:~3.0
Use the basic example from here: https://phpseclib.com/docs/sftp
use phpseclib3\Net\SFTP;
$sftp = new SFTP('localhost');
$sftp->login('username', 'password');
$sftp->put('filename.remote', 'filename.local', SFTP::SOURCE_LOCAL_FILE);
Other useful links: GitHub: https://github.com/phpseclib/phpseclib and website: https://phpseclib.com/
I performed a full-on cop-out and wrote a class which creates a batch file and then calls sftp via a system call. Not the nicest (or fastest) way of doing it but it works for what I need and it didn't require any installation of extra libraries or extensions in PHP.
Could be the way to go if you don't want to use the ssh2 extensions
I'm having trouble with ssh2_sftp_rename, or in general with the SSH2/SFTP wrapper.
We have two SFTP servers. One is a test server, which we've setup by ourself, and one is from a service provider. On the test server, everything works fine while on the provided SFTP we cannot rename a file via PHP. Manual renaming via sftp cli works fine. Our SFTP server uses the protocol version 2.0 and remote software version OpenSSH_6.0p1 Debian-4, while the other one uses protocol version 2.0, remote software version OpenSSH_5.3. So I'd guess they should act the same.
Here's the smallest code snippet for testing, which doesn't work:
<?php
$connection = ssh2_connect('sftp.serviceprovider.com', 22);
ssh2_auth_password($connection, 'username', 'password');
$sftp = ssh2_sftp($connection);
$result = ssh2_sftp_rename($sftp, '/new/foo.xml.gz', '/processed/foo.xml.gz');
var_dump($result);
var_dump(error_get_last());
$result is false and error_get_last() returns NULL. I also tried leaving the beginning slashes or any other combination I can think of, but it just "didn't work": the file is still in the 'new' folder.
So, unfortunately I cannot take a look into the log files of the service provider but I have requested an log excerpt or at least an analysis. But I don't want to wait for their support center to answer my question and try to debug this stuff.
Is there any possibility to debug what ssh2_sftp_rename or renameare doing behind the scenes? Does anyone else experienced something like this and found a solution?
Thanks for any hints,
Matthias
Diagnosing issues with libssh2 is nigh impossible. My recommendation: use phpseclib, a pure PHP SFTP implementation. eg.
<?php
include('Net/SFTP.php');
include('Crypt/RSA.php');
define('NET_SFTP_LOGGING', NET_SFTP_LOG_COMPLEX);
$sftp = new Net_SFTP('www.domain.tld');
if (!$sftp->login('username', 'password')) {
exit('Login Failed');
}
$sftp->rename('filename.remote', 'newname.remote');
echo $sftp->getSFTPLog();
?>
In lieu of seeing the logs my guess would be a permissions issue but the logs should tell us for sure.
I have been told this cannot be done but I want to get some other opinions here. I am a bit of a newbie when it comes to things like this.
My Site: ExampleSiteA.com
File to download: ExampleSiteB.com
Basically, I am downloading a csv file from ExampleSiteB.com to make updates to my site, ExampleSiteA.com. To do this, I am downloading the csv file manually through CoreFTP and then uploading it manually to ExampleSiteA.com. The file changes daily and I would like to skip this step so I can automate the process.
Keep in mind that I need to download the csv file from ExampleSiteB.com through SFTP...
I am not sure if it is possible to directly download/upload a file from one server to another if one is SFTP. The file size is also quite large, it averages about 25,000 KB / 25 MB.
Another option that I haven't explored yet is requiring or including a file from another server... is that an option or a possibility? The file is located in a folder exclusively for my site and a login is required for SFTP download.
Any insight will be appreciated. Thanks in advance!
Go here and download what you need: http://phpseclib.sourceforge.net/
UPDATE
FOR SFTP
Then in your script:
<?php
include('Net/SFTP.php');
$url = 'http://www.downloadsite.com';
$fileToDownload = "yourCSV.csv";
$cmd = "wget -q \"$url\" -O $fileToDownload";
exec($cmd);
$sftp = new Net_SFTP('www.uploadsite.com');
if (!$sftp->login('username', 'password')) {
exit('Login Failed');
}
echo $sftp->pwd() . "\r\n";
$sftp->put('remote.file.csv', 'yourCSV.csv', NET_SFTP_LOCAL_FILE);
print_r($sftp->nlist());
?>
If you need to connect to a second server for download:
$sftp2 = new Net_SFTP('www.serverFromWhichToDownload.com');
if (!$sftp2->login('username', 'password')) {
exit('Login Failed');
}
echo $sftp2->pwd() . "\r\n";
$sftp2->get('localFileName.csv', 'remoteFileName.csv');
print_r($sftp2->nlist());
Read the docs for further help and examples: http://phpseclib.sourceforge.net/documentation/net.html#net_sftp_get
To Log what your connection is doing if it fails, etc. use this:
include('Net/SSH2.php');
define('NET_SSH2_LOGGING', true);
$ssh = new Net_SSH2('www.domain.tld');
$ssh->login('username','password');
echo $ssh->getLog();
FOR FTP upload - SO has gone crazy, does not want to format my code, but here it is anyway:
$file = 'somefile.txt';
$remote_file = 'readme.txt';
$conn_id = ftp_connect($ftp_server);
$login_result = ftp_login($conn_id, $ftp_user_name, $ftp_user_pass);
if (ftp_put($conn_id, $remote_file, $file, FTP_ASCII)) {
echo "successfully uploaded $file\n";
} else {
echo "There was a problem while uploading $file\n";
}
ftp_close($conn_id);
Yes, that's possible using ssh2_sftp.
http://php.net/manual/en/function.ssh2-sftp.php
I have had good luck with cURL in the past. If you are on a Linux box, it would be trivial to set up a CRON job to do this update process for you. A good reference for CLI HTTP scripting in cURL can be found here, however you may need the -T flag (for file transport) to accomplish the upload portion. Speaking of uploading, if you can run the script/process/crontab from the server you would like to update, I would recommend downloading from the web server to obviate one trip and a third party. Or, if you need to update on demand, you could write a PHP script that uses the built in PHP cURL functions. If you take the Linux+CLI route, you could also use sftp.
Update: In testing curl with sftp (curl -u uname:pword sftp://domain.tld) I get the following error: curl: (1) Protocol sftp not supported or disabled in libcurl on Kubuntu 12.04. So cURL may not be a good idea. I also tested CLI sftp (sftp uname#domain.tld:/dir/file.ext) but could not find a way (short of using ssh keys) to send authentication. Thus, this would necessarily be a manual process unless you did set up ssh keys between the servers. As it does not sound like you have that kind of access to ExampleSiteB.com, this probably isn't acceptable.
Update 2: Since my initial answer turned out to be of little use, I figured I would expand upon one of the above answers. I was trying to find a solution that did not involve a PECL extension, but I did not have much luck with ftp_ssh_connect(). I recommend trying it, you may have better luck and could forgo the PECL extension route.
Sigh, on further reading, it appears ftp_ssh_connect is, understandably, incompatible with the sftp protocol. However, I found a nice blog post about utilizing ssh2_connect() and ssh2_sftp() (as mentioned in a previous answer) and figured I would post that to give you some additional assistance. It is not as simple as calling the functions for most PHP distributions. Here is the blog post. Some of those steps may not be necessary or you may need to do some additional things listed in another blog post I ran across, here.
On my system, all I had to do was run apt-get install libssh2-1-dev libssh2-php and I was able to find ssh2 in my php -m output.
Having an include, as long as you have read/write permissions on the website you're getting the file from should work, however this is just guess work atm as i don't have any means of checking it. Good luck though!
Yes, you should be able to do this.
Whoever told you that you can't do this might be getting confused with JavaScript and cross-site scripting browser restrictions which prevent JavaScript downloaded from one domain to access content in a different domain.
That being said, if you are using PHP which to me implies that you are talking about PHP running on a web sever, you should be able to use PHP or any other scripting or programming language to download the file from SiteB.com, then update the file, and then finally FTP the file to a different web server (SiteA.com).
I've been looking for a way to use PHP's SSH2 to create a sort of terminal. To connect to Amazon in a normal terminal, you would use something like ssh -i path_to/key.pem ec2.ip-555-xxx.com. In PHP on the other hand, SSH2 has a function ssh2_auth_pubkey_file . But have run into a bit of a wall here, as Amazon only provides me with 1 private key (.pem) file, and the function has arguments for both private and public keys. Ultimately I'd like to have a client upload a .pem file to the server and be able to connect to a local or remote SSH server with PHP SSH2 on Amazon using that .pem file.
.pem is the server certificate for the apache web server, it has nothing to do with ssh. See: https://serverfault.com/questions/9708/what-is-a-pem-file-and-how-does-it-differ-from-other-openssl-generated-key-file-f
Seems it might also be a combo file with public and private keys in it. In any case it will not work for ssh directly, you will need to convert it to normal files.
You have it backward anyway - amazon will not give you the private key, quite the opposite - you give amazon the public key. You generate the private/public key pair locally, then upload the public key into the .ssh/authorize_keys files.
Personally, I'd recommend phpseclib, a pure PHP SSH2 implementation be used:
<?php
include('Net/SSH2.php');
$key = new Crypt_RSA();
//$key->setPassword('whatever');
$key->loadKey(file_get_contents('privatekey'));
$ssh = new Net_SSH2('www.domain.tld');
if (!$ssh->login('username', $key)) {
exit('Login Failed');
}
echo $ssh->exec('ls -la');
?>
This is how you get the private and public keys from .pem within php
$eKey = file_get_contents('/pathto/key.pem');
$key_private = openssl_get_privatekey($eKey);
$keyDet=openssl_pkey_get_details($key_private);
$key_public = openssl_pkey_get_public(array($keyDet['key'],""));
$keyPDet=openssl_pkey_get_details($key_public);
I have came across many PHP scripts for web FTP clients. I need to implement a SFTP client as a web application in PHP.
Does PHP support for SFTP? I couldn't find any samples.
Can anyone help me with this?
PHP has ssh2 stream wrappers (disabled by default), so you can use sftp connections with any function that supports stream wrappers by using ssh2.sftp:// for protocol, e.g.
file_get_contents('ssh2.sftp://user:pass#example.com:22/path/to/filename');
or - when also using the ssh2 extension
$connection = ssh2_connect('shell.example.com', 22);
ssh2_auth_password($connection, 'username', 'password');
$sftp = ssh2_sftp($connection);
$stream = fopen("ssh2.sftp://$sftp/path/to/file", 'r');
See http://php.net/manual/en/wrappers.ssh2.php
On a side note, there is also quite a bunch of questions about this topic already:
https://stackoverflow.com/search?q=sftp+php
The ssh2 functions aren't very good. Hard to use and harder yet to install, using them will guarantee that your code has zero portability. My recommendation would be to use phpseclib, a pure PHP SFTP implementation.
I found that "phpseclib" should help you with this (SFTP and many more features). http://phpseclib.sourceforge.net/
To Put the file to the server, simply call (Code example from http://phpseclib.sourceforge.net/sftp/examples.html#put)
<?php
include('Net/SFTP.php');
$sftp = new Net_SFTP('www.domain.tld');
if (!$sftp->login('username', 'password')) {
exit('Login Failed');
}
// puts a three-byte file named filename.remote on the SFTP server
$sftp->put('filename.remote', 'xxx');
// puts an x-byte file named filename.remote on the SFTP server,
// where x is the size of filename.local
$sftp->put('filename.remote', 'filename.local', NET_SFTP_LOCAL_FILE);
Install Flysystem v1:
composer require league/flysystem-sftp
Then:
use League\Flysystem\Filesystem;
use League\Flysystem\Sftp\SftpAdapter;
$filesystem = new Filesystem(new SftpAdapter([
'host' => 'example.com',
'port' => 22,
'username' => 'username',
'password' => 'password',
'privateKey' => 'path/to/or/contents/of/privatekey',
'root' => '/path/to/root',
'timeout' => 10,
]));
$filesystem->listFiles($path); // get file lists
$filesystem->read($path_to_file); // grab file
$filesystem->put($path); // upload file
....
Read:
https://flysystem.thephpleague.com/v1/docs/
Upgrade to v2:
https://flysystem.thephpleague.com/v2/docs/advanced/upgrade-to-2.0.0/
Install
composer require league/flysystem-sftp:^2.0
Then:
//$filesystem->listFiles($path); // get file lists
$allFiles = $filesystem->listContents($path)
->filter(fn (StorageAttributes $attributes) => $attributes->isFile());
$filesystem->read($path_to_file); // grab file
//$filesystem->put($path); // upload file
$filesystem->write($path);
After messing around with PECL ssh2 I decided to take a look at phpseclib 3 and it worked out of the box. No installation on the server. I used composer to install it and put the code in. It has a ton of useful things and it's free. These are the steps:
Run this composer install on your PHP app folder. I used VS Code and I opened a terminal window (need Composer to be installed on your machine first):
composer require phpseclib/phpseclib:~3.0
Use the basic example from here: https://phpseclib.com/docs/sftp
use phpseclib3\Net\SFTP;
$sftp = new SFTP('localhost');
$sftp->login('username', 'password');
$sftp->put('filename.remote', 'filename.local', SFTP::SOURCE_LOCAL_FILE);
Other useful links: GitHub: https://github.com/phpseclib/phpseclib and website: https://phpseclib.com/
I performed a full-on cop-out and wrote a class which creates a batch file and then calls sftp via a system call. Not the nicest (or fastest) way of doing it but it works for what I need and it didn't require any installation of extra libraries or extensions in PHP.
Could be the way to go if you don't want to use the ssh2 extensions