{"title":"LDAP Injection via Unescaped Search Filter Construction","language":"Python","severity":"Critical","cwe":"CWE-90","source_lines":[3],"flow_lines":[3,6,7],"sink_lines":[7],"vulnerable_code":"import ldap\n\ndef retrieve_iot_device_credentials(device_mac, tenant_id):\n    ldap_conn = ldap.initialize('ldap://iot-directory.internal:389')\n    ldap_conn.simple_bind_s('cn=iot-reader,dc=company,dc=com', 'P@ssw0rd')\n    search_filter = f\"(&(objectClass=iotDevice)(macAddress={device_mac})(tenantID={tenant_id}))\"\n    results = ldap_conn.search_s('ou=devices,dc=company,dc=com', ldap.SCOPE_SUBTREE, search_filter, ['deviceKey', 'apiToken'])\n    if results:\n        device_dn, device_attrs = results[0]\n        return {'key': device_attrs['deviceKey'][0].decode(), 'token': device_attrs['apiToken'][0].decode()}\n    return None","explanation":"The function accepts user-controlled parameters (device_mac and tenant_id) and directly interpolates them into an LDAP search filter using f-strings without any sanitization or escaping. An attacker can inject malicious LDAP filter syntax to bypass authentication checks, access unauthorized devices across tenants, or extract sensitive credential data.","remediation":"The fix applies defense-in-depth by first validating that the MAC address and tenant ID conform to expected formats using regex patterns, then escaping any remaining LDAP special characters using ldap.filter.escape_filter_chars() before interpolating them into the search filter. This prevents attackers from injecting malicious LDAP filter syntax such as wildcards or parentheses that could bypass tenant isolation or extract unauthorized credentials.","secure_code":"import ldap\nfrom ldap.filter import escape_filter_chars\nimport re\n\ndef validate_mac_address(mac):\n    \"\"\"Validate MAC address format before using in LDAP filter.\"\"\"\n    mac_pattern = re.compile(r'^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$')\n    if not mac_pattern.match(mac):\n        raise ValueError(f\"Invalid MAC address format: {mac}\")\n    return mac\n\ndef validate_tenant_id(tenant_id):\n    \"\"\"Validate tenant ID format (alphanumeric and hyphens only).\"\"\"\n    tenant_pattern = re.compile(r'^[a-zA-Z0-9\\-]+$')\n    if not tenant_pattern.match(tenant_id):\n        raise ValueError(f\"Invalid tenant ID format: {tenant_id}\")\n    return tenant_id\n\ndef retrieve_iot_device_credentials(device_mac, tenant_id):\n    # Validate input formats\n    validate_mac_address(device_mac)\n    validate_tenant_id(tenant_id)\n    \n    # Escape special LDAP filter characters to prevent injection\n    safe_mac = escape_filter_chars(device_mac)\n    safe_tenant_id = escape_filter_chars(tenant_id)\n    \n    ldap_conn = ldap.initialize('ldap://iot-directory.internal:389')\n    ldap_conn.simple_bind_s('cn=iot-reader,dc=company,dc=com', 'P@ssw0rd')\n    search_filter = '(&(objectClass=iotDevice)(macAddress={})(tenantID={}))'.format(safe_mac, safe_tenant_id)\n    results = ldap_conn.search_s('ou=devices,dc=company,dc=com', ldap.SCOPE_SUBTREE, search_filter, ['deviceKey', 'apiToken'])\n    if results:\n        device_dn, device_attrs = results[0]\n        return {'key': device_attrs['deviceKey'][0].decode(), 'token': device_attrs['apiToken'][0].decode()}\n    return None"}