Question

I learned that using ftps (´ftp_ssl_connect()´) under PHP for Windows is tough. You are asked to enter a long journey of building your own binaries to include Open SSL... I found advice on phpseclib as a replacement (but getting openssl.cnf right to support peer validation sounds pretty hard to me, too...).

Then I hit this article and its pleasingly simple example including working peer validation (to avoid Man-in-the-middle-Attacks) drew my attention to streams:

$uri = 'https://www.bankofamerica.com/';
$ctx = stream_context_create(['ssl' => [
    'verify_peer' => true, // important
    'cafile' => '/hard/path/to/cacert.pem',
    'CN_match' => 'www.bankofamerica.com'
]]);
$html = file_get_contents($uri, FALSE, $ctx); // we are good

However, is this useful for ftp connections? Can I also use peer-validated stream contexts to open ftps streams? The php manual hints, ssl context options also apply to sftp, but lacks further guidance.

So I am wildly guessing:

$ctx = stream_context_create(['ftps' => [
    'verify_peer' => true,
    'cafile' => 'd:/sandbox/mycerts.pem',
    'CN_match' => 'ftp-12345678.mywebhoster.com'
]]);

Right? Wrong? User+Password as options now? And then what? User/Password now? Or later? I am clueless...

  • How do I scan the remote ftp directory contents to start with?
  • How to I upload or download a file from an ftp server?

Is any of that even possible? (A few lines of code would be very helpful....) Or are streams only good for singular file access?

Update: Curl might do what I want (including peer validation and directory lsiting) as shown in this SO answer. Will examine later this week...

Was it helpful?

Solution

Will this work?

Can I also use peer-validated stream contexts to open ftps streams?

Yes. The ftps stream wrapper utilizes the same SSL context options as the https wrapper and will be available as long as you have the openssl extension enabled in your PHP build. You can verify if the ftps wrapper is available by checking the output from stream_get_wrappers() like so:

<?php
print_r(stream_get_wrappers());

If you have ext/openssl enabled in your php build you'll see ftps listed in the output alongside the other available stream wrappers.

How do I assign the SSL context options?

So I am wildly guessing

You're really close! The only thing you need to change in your code is to replace "ftps" with "ssl" as shown here:

<?php
$ctx = stream_context_create(['ssl' => [
    'verify_peer' => true,
    'cafile' => 'd:/sandbox/mycerts.pem',
    'CN_match' => 'ftp-12345678.mywebhoster.com'
]]);

Regardless of whether you're using https, ftps or any other stream wrapper the context options governing SSL/TLS encryption are always stored in the "ssl" key.

Where do I put the user/password?

Right? Wrong? User+Password as options now? And then what? User/Password now? Or later? I am clueless...

The ftp and ftps stream wrappers both expect the username and password in the URI as shown here:

<?php
$ftpPath = 'ftps://username:password@example.com';

Don't be thrown off by our specification of the user/pass in cleartext here. The stream wrapper will only send the username and password after an encrypted connection is established.

Putting it all together

The opendir() family of functions supports the ftp wrapper (since PHP 5.0). You use these functions the same way you would with local filesystem paths:

<?php
$ctx = stream_context_create(['ssl' => [
    'verify_peer' => true,
    'cafile' => 'd:/sandbox/mycerts.pem',
    'CN_match' => 'ftp-12345678.mywebhoster.com'
]]);
$dirHandle = opendir('ftps://username:password@example.com/', $ctx);
while (($file = readdir($dirHandle)) !== false) {
    echo "filename: $file\n";
}
closedir($dirHandle);

Note on SSL/TLS name matching

If it doesn't work initially you should test without passing the additional context $ctx containing the SSL options. The CN (common name) field of the server's certificate must match the "CN_match" value you specify (with limited wildcard matching for subdomains). Also, prior to the forthcoming PHP-5.6 release there is no support for matching names against the Subject Alternative Name field in the remote party's certificate. Unless you're working with a development preview for 5.6 you won't have this capability (SAN matching) and the peer verification routine will fail if the server relies on SAN.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top