Cleartext Transmission of Sensitive Information

ID

swift.sensitive_information_cleartext_transmission

Severity

low

Resource

Information Leak

Language

Swift

Tags

CWE:319, MASVS:NETWORK-1, MASWE:0050, NIST.SP.800-53, OWASP:2021:A2, PCI-DSS:4.1

Description

Transmitting sensitive data over unencrypted network connections exposes it to interception by attackers. When applications send passwords, authentication tokens, credit card numbers, personal health information, or other sensitive data over HTTP (cleartext) connections, attackers on the same network can capture and read this information.

This rule detects when sensitive information is transmitted without encryption, specifically when:

  • The transmitted data contains sensitive keywords (password, token, creditCard, ssn, etc.)

  • The connection uses HTTP instead of HTTPS

  • No encryption is applied to the data before transmission

Rationale

Network traffic transmitted without encryption is vulnerable to several attack vectors:

  • Man-in-the-Middle (MITM) Attacks:

import Foundation

class VulnerableAuthService {
    // VULNERABLE: Sending password over HTTP
    func login(username: String, password: String) {
        let loginURL = "http://api.example.com/login" // HTTP - not HTTPS!
        guard let url = URL(string: loginURL) else { return }

        var request = URLRequest(url: url)
        request.httpMethod = "POST"

        // FLAW: Sensitive password transmitted in cleartext
        let body = "username=\(username)&password=\(password)"
        request.httpBody = body.data(using: .utf8)

        // ISSUE: Password can be intercepted on the network
        URLSession.shared.dataTask(with: request) { data, response, error in
            // Handle response
        }.resume()
    }

    // VULNERABLE: API token in HTTP request
    func fetchUserData(apiToken: String) {
        let endpoint = "http://api.example.com/user/profile"
        guard let url = URL(string: endpoint) else { return }

        var request = URLRequest(url: url)
        // FLAW: API token in Authorization header over HTTP
        request.addValue("Bearer \(apiToken)", forHTTPHeaderField: "Authorization")

        URLSession.shared.dataTask(with: request) { data, response, error in
            // Token exposed in cleartext
        }.resume()
    }

    // VULNERABLE: Credit card data over HTTP
    func submitPayment(creditCardNumber: String, cvv: String) {
        let paymentURL = "http://payments.example.com/process"
        guard let url = URL(string: paymentURL) else { return }

        let paymentData = [
            "cardNumber": creditCardNumber, // FLAW: PCI-DSS violation
            "cvv": cvv
        ]

        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.httpBody = try? JSONSerialization.data(withJSONObject: paymentData)

        // ISSUE: Credit card data transmitted in cleartext
        URLSession.shared.dataTask(with: request) { data, response, error in
            // Payment info exposed
        }.resume()
    }
}
  • Alamofire with HTTP:

import Alamofire

class AlamofireVulnerableService {
    // VULNERABLE: Alamofire request with sensitive data over HTTP
    func authenticateUser(email: String, password: String) {
        let loginEndpoint = "http://api.example.com/auth/login" // HTTP!

        let parameters: [String: String] = [
            "email": email,
            "password": password // FLAW: Password in HTTP POST
        ]

        // ISSUE: Alamofire sends password over unencrypted connection
        AF.request(loginEndpoint, method: .post, parameters: parameters).response { response in
            // Password exposed in network traffic
        }
    }

    // VULNERABLE: API key in HTTP headers
    func fetchData(apiKey: String) {
        let endpoint = "http://api.example.com/data"

        let headers: HTTPHeaders = [
            "X-API-Key": apiKey // FLAW: API key over HTTP
        ]

        AF.request(endpoint, headers: headers).responseJSON { response in
            // API key can be captured
        }
    }

    // VULNERABLE: SSN transmitted over HTTP
    func updateProfile(ssn: String) {
        let profileURL = "http://api.example.com/profile"

        AF.request(profileURL, method: .put, parameters: ["ssn": ssn]).response { response in
            // ISSUE: Social Security Number in cleartext
        }
    }
}
  • Vapor Server-Side HTTP Client:

import Vapor

class VaporVulnerableClient {
    // VULNERABLE: Vapor client sending sensitive data over HTTP
    func sendWebhook(req: Request, authToken: String) async throws {
        let webhookURL = "http://partner-api.com/webhook" // HTTP!

        let payload = [
            "token": authToken, // FLAW: Auth token over HTTP
            "event": "user_created"
        ]

        // ISSUE: Sending auth token in cleartext
        let response = try await req.client.post(URI(string: webhookURL)) { webhookReq in
            try webhookReq.content.encode(payload)
        }
    }

    // VULNERABLE: Health data over HTTP
    func syncHealthData(req: Request, healthRecords: [String: Any]) async throws {
        let syncURL = "http://health-sync.example.com/upload"

        // FLAW: PHI (Protected Health Information) over HTTP - HIPAA violation
        let response = try await req.client.post(URI(string: syncURL)) { syncReq in
            try syncReq.content.encode(healthRecords)
        }
    }
}
  • Network Framework (NWConnection):

import Network

class NetworkConnectionService {
    // VULNERABLE: NWConnection without TLS
    func sendData(password: String) {
        // HTTP endpoint (port 80) without TLS
        let endpoint = NWEndpoint.hostPort(host: "api.example.com", port: 80)

        // FLAW: Parameters without TLS
        let parameters = NWParameters.tcp

        let connection = NWConnection(to: endpoint, using: parameters)

        connection.start(queue: .global())

        // ISSUE: Sending password without encryption
        let data = "password=\(password)".data(using: .utf8)!
        connection.send(content: data, completion: .contentProcessed { error in
            // Password transmitted in cleartext
        })
    }

    // VULNERABLE: Token over insecure connection
    func authenticateWithToken(authToken: String) {
        let host = NWEndpoint.Host("api.example.com")
        let port = NWEndpoint.Port(rawValue: 8080)!
        let endpoint = NWEndpoint.hostPort(host: host, port: port)

        // No TLS configured
        let connection = NWConnection(to: endpoint, using: .tcp)
        connection.start(queue: .main)

        // FLAW: Auth token sent without encryption
        let tokenData = "Authorization: Bearer \(authToken)".data(using: .utf8)!
        connection.send(content: tokenData, completion: .idempotent)
    }
}

These examples demonstrate cleartext transmission vulnerabilities:

  1. HTTP URLs: Using http:// instead of https:// protocol

  2. Sensitive parameters: Passwords, tokens, API keys in request bodies or headers

  3. Financial data: Credit card numbers, CVV codes transmitted without encryption

  4. Personal data: SSN, health records, location data over insecure connections

  5. No TLS configuration: TCP connections without TLS in Network framework

Security Impact:

  • Passive eavesdropping: Attackers on the same network (WiFi, ISP) can capture traffic

  • Man-in-the-Middle: Attackers can intercept and modify requests/responses

  • Session hijacking: Authentication tokens stolen from cleartext traffic

  • Credential theft: Usernames and passwords captured from login requests

  • Compliance violations: PCI-DSS, HIPAA, GDPR require encryption for sensitive data

Remediation

Always use HTTPS (TLS/SSL) for transmitting sensitive data. iOS and macOS enforce App Transport Security (ATS) by default, which blocks cleartext HTTP connections, but this can be disabled in Info.plist (which should be avoided).

Use HTTPS URLs

  • FIXED: URLSession with HTTPS:

import Foundation

class SecureAuthService {
    // FIXED: Using HTTPS instead of HTTP
    func login(username: String, password: String) {
        let loginURL = "https://api.example.com/login" // HTTPS!
        guard let url = URL(string: loginURL) else { return }

        var request = URLRequest(url: url)
        // ... rest of code
    }

    // FIXED: API token over HTTPS
    func fetchUserData(apiToken: String) {
        let endpoint = "https://api.example.com/user/profile" // HTTPS
        guard let url = URL(string: endpoint) else { return }

        var request = URLRequest(url: url)
        // ... rest of code
    }

    // FIXED: URLSession configuration with TLS requirements
    func configureSecureSession() -> URLSession {
        let config = URLSessionConfiguration.default

        // Enforce TLS 1.2 minimum
        config.tlsMinimumSupportedProtocolVersion = .TLSv12

        return URLSession(configuration: config)
    }
}
  • FIXED: Alamofire with HTTPS:

import Alamofire

class SecureAlamofireService {
    // FIXED: HTTPS endpoint
    func authenticateUser(email: String, password: String) {
        let loginEndpoint = "https://api.example.com/auth/login" // HTTPS
        // ... rest of code
    }

    // FIXED: Certificate pinning for additional security
    func setupCertificatePinning() -> Session {
        let evaluators = [
            "api.example.com": PinnedCertificatesTrustEvaluator()
        ]

        let serverTrustManager = ServerTrustManager(evaluators: evaluators)

        return Session(serverTrustManager: serverTrustManager)
    }

    // FIXED: Using pinned session
    func secureRequest(apiKey: String, session: Session) {
        let endpoint = "https://api.example.com/data"
        // ... rest of code
    }
}
  • FIXED: Vapor with HTTPS:

import Vapor

class SecureVaporClient {
    // FIXED: HTTPS URL
    func sendWebhook(req: Request, authToken: String) async throws {
        let webhookURL = "https://partner-api.com/webhook" // HTTPS
        // ... rest of code
    }

    // FIXED: Configure HTTP client with TLS
    func configureSecureClient(app: Application) {
        app.http.client.configuration.tlsConfiguration = .makeClientConfiguration()
    }
}
  • FIXED: Network Framework with TLS:

import Network

class SecureNetworkService {
    // FIXED: NWConnection with TLS
    func sendDataSecurely(password: String) {
        let endpoint = NWEndpoint.hostPort(host: "api.example.com", port: 443)

        // Use TLS parameters
        let tlsOptions = NWProtocolTLS.Options()
        let parameters = NWParameters(tls: tlsOptions)

        let connection = NWConnection(to: endpoint, using: parameters)
        // ... Rest of code the same
    }

    // FIXED: Custom TLS configuration
    func configureCustomTLS() -> NWParameters {
        let tlsOptions = NWProtocolTLS.Options()

        // Configure TLS settings via sec_protocol_options
        sec_protocol_options_set_min_tls_protocol_version(
            tlsOptions.securityProtocolOptions,
            .TLSv12 // Minimum TLS 1.2
        )

        return NWParameters(tls: tlsOptions)
    }
}

Encrypt Sensitive Data Before Transmission

If you must use HTTP or have additional security requirements, encrypt sensitive data before transmission:

import CryptoKit

class EncryptedTransmission {
    // Encrypt sensitive data before HTTP transmission
    func sendEncryptedPassword(password: String, encryptionKey: SymmetricKey) {
        // Encrypt password
        let passwordData = password.data(using: .utf8)!
        let sealedBox = try! AES.GCM.seal(passwordData, using: encryptionKey)

        let encryptedData = sealedBox.combined!

        // Can now transmit encrypted data (though HTTPS is still recommended)
        let loginURL = "https://api.example.com/login"
        guard let url = URL(string: loginURL) else { return }
        // ... rest of code
    }
}

App Transport Security (ATS)

iOS and macOS enforce App Transport Security by default, which requires HTTPS. Never disable ATS unless absolutely necessary (and only for specific domains).

Key Prevention Strategies

  1. Always use HTTPS - Replace all http:// URLs with https://

  2. Enforce TLS 1.2 or newer - Configure URLSession and network libraries to reject weak TLS versions.

  3. Certificate pinning - Implement certificate or public key pinning for critical connections.

  4. Respect ATS - Keep App Transport Security enabled, avoid NSAllowsArbitraryLoads

  5. Encrypt sensitive fields - Use CryptoKit to encrypt sensitive data before transmission (defense in depth)

  6. Audit Info.plist - Review and minimize ATS exceptions to the HTTPS enforcing policy.

  7. Use secure default configurations - Prefer framework defaults that enforce HTTPS

Configuration

This detector can be configured to:

  • Customize which data categories are considered sensitive (via sensitiveKinds).

  • Define encryption function names that neutralize the issue.

Example configuration in swift.sensitive_information_cleartext_transmission.yml:

properties:
  sensitiveKinds:
    - access_control
    - crypto
    - financial
    - health
    - location
    - personal_identifiable_information

  encryptionFunctions:
    - encrypt
    - seal
    - encryptData

References