Sensitive data sent through Swift NWConnection without TLS or DTLS

Sensitive data sent through Swift NWConnection without TLS or DTLS

Description

Apple's Network.framework is a lower-level API for configuring protocols such as TCP, UDP, TLS, and DTLS. The URL Loading System's App Transport Security (ATS) does not apply to these connections, so the application must configure transport protection and peer authentication correctly.

Network.NWParameters.tls combines TLS with TCP, and Network.NWParameters.dtls combines DTLS with UDP. Network.NWParameters.tcp and Network.NWParameters.udp do not add those security protocols. The forms Network.NWParameters(tls: nil, tcp: ...) and Network.NWParameters(dtls: nil, udp: ...) explicitly omit them as well.

Before sending sensitive data, check the actual protocol and peer authentication. A send call alone does not establish that a connection succeeded or that independent application-layer protection is absent.

Potential impact

If the application protocol also lacks authenticated encryption:

  • Network observers or intermediaries may read credentials and personal information.
  • An active attacker may modify transmitted data.
  • An attacker may impersonate the server or peer.
  • Captured messages may be replayed if separate replay controls are absent.

Remediation

  1. For TCP, use Network.NWParameters.tls or a non-nil TLS option. For UDP, use Network.NWParameters.dtls or a non-nil DTLS option.
  2. Retain the system's default server-trust evaluation. Do not install a callback that accepts every certificate or peer.
  3. For HTTP and URL-based resources, use HTTPS with URLSession and minimize ATS exceptions.
  4. Minimize sensitive data in transit. If the threat model extends beyond TLS endpoints, add application-layer authenticated encryption with independent key management, unique nonces, and replay controls as defense in depth. It does not replace authenticated transport.
  5. Inspect network traffic from release builds to check for cleartext data and unexpected protocol paths.

Examples

Before

swift
import Dispatch
import Foundation
import Network

func sendCredential(clientSecret: Data) {
    let connection = Network.NWConnection(
        host: "api.example.com",
        port: 443,
        using: Network.NWParameters.tcp
    )
    let queue = DispatchQueue(label: "cleartext-connection")
    connection.start(queue: queue)
    connection.send(content: clientSecret, completion: .contentProcessed { _ in })
}

After

swift
import Dispatch
import Foundation
import Network

func sendCredential(clientSecret: Data) {
    let connection = Network.NWConnection(
        host: "api.example.com",
        port: 443,
        using: Network.NWParameters.tls
    )
    let queue = DispatchQueue(label: "tls-connection")
    connection.start(queue: queue)
    connection.send(content: clientSecret, completion: .contentProcessed { _ in })
}

Explanation:

  • Before: Port 443 does not cause Network.NWParameters.tcp to add TLS. Without an independently authenticated upper-layer protocol, clientSecret is unprotected.
  • After: Network.NWParameters.tls configures the default TLS/TCP stack. Keep server-trust evaluation enabled and handle connection states and send errors in the application.

References