Transfer files from android with FTPS to the server [duplicate]
Asked Answered
L

2

7

I am using the Apache Commons FTP library in my android application

I am making the connection through FTPS, and although it connects perfectly to the server, I have a problem when transferring files.

The client who orders the app, for security reasons, requests that the TLS session resumption on data connection be requested when using PROT P.

Therefore, I have this option enabled on the server:

enter image description here

As I said, I can connect to the server, but not transfer files. If I deactivate the "Required TLS session resumption on data connection when using PROT P" box, the transfer works correctly.

I'm looking for a way to make file transfers using the library, but without success, however, I understand that there must be a way.

I give you the part of the related code:

TransferImagenesFTP.ftpClient = new FTPSClient();

TransferImagenesFTP.ftpClient.connect(InetAddress.getByName("XXX_XXX_XX_X"), 26);
TransferImagenesFTP.ftpClient.enterLocalPassiveMode();
TransferImagenesFTP.ftpClient.setBufferSize(1024000);
TransferImagenesFTP.ftpClient.login("xxxxxx", "zzzzzz");
TransferImagenesFTP.ftpClient.execPROT("P");
TransferImagenesFTP.ftpClient.type(FTP.BINARY_FILE_TYPE);

I appreciate any help, thanks.

Lawsuit answered 9/2, 2018 at 10:52 Comment(3)
I found this article that I think facilitates a solution, but I can not find a way to apply it. The how to do it would also solve the bounty: eng.wealthfront.com/2016/06/10/…Lawsuit
So I guess that the answer #32399254 wasn''t helpful?Frame
@Lawsuit there is an issue with the wealthfront's solution, and that is the code assumes that ClientSessionContext has a field named sessionHostPortCache. However, on Android, this field does not exist (or it's private and inaccessible), so when your code tries to find it, it throws a NoSuchFieldException, meaning the field is not found.Koffler
F
3

The problem in your case is that the Apache FTPSClient doesn't support TLS session resumption and , thus, fails when you try to transfer the file.

Understanding the Problem

When you connect to an FTP server over TLS, the server intiates a secure ssl session with the client on the control connection. The client then enter passive mode by sending a PASV command and in response the server opens a random unprivileged port and sends in response the port number to the client. This port represents the data connection. Now to connect to this new port securely, the client must reuse the existing TLS session that it already have with the server on the control connection.

Why to reuse the TLS session?

Not requiring session resumption allows session stealing attacks. The problem with FTP is that the data connection does not authenticate the client.
If the server/client doesn't reuse the existing TLS session, it might have been possible for an attacker to connect to the data port instead and upload a malware. So to protect against such attack, the FTP server requires the client to reuse the already established session.

In your case, the Apache FTPSClient fails to reuse the session (it's a known issue) and thus the server thinks your client is unauthorized and denies the transfer.

Checkout the Wealthfront post on how to patch and a sample implementation.

Sources:

  • Wealthfront [link]
  • Slacksite [link]
  • FlieZilla Forum [link]
Fontanel answered 19/2, 2018 at 11:30 Comment(2)
It is more or less the same conclusion to which I had arrived, thank you very much.Lawsuit
From what I've seen, FTPS/FTPES servers additionally require a data channel to originate from the same IP address as the control channel in order for it to be used. Since FTP runs over TCP, you don't have to worry about IP address spoofing, so the attack surface on this vulnerability is reduced to NAT'ing gateways (e.g. VPNs, almost everyone's firewalls, etc.). In order for this attack to work (when TLS session resumption is not required), the attacker has to be behind the same NAT'ing gateway as the victim.Starve
G
2

You can try the following code, I hope it will work for your case too.

The code uses Apache Commons vsf2 for uploading file over secure ftp connection (SFTP)

try {
  String filepath = "<FILE PATH>";
  String serverAddress = "<FTP SERVER ADDRESS>";
  String userId = "<FTP USER ID>";
  String password = "<FTP PASSWORD>";
  String remoteDirectory = "<FTP DIRECTORY TO UPLOAD TO>";   
  String keyPath = "<PATH TO YOUR KEY>";   
  String passPhrase = "<PASSWORD FOR YOUR KEY>";   


  File file = new File(filepath);
  if (!file.exists())
    throw new RuntimeException("Error. File not found");

  //Initializes the file manager
  StandardFileSystemManager manager = new StandardFileSystemManager();
  manager.init();

  //Setup our SFTP configuration
  FileSystemOptions opts = new FileSystemOptions();
  SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(opts, "no");
  SftpFileSystemConfigBuilder.getInstance().setUserDirIsRoot(opts, true);
  SftpFileSystemConfigBuilder.getInstance().setTimeout(opts, 10000);

  // Create local file object
  FileObject localFile = manager.resolveFile(file.getAbsolutePath());

  // Create remote file object
  FileObject remoteFile = manager.resolveFile(createConnectionString(serverAddress, userId, password, keyPath, passPhrase, fileToFTP), createDefaultOptions(keyPath, passPhrase));


  // Copy local file to sftp server
  remoteFile.copyFrom(localFile, Selectors.SELECT_SELF);
  System.out.println("File upload successful");

}
catch (Exception ex) {
  ex.printStackTrace();
  return false;
}
finally {
  manager.close();
}

You can check more at Apache Commons VFS Documentation

Edited

After understanding the logic behind FTPS and the post by @riyaz-ali and referring to link in your comment to this article

There is a problem with Apache FTP client, it does not support TLS session resumption. You can patch the existing implementation of Apache Commons Library.

You can try the following code steps to get it working:

  1. Add the following patched class to in your project. (This class extends the existing FTPS implementation given in Apache commons with patch)

    import java.io.IOException;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.net.Socket;
    import java.util.Locale;
    
    import javax.net.ssl.SSLSession;
    import javax.net.ssl.SSLSessionContext;
    import javax.net.ssl.SSLSocket;
    
    import org.apache.commons.net.ftp.FTPSClient;
    
    import com.google.common.base.Throwables;
    
    public class PatchedFTPSClient extends FTPSClient {
    
            @Override
            protected void _prepareDataSocket_(final Socket socket) throws IOException {
                    if(socket instanceof SSLSocket) {
                            final SSLSession session = ((SSLSocket) _socket_).getSession();
                            final SSLSessionContext context = session.getSessionContext();
                            try {
                                    final Field sessionHostPortCache = context.getClass().getDeclaredField("sessionHostPortCache");
                                    sessionHostPortCache.setAccessible(true);
                                    final Object cache = sessionHostPortCache.get(context);
                                    final Method method = cache.getClass().getDeclaredMethod("put", Object.class, Object.class);
                                    method.setAccessible(true);
                                    final String key = String.format("%s:%s", socket.getInetAddress().getHostName(),
                                                                                                    String.valueOf(socket.getPort())).toLowerCase(Locale.ROOT);
                                    method.invoke(cache, key, session);
                            } catch(Exception e) {
                                    throw Throwables.propagate(e);
                            }
                    }
            }
    
    }
    
  2. Use this modified code snippet.

    TransferImagenesFTP.ftpClient = new PatchedFTPSClient();
    
    TransferImagenesFTP.ftpClient.connect(InetAddress.getByName<SERVER-ADDRESS>"), 26);
    TransferImagenesFTP.ftpClient.login("<USERNAME>", "<PASSWORD>");
    TransferImagenesFTP.ftpClient.execPBSZ(0);
    TransferImagenesFTP.ftpClient.execPROT("P");
    TransferImagenesFTP.ftpClient.enterLocalPassiveMode();
    
    //Now use the FTP client to upload the file as usual.
    

    Hope this will work for you and will solve your problem.

Gemperle answered 19/2, 2018 at 4:6 Comment(4)
This is a solution for use under SFTP, I would need the file transfer under FTPS. Thank you anyway.Lawsuit
Unfortunately, this solution is still not working. ThanksLawsuit
I have successfully tested the solution by @NahidAli on a Java 8 VM on desktop (ubuntu) and it's working fine. I've uploaded a Gist, check it out. I'll update my answer after testing it on Android.Fontanel
I have tried this same gist under mac with Java 8 update 161, and it does not work :(Lawsuit

© 2022 - 2024 — McMap. All rights reserved.