logo

Database

Use of insecure channel - Source code

Need

Transmission of credentials and files only over encrypted connections

Context

• Usage of Kotlin 1.9+ on the JVM for building application services

• Usage of the Apache Commons Net library for FTP and SMTP connections

Description

1. Non compliant code

import org.apache.commons.net.ftp.FTP
import org.apache.commons.net.ftp.FTPClient
import java.io.File

fun uploadReport(report: File, user: String, password: String) {
    // Credentials and file contents travel in clear text
    val ftp = FTPClient()
    ftp.connect("ftp.partner.example.com")...

The `uploadReport` function below sends a file to a partner with `FTPClient` from Apache Commons Net, logging in with a user name and a password. Plain FTP sends everything in clear text: the `USER` and `PASS` commands with the credentials, and the file on the data channel. Anyone on the network path, such as a compromised router, a shared Wi-Fi network or a cloud neighbor with access to the traffic, can capture the credentials and the report, or modify the file in transit. `SMTPClient` and `TelnetClient` from the same library have the same problem, and so does an OkHttp client configured with `ConnectionSpec.CLEARTEXT`, which allows plain HTTP.

2. Steps

• Replace `FTPClient` with `FTPSClient` and call `execPBSZ(0)` and `execPROT("P")` after login, or use SFTP over SSH.

• Replace `SMTPClient` with `SMTPSClient`, or with `AuthenticatingSMTPClient` and `execTLS()` before authenticating.

• Remove `TelnetClient` and use SSH for remote shells.

• Enable host name verification with `setEndpointCheckingEnabled(true)` and keep the default trust store.

• Do not use `ConnectionSpec.CLEARTEXT` in OkHttp clients outside of local development.

3. Secure code example

import org.apache.commons.net.ftp.FTP
import org.apache.commons.net.ftp.FTPSClient
import java.io.File

fun uploadReport(report: File, user: String, password: String) {
    // Implicit TLS protects the login; PROT P encrypts the data channel too
    val ftps = FTPSClient(true)
    ftps.setEndpointCheckingEnabled(true)...

The corrected function uses `FTPSClient` with implicit TLS (`FTPSClient(true)`), so the TLS handshake happens before any FTP command and the credentials are never sent in clear text. After logging in, it sends `PBSZ 0` and `PROT P`, which switch the data channel to TLS as well; without them, FTPS protects the password but still transfers the file unencrypted. It also enables host name verification, so the client only accepts a certificate issued for the partner's server. When the partner supports it, SFTP over SSH, for example with the `sshj` library, is a simpler alternative. For email, `SMTPSClient` or `AuthenticatingSMTPClient` with `execTLS()` replaces `SMTPClient`, and Telnet should be replaced with SSH, since it has no secure variant.