Improper Verification of Source of a Communication Channel

Description

Improper Verification of Source of a Communication Channel occurs when a product accepts incoming communications or requests without verifying that they originate from an expected or authorized source. Many security controls rely on trusting the declared source of a message, but without proper verification, attackers can spoof the source to gain unauthorized access, trigger unintended actions, or poison caches and data stores. This vulnerability is particularly prevalent in network protocols, inter-process communication, and mobile application handlers.

Risk

Failure to verify communication sources enables various attack vectors. DNS cache poisoning allows attackers to redirect traffic to malicious servers. Mobile applications may execute privileged operations triggered by malicious apps or websites. Web applications may accept data from untrusted origins. Internal services may be accessed by external attackers spoofing internal addresses. Authentication can be bypassed when source verification replaces proper authentication. The impact ranges from data manipulation to complete system compromise depending on what functionality is exposed.

Solution

Implement cryptographic authentication for communication sources where possible. Use signed tokens, TLS client certificates, or HMAC-based verification. For mobile apps, validate intent sources and use signature-level permissions. Never trust source identifiers that can be easily spoofed (IP addresses, referrer headers). For DNS, implement DNSSEC and validate responses cryptographically. Use explicit trust relationships rather than implicit source-based trust. Validate the complete chain of trust for forwarded requests. Implement rate limiting and anomaly detection for requests from unverified sources.

Common Consequences

ImpactDetails
Access ControlScope: Access Control

Gain Privileges or Assume Identity - Attackers can impersonate trusted sources to gain unauthorized access to functionality.
IntegrityScope: Integrity

Modify Application Data - Cache poisoning or data injection from spoofed sources can corrupt application state.
Access ControlScope: Access Control

Bypass Protection Mechanism - Source-based access controls are bypassed when sources can be spoofed.

Example Code

Vulnerable Code

// Vulnerable: Android BroadcastReceiver without source verification
public class VulnerableAccountReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        // Vulnerable: No verification of intent source
        if ("com.app.DELETE_ACCOUNT".equals(action)) {
            String accountId = intent.getStringExtra("account_id");
            // Any app can delete accounts!
            deleteAccount(context, accountId);
        }

        if ("com.app.SET_ADMIN".equals(action)) {
            String userId = intent.getStringExtra("user_id");
            // Any app can grant admin privileges!
            setUserAsAdmin(context, userId);
        }
    }
}
// Vulnerable: DNS cache without source validation
typedef struct {
    char domain[256];
    char ip_address[16];
    time_t expiry;
} DNSCacheEntry;

DNSCacheEntry dns_cache[1000];

void vulnerable_process_dns_response(int socket, struct sockaddr_in *from) {
    char buffer[512];
    recv(socket, buffer, sizeof(buffer), 0);

    DNSResponse *response = parse_dns_response(buffer);

    // Vulnerable: No verification that response is from legitimate DNS server
    // Attacker can send spoofed responses
    for (int i = 0; i < response->answer_count; i++) {
        // Cache poisoned with attacker's IP
        add_to_cache(response->answers[i].domain,
                     response->answers[i].ip);
    }
}
# Vulnerable: Web service trusting X-Forwarded-For
from flask import Flask, request

app = Flask(__name__)

ALLOWED_IPS = {'10.0.0.1', '10.0.0.2', '10.0.0.3'}

@app.route('/admin/action')
def vulnerable_admin_action():
    # Vulnerable: X-Forwarded-For can be easily spoofed
    client_ip = request.headers.get('X-Forwarded-For', request.remote_addr)

    # Attacker adds X-Forwarded-For: 10.0.0.1
    if client_ip in ALLOWED_IPS:
        perform_admin_action()
        return "Action completed"

    return "Access denied", 403
// Vulnerable: PostMessage without origin verification
window.addEventListener('message', function(event) {
    // Vulnerable: No verification of message origin
    var data = event.data;

    if (data.action === 'updateCredentials') {
        // Any website can send this message!
        updateStoredCredentials(data.username, data.password);
    }

    if (data.action === 'processPayment') {
        // Any website can trigger payment!
        processPayment(data.amount, data.recipient);
    }
});
// Vulnerable: WebView URL scheme without source check
class VulnerableWebViewController: UIViewController {

    func webView(_ webView: WKWebView,
                 decidePolicyFor navigationAction: WKNavigationAction,
                 decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {

        if let url = navigationAction.request.url,
           url.scheme == "myapp" {

            // Vulnerable: Any website can invoke native APIs
            // No verification of page origin
            let action = url.host ?? ""

            if action == "getLocation" {
                // Malicious site can track user
                sendLocationToWebView(webView)
            }

            if action == "accessPhotos" {
                // Malicious site can steal photos
                sendPhotosToWebView(webView)
            }

            decisionHandler(.cancel)
            return
        }

        decisionHandler(.allow)
    }
}

Fixed Code

// Fixed: Android BroadcastReceiver with source verification
public class FixedAccountReceiver extends BroadcastReceiver {

    private static final String EXPECTED_PACKAGE = "com.mycompany.trustedapp";

    @Override
    public void onReceive(Context context, Intent intent) {
        // Fixed: Verify intent source
        if (!isValidSource(context, intent)) {
            Log.w(TAG, "Rejecting intent from unauthorized source");
            return;
        }

        String action = intent.getAction();

        if ("com.app.DELETE_ACCOUNT".equals(action)) {
            String accountId = intent.getStringExtra("account_id");
            deleteAccount(context, accountId);
        }
    }

    private boolean isValidSource(Context context, Intent intent) {
        // Fixed: Check if this is an implicit broadcast (from system)
        if (intent.getComponent() == null) {
            return true;  // System broadcast
        }

        // For explicit intents, verify sender has permission
        // Using signature-level permission defined in manifest
        String permission = "com.app.permission.ACCOUNT_MANAGEMENT";
        if (context.checkCallingPermission(permission)
                == PackageManager.PERMISSION_GRANTED) {
            return true;
        }

        return false;
    }
}

// AndroidManifest.xml:
// <permission
//     android:name="com.app.permission.ACCOUNT_MANAGEMENT"
//     android:protectionLevel="signature"/>
//
// <receiver android:name=".FixedAccountReceiver"
//     android:exported="true"
//     android:permission="com.app.permission.ACCOUNT_MANAGEMENT"/>
// Fixed: DNS cache with response validation
#include <openssl/evp.h>

typedef struct {
    struct sockaddr_in expected_server;
    uint16_t expected_transaction_id;
    char queried_domain[256];
} PendingQuery;

PendingQuery pending_queries[1000];

void fixed_process_dns_response(int socket, struct sockaddr_in *from) {
    char buffer[512];
    recv(socket, buffer, sizeof(buffer), 0);

    DNSResponse *response = parse_dns_response(buffer);

    // Fixed: Verify response source matches our query
    PendingQuery *query = find_pending_query(response->transaction_id);
    if (!query) {
        fprintf(stderr, "Unexpected DNS response\n");
        return;
    }

    // Fixed: Verify response is from expected server
    if (from->sin_addr.s_addr != query->expected_server.sin_addr.s_addr ||
        from->sin_port != query->expected_server.sin_port) {
        fprintf(stderr, "DNS response from unexpected source\n");
        return;
    }

    // Fixed: Verify domain matches our query
    if (strcmp(response->question.domain, query->queried_domain) != 0) {
        fprintf(stderr, "DNS response domain mismatch\n");
        return;
    }

    // Fixed: Implement DNSSEC validation
    if (!validate_dnssec_signatures(response)) {
        fprintf(stderr, "DNSSEC validation failed\n");
        return;
    }

    // Now safe to cache
    for (int i = 0; i < response->answer_count; i++) {
        add_to_cache(response->answers[i].domain,
                     response->answers[i].ip);
    }

    remove_pending_query(response->transaction_id);
}
# Fixed: Web service with proper source verification
from flask import Flask, request
import hmac
import time

app = Flask(__name__)

ALLOWED_IPS = {'10.0.0.1', '10.0.0.2', '10.0.0.3'}
API_SECRET = b'shared_secret_key'

@app.route('/admin/action')
def fixed_admin_action():
    # Fixed: Don't rely solely on IP
    # Require authentication token

    auth_token = request.headers.get('Authorization')
    if not auth_token:
        return "Missing authentication", 401

    # Verify HMAC-based token
    if not verify_auth_token(auth_token):
        return "Invalid authentication", 401

    # Fixed: If behind known proxy, validate IP as defense in depth
    if is_behind_trusted_proxy():
        client_ip = request.headers.get('X-Forwarded-For', '').split(',')[0].strip()
    else:
        client_ip = request.remote_addr

    if client_ip not in ALLOWED_IPS:
        # Log but don't necessarily block if auth is valid
        app.logger.warning(f"Request from unexpected IP: {client_ip}")

    perform_admin_action()
    return "Action completed"

def verify_auth_token(token):
    try:
        # Token format: timestamp:signature
        parts = token.split(':')
        if len(parts) != 2:
            return False

        timestamp, signature = parts

        # Check timestamp freshness (prevent replay)
        if abs(time.time() - float(timestamp)) > 300:
            return False

        # Verify signature
        expected = hmac.new(API_SECRET, timestamp.encode(), 'sha256').hexdigest()
        return hmac.compare_digest(signature, expected)

    except Exception:
        return False
// Fixed: PostMessage with origin verification
const TRUSTED_ORIGINS = [
    'https://trusted-partner.com',
    'https://app.mycompany.com'
];

window.addEventListener('message', function(event) {
    // Fixed: Verify message origin
    if (!TRUSTED_ORIGINS.includes(event.origin)) {
        console.warn('Rejected message from untrusted origin:', event.origin);
        return;
    }

    var data = event.data;

    // Fixed: Validate message structure
    if (!data || typeof data.action !== 'string') {
        return;
    }

    // Fixed: Define allowed actions per origin
    const allowedActions = {
        'https://trusted-partner.com': ['updatePreferences', 'shareContent'],
        'https://app.mycompany.com': ['updateCredentials', 'processPayment']
    };

    const originActions = allowedActions[event.origin] || [];

    if (!originActions.includes(data.action)) {
        console.warn('Action not allowed from this origin:', data.action);
        return;
    }

    switch (data.action) {
        case 'updatePreferences':
            updatePreferences(data.preferences);
            break;
        case 'processPayment':
            // Additional confirmation for sensitive actions
            confirmAndProcessPayment(data.amount, data.recipient);
            break;
    }
});
// Fixed: WebView URL scheme with origin verification
class FixedWebViewController: UIViewController {

    private let trustedOrigins = Set([
        "https://www.trusted-site.com",
        "https://app.trusted-site.com"
    ])

    func webView(_ webView: WKWebView,
                 decidePolicyFor navigationAction: WKNavigationAction,
                 decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {

        if let url = navigationAction.request.url,
           url.scheme == "myapp" {

            // Fixed: Verify the origin of the request
            guard let currentURL = webView.url,
                  let origin = currentURL.absoluteString.components(separatedBy: "/").prefix(3).joined(separator: "/"),
                  trustedOrigins.contains(origin) else {

                print("Rejecting custom URL from untrusted origin")
                decisionHandler(.cancel)
                return
            }

            let action = url.host ?? ""

            // Fixed: Define allowed actions per origin
            let allowedActions = getActionsForOrigin(origin)

            if !allowedActions.contains(action) {
                print("Action \(action) not allowed from \(origin)")
                decisionHandler(.cancel)
                return
            }

            handleVerifiedAction(action, url: url, webView: webView)
            decisionHandler(.cancel)
            return
        }

        decisionHandler(.allow)
    }

    private func getActionsForOrigin(_ origin: String) -> Set<String> {
        switch origin {
        case "https://www.trusted-site.com":
            return ["shareContent", "updateTheme"]
        case "https://app.trusted-site.com":
            return ["getLocation", "accessPhotos", "shareContent"]
        default:
            return []
        }
    }
}

CVE Examples

  • CVE-2000-1218: DNS cache poisoning via unverified server updates.
  • CVE-2005-0877: DNS resolver accepted responses from unauthorized sources.
  • CVE-2001-1452: Caching of unauthorized glue records from non-delegated name servers.

  • CWE-346: Origin Validation Error (parent)
  • CWE-923: Improper Restriction of Communication Channel to Intended Endpoints (parent)
  • CWE-925: Improper Verification of Intent by Broadcast Receiver (child)
  • CWE-939: Improper Authorization in Handler for Custom URL Scheme (child)

References

  1. MITRE Corporation. "CWE-940: Improper Verification of Source of a Communication Channel." https://cwe.mitre.org/data/definitions/940.html
  2. OWASP. "Input Validation Cheat Sheet."
  3. RFC 5452. "Measures for Making DNS More Resilient against Forged Answers."