使用FTPS从安卓设备传输文件到服务器

7

我在我的安卓应用程序中使用Apache Commons FTP库,通过FTPS进行连接,虽然连接到服务器时完美的,但是在传输文件时有问题。

为了安全起见,订购该应用程序的客户要求在使用PROT P时请求数据连接上的TLS会话恢复。

因此,我在服务器上启用了此选项:

enter image description here

正如我所说,我可以连接到服务器,但无法传输文件。 如果取消“在使用PROT P时需要TLS会话恢复”框中的选项,则传输将正常工作。

我正在寻找一种使用该库进行文件传输的方法,但没有成功,不过我理解肯定有一种方法。

以下是相关代码部分:

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);

非常感谢任何的帮助,谢谢。


我找到了一篇文章,我认为它提供了一个解决方案,但我找不到应用它的方法。如何做也可以解决这个问题:http://eng.wealthfront.com/2016/06/10/connecting-to-an-ftps-server-with-ssl-session-reuse-in-java-7-and-8/#solution-identifier - Sergio76
那么我猜这个答案 https://dev59.com/3FwY5IYBdhLWcg3wpI8- 对你没有帮助? - NiVeR
2个回答

3
你的问题是 Apache FTPSClient 不支持TLS会话恢复,因此在尝试传输文件时会失败。
## 理解问题
当你通过TLS连接到FTP服务器时,服务器会在控制连接上与客户端建立一个安全的SSL会话。然后,客户端发送一个PASV命令,进入被动模式。作为响应,服务器会打开一个随机的非特权端口,并将端口号发送给客户端。该端口代表数据连接。现在,为了安全地连接到这个新的端口,客户端必须重用已经与服务器建立的控制连接上的现有TLS会话。
为什么要重用TLS会话呢?
不要求会话恢复会导致会话劫持攻击。FTP的问题在于数据连接未对客户端进行身份验证。
如果服务器/客户端不重用现有的TLS会话,则攻击者可能已经可以连接到数据端口并上传恶意软件。因此,为了防止此类攻击,FTP服务器要求客户端重用已建立的会话。
在您的情况下,Apache FTPSClient 无法重用会话(这是一个已知问题),因此服务器认为您的客户端未经授权并拒绝传输。
请查看Wealthfront post以了解如何打补丁和示例实现。
来源:

我得出的结论与此差不多,非常感谢。 - Sergio76
1
据我所见,FTPS/FTPES服务器还要求数据通道必须从与控制通道相同的IP地址发起才能使用。由于FTP运行在TCP上,因此您不必担心IP地址欺骗,因此此漏洞的攻击面减少到NAT网关(例如VPN、几乎所有人的防火墙等)。为了使此攻击生效(当不需要TLS会话恢复时),攻击者必须在受害者身后与相同的NAT网关处。 - Granger

2
你可以尝试以下代码,希望它对你的情况也适用。
该代码使用Apache Commons VSF2在安全ftp连接(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();
}

您可以在Apache Commons VFS文档中了解更多信息。

编辑过的内容

在理解FTPS背后的逻辑、@riyaz-ali的文章和您评论中的链接这篇文章之后,发现Apache FTP客户端存在问题,不支持TLS会话恢复。您可以修补现有的Apache Commons库实现。

您可以尝试以下代码步骤来使其正常工作:

  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.


这是一个适用于SFTP的解决方案,但我需要在FTPS下进行文件传输。无论如何,谢谢。 - Sergio76
遗憾的是,这个解决方案仍然不起作用。谢谢。 - Sergio76
我已经在桌面上的Java 8虚拟机(ubuntu)上成功测试了@NahidAli的解决方案,并且它运行良好。我已经上传了一个Gist,请查看。在Android上测试后,我会更新我的答案。 - riyaz-ali
我已经在使用Java 8更新161的Mac下尝试了相同的要点,但它不起作用 :( - Sergio76

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接