Connection String Parameter Pollution

ID

swift.connection_string_parameter_pollution

Severity

critical

Resource

Resource Management

Language

Swift

Tags

CWE:15, NIST.SP.800-53, OWASP:2021:A5, PCI-DSS:6.5.10

Description

External control of connection string.

In iOS, macOS, and server-side Swift applications, connection string pollution commonly occurs when:

  • SQLite database file paths come from user input (FMDB, GRDB, SQLite.swift)

  • MongoDB connection strings include user-controlled parameters (MongoSwift, MongoKitten)

  • Redis server addresses or credentials are derived from external sources (SwiftRedis)

  • PostgreSQL/MySQL connection URLs are constructed from query parameters or environment variables

Rationale

Database connectivity typically involves constructing a connection string – a snippet of text encoding the details of the connection to the database, such as hostname, database name, and credentials.

When user input is improperly included in a connection string, it can lead to parameter pollution. This means that the attacker can inject additional parameters or override existing ones, potentially accessing databases they shouldn’t or altering their privileges.

The following example shows this vulnerability with MongoDB connection string:

import MongoSwift

class MongoService {
    // VULNERABLE: User-controlled MongoDB connection string
    func connectToMongoDB(connectionString: String) async throws {
        // connectionString from environment variable, config file, or query parameter
        // Attacker might provide:
        // "mongodb://attacker.com/malicious?authSource=admin&ssl=false"
        // "mongodb://localhost/mydb?connectTimeoutMS=1&socketTimeoutMS=1" (DoS)

        // ISSUE: User controls entire connection string
        let client = try MongoClient(connectionString)

        let db = client.db("myapp")
        // Attacker may have connected to their own server or disabled security
    }
}

Remediation

Mitigating connection string parameter pollution involves several key practices:

  1. Use Parameter Objects: Avoid concatenating user input into connection strings. Instead, use APIs or configurations that separate parameters from the connection logic.

  2. Validate and Sanitize User Input: If user input must be incorporated into the connection process, ensure it is strictly validated and sanitized according to expected patterns.

  3. Environment Configuration: Use environment variables or configuration files to manage sensitive credential information away from user modification capabilities.

To fix the previous vulnerability, carefully validate each part of the connection string:

import MongoSwift

class SecureMongoService {
    // FIXED: Use structured configuration, not raw strings
    func connectToMongoDB(host: String, port: Int, database: String, username: String, password: String) async throws {
        // Validate inputs
        guard isValidHostname(host),
              isValidPort(port),
              isValidIdentifier(database),
              isValidIdentifier(username) else {
            throw ValidationError.invalidInput
        }

        // Build connection string with validated parameters
        let connectionString = "mongodb://\(username):\(password)@\(host):\(port)/\(database)?ssl=true&authSource=admin"

        let client = try MongoClient(connectionString)
    }

    // FIXED: Use connection options object
    func connectWithOptions() async throws {
        var options = MongoClientOptions()
        options.credential = MongoCredential(username: "user", password: "pass", source: "admin")
        options.tls = TLSOptions(enabled: true)

        let client = try MongoClient("mongodb://localhost:27017", options: options)
    }

    private func isValidHostname(_ host: String) -> Bool {
        // Whitelist allowed hosts
        let allowedHosts = ["localhost", "db.example.com", "mongo-cluster.example.com"]
        return allowedHosts.contains(host)
    }

    private func isValidPort(_ port: Int) -> Bool {
        return port > 0 && port <= 65535 && port == 27017 // Restrict to standard MongoDB port
    }

    private func isValidIdentifier(_ identifier: String) -> Bool {
        // Only allow alphanumeric and underscore
        let pattern = "^[a-zA-Z0-9_]+$"
        return identifier.range(of: pattern, options: .regularExpression) != nil
    }
}

enum ValidationError: Error {
    case invalidInput
}

Key Prevention Strategies:

  1. Never concatenate user input into connection strings.

  2. Use whitelisting for database names, hostnames, and other parameters.

  3. Validate all inputs with strict patterns (alphanumeric only, no special characters).

  4. Use configuration objects instead of raw connection strings when available

  5. Strip path traversal sequences (e.g., use lastPathComponent for file paths)

  6. Store connection details in secure configuration files, not user-accessible sources

  7. Use environment variables with validation for sensitive credentials

  8. Enforce SSL/TLS in connection configuration (don’t allow user to disable)

  9. Log connection attempts to detect unauthorized database access

  10. Follow the Principle of Least Privilege: Use database accounts with minimal required permissions

Configuration

The detector has the following configurable parameters:

  • sources, that indicates the source kinds to check.

  • neutralizations, that indicates the neutralization kinds to check.

Unless you need to change the default behavior, you typically do not need to configure this detector.

References