Scripting FTPES (Explicit TLS/SSL) with cURL
I thought I’d post quickly about automating FTP using explicit TLS/SSL with cURL. No, this isn’t a particularly advanced subject in the grand scheme of things (the command line is very simple) but some of the connectivity issues you might see if you’re doing this in an enterprise environment could boggle your mind, so I’ll try to explain them.
There are a number of flavours of file transfer with FTP. A few of the favourites are, of course FTP (in the clear FTP), SFTP (SSH File Transfer Protocol), FTPES (FTP over explicit TLS/SSL) and FTPS (FTP over implicit TLS/SSL).
For the purposes of this post, I’ll be concentrating on FTPES.
FTPES will happily listen on TCP/21 and generally, that is how it is implemented. The main reason for this is so that providers can offer normal (unsecured) FTP on TCP/21 at the same time as FTPES. When the client wishes to use a secure channel, it simply issues the AUTH TLS command and in most clients I have seen, the normal standard is for clients wishing to use FTPES to immediately send the AUTH TLS command when connected. A client wouldn’t (shouldn’t) authenticate in the clear and then ask for a secure connection, that would defeat the object of having a secure connection.
The only serious problem with this implementation of a secure FTP data transfer method is that modern Unified Threat Management (UTM) devices as deployed in enterprises the world over try to enhance security of FTP by watching for outbound connections to TCP/21 using an often well hidden feature called the “FTP helper service”. This is all well and good up to the point where you need to connect to an FTPES server…
When a typical FTP session begins, the client will connect to TCP/21 and issue the USER command, followed by the PASS command with everything sent in the clear. The client connects, downloads the list of files in the directory and the FTP helper service on the enterprise firewall is happy to deal with that data because it looks like it should, FTP data.
When a typical FTPES session begins, the client will connect to TCP/21 and issue the AUTH TLS or AUTH SSL command. The FTP helper service allows the command because it is sent in the clear. It is at this point that the command channel, and thus data, sent back by the server is encrypted. Unfortunately, it is because of this encrypted data that the FTP helper service spits out its dummy because it was expecting simple clear text FTP commands. As a result of seeing what the FTP helper service deems to be garbage (actually just encrypted data), the UTM device will kill the session.
There is nothing that can be done to resolve this situation without disabling the FTP helper service on the UTM device or (if it is powerful enough) by enabling it to process encrypted sessions using a transparent man-in-the-middle style interception (intercept-decrypt-analyse-reencrypt-forward).
If the provider of the service is offering FTPES on TCP/21 then there is a fairly safe bet they offer FTPS (Implicit TLS/SSL) on TCP/990 also. The only issue here is that there is a good chance that TCP/990 isn’t a port that your firewall administrators would typically allow you to connect to, so it becomes a tough situation to resolve satisfactorily.
The main issue here is that the FTP helper service is getting in the way of the secure connection. The best way for enterprise firewall admins to think of this problem is that it’s like trying to scan HTTPS traffic as it traverses TCP/80 and expecting it to be normal HTTP traffic. The engine dealing with the scanning of data sees garbage (because the data is encrypted) so it just drops the packet.
Unfortunately the list of options at this junction is very short and very ugly from a security perspective:
- Disable the FTP helper service on the UTM device – generally this is a non-starter because doing so effectively renders the firewall incapable of scanning normal FTP (unencrypted) data for everybody that might connect to an FTP site. The purpose behind this change is to prevent the UTM device from trying to scan the traffic as normal (in the clear) FTP traffic – for FTPES the traffic is encrypted, so it simply won’t work.
- Connect to the service provider’s FTPS service on TCP/990 – If they’re offering FTPES, they’re probably also offering FTPS (FTP over implicit TLS/SSL) too but you will need to persuade your firewall administrator that opening TCP/990 is OK. They will be able to secure the IP and port combination and the FTP helper service won’t interfere on this port so this is the preferred fix.
OK, and with that out of the way, if you don’t have an enterprise UTM device trying to be overly helpful, this is a typical cURL command line to upload a file to an FTPES server.
1 2 3 4 5 |
curl.exe -T "d:\myfile.txt" ftp://username:password@server.domain.com -k --ftp-ssl % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 315k 0 0 100 315k 0 89899 0:00:03 0:00:03 --:--:-- 90708 |
This will upload a file called myfile.txt from D:\ to ftp://server.domain.com (on the default port TCP/21!) and will authenticate as username with the password password, ignoring any certificate errors (-k) and authenticating using FTPES (–ftp-ssl).
Of course if you’re automating on Windows (like me) you’ll need a windows binary for cURL that has the SSL library compiled in. You can get that from the cURL downloads page. Scroll down to Win32 – Generic and select the binary with SSL support. [At the time of posting, the latest release is 7.21.0 and the binary I used was built by Günter Knauf]
Don’t forget to read the man[ual] page that comes with the download for more details on automating at the command line with cURL.
Hope this helps you!
EDIT: You may be concerned that when you connect to an FTPES or FTPS server using cURL that you have to specify the -k option to ignore certificate validation errors and it seems that the server’s certificate isn’t verified by a trusted root CA. This doesn’t necessarily mean that the server’s certificate is invalid. In the most recent binary builds, cURL no longer comes bundled with the public keys of valid root CAs like Verisign, Thawte etc. so, you have two options. 1. Use the VBScript that’s included with the binaries to generate your own (when I ran it, it didn’t work so YMMV) or, 2. Provide your own bundle of verified public root CA certificates by visiting the cURL website, click the “CA Extract” link in the menu, download the cacert.pem file in to the same directory as your cURL binary and rename it to curl-ca-bundle.crt. All of these instructions can be found at http://curl.haxx.se/docs/sslcerts.html which is also mentioned in the error message about the cert that cURL spat out when you connected to the server without the -k option.
-Lewis
Thanks for your help. This is exactly what I was trying to attempt by other inefficient means. Do you have twitter? I was going to follow you however I suppose I could subscribe via rss.
In my unix, I don’t have –ftp-ssl option. so how to specify? is that version issue?
The current version which I see in my unix envrionment is:
I need to use curl for FTPES
Version Info:
curl 7.10.3 (sparc-sun-solaris2.8) libcurl/7.10.3
any suggestion?
Hi Raja, it would probably mean that the version you have doesn’t support FTPS or FTPES as it wasn’t compiled with that library/option so that option simply doesn’t exist for you. You may need to compile it from source and include the necessary additional libraries and options to get a working binary for use with FTPS/FTPES servers.
Hey Lewis,
Thank you so much for your advise, that has saved me many hours and hair-pulling!
I had quite a big problem trying to connect to my web hosting provider’s FTP using FTPES for two reasons:
1. They were using older SSL certificates and I was using a Mac.
2. I don’t have fixed internet at home (ADSL etc), I use my phone on personal hotspot with Telstra (Australia) as my provider. This is where you saved the day!
Issue 1 was quite bizarre as I was successfully able to connect using FTPES on a Windows machine but not on a Mac. Turns out Apple are less forgiving when it comes to connecting over SSL where the certificates are not as strong as they could be. I spoke with my provider who give awesome support and they re-issued new and better certificates – great.
But then I still couldn’t connect at home. I realised I could if I used somebody else’s ADSL connection, but not with my phone connected. So your answers above explained it all. Thanks to Telstra for their over-zealous UTM. *sigh*
Issue 2 was fixed by my web hosting provider kindly opening another port and mapping it to port 21 on the FTP server – so this bypasses the UTM sniffing on port 21 connections. Problem solved and connection is encrypted.
Thanks again!
Rusty
Hello
Please, Can you rectify my script?
$Xftp = “ftp://perso-ftp.orange.fr/”
# ?????????????
$Xftp.UsePassive = $true
$Xftp.EnableTls = $true
$XlocalDirectory = “c:\studio\”
$Xuser = “………….”
$Xpass = “………….”
$Xwebclient = New-Object System.Net.WebClient
$Xwebclient.Credentials = New-Object System.Net.NetworkCredential($Xuser,$Xpass)
$XFiles = Get-ChildItem -Path “c:\studio\*” -Rec -For | ? {$_.LastWriteTime -gt (Get-Date).AddHours(-1)} | where { ! $_.PSIsContainer } | Select-Object FullName
$Xnombre = $XFiles.Count
foreach ($XFile in $XFiles)
{
$XLocalFile = $XFile.FullName
$XRemoveDirectory = $XLocalFile.Replace(“C:\studio\”,””)
$XChangeSlashes = $XRemoveDirectory.Replace(‘\’, ‘/’)
$XRemoveSpaces = $XChangeSlashes.Trim()
$XRemoteFile = $Xftp+$XRemoveSpaces
$Xuri = New-Object System.Uri(“$XRemoteFile”)
$Xwebclient.UploadFile($Xuri, $XLocalFile)
Write-Host “Getting $XFile from $XlocalDirectory” -Foreground “Red” -BackgroundColor DarkBlue
Write-Host “Puting $XFile to $Xftp” -Foreground “Yellow” -BackgroundColor DarkBlue
}
Write-Host “Finished Sync to $Xftp” -Foreground “Green” -BackgroundColor DarkBlue
“”
$Xnombre.ToString() + ” File sync — END — ” + (Get-Date)
$Xtext1 = $Xnombre.tostring() + ” File sync — END — ” + (Get-Date)
Many thanks for your help
Arnold
Thank you Lewis!
Je cherchais une solution pour uploader sur mes “pages perso orange” depuis des jours!
It works perfectly! Thanks thanks thanks!
Bruno