Improper Restriction of Communication Channel to Intended Endpoints

Description

Improper Restriction of Communication Channel to Intended Endpoints occurs when a product creates a communication channel for privileged or protected operations but fails to properly ensure that it is communicating with the correct endpoint. When software establishes connections to servers, peers, or other endpoints, it must verify the identity of the remote party before exchanging sensitive data or performing privileged operations. Failure to properly validate endpoints allows attackers to impersonate legitimate servers or clients, intercepting communications, stealing credentials, or injecting malicious data.

Risk

This vulnerability enables man-in-the-middle attacks where attackers intercept and potentially modify communications between legitimate parties. Attackers can impersonate trusted servers to steal credentials or serve malicious content. In cross-domain scenarios, malicious websites can make unauthorized requests to sensitive services. On mobile platforms, applications may accept intents or messages from untrusted sources. Certificate validation failures allow attackers with network access to present fraudulent certificates. The risk is severe because users and systems believe they are communicating with legitimate endpoints while actually interacting with attackers.

Solution

Implement robust endpoint verification for all communications. Verify TLS certificates including hostname validation—never disable certificate checks even temporarily. Use certificate pinning for high-security applications. Validate the source of messages, intents, or requests before processing. Implement strict cross-domain policies (CORS, Content-Security-Policy). For IP-based restrictions, combine with authentication rather than relying on IP alone. Use mutual TLS where both parties authenticate. Validate redirect destinations before following. Implement proper DNS security measures. For mobile apps, validate intent origins and use explicit intents where possible.

Common Consequences

ImpactDetails
Integrity, ConfidentialityScope: Integrity, Confidentiality

Gain Privileges or Assume Identity - Attackers can impersonate endpoints to obtain privileges and access intended for legitimate parties.
ConfidentialityScope: Confidentiality

Read Application Data - Man-in-the-middle attacks allow attackers to intercept and read sensitive communications.
IntegrityScope: Integrity

Modify Application Data - Attackers can inject or modify data in transit when endpoint verification is missing.

Example Code

Vulnerable Code

<!-- Vulnerable: Flash/Silverlight permissive cross-domain policy -->
<!-- crossdomain.xml -->
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM
  "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
    <!-- Vulnerable: Allows requests from ANY domain -->
    <allow-access-from domain="*" />
</cross-domain-policy>

<!-- Any malicious Flash app can make authenticated requests -->
// Vulnerable: Android BroadcastReceiver accepting any intent
public class VulnerableReceiver extends BroadcastReceiver {

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

        if ("com.app.DELETE_USER".equals(action)) {
            // Vulnerable: No verification of intent sender
            String userId = intent.getStringExtra("user_id");
            deleteUserAccount(userId);  // Any app can trigger this!
        }
    }
}

// AndroidManifest.xml
// <receiver android:name=".VulnerableReceiver" android:exported="true">
//     <intent-filter>
//         <action android:name="com.app.DELETE_USER"/>
//     </intent-filter>
// </receiver>
// Vulnerable: Disabling SSL certificate validation
public class VulnerableHttpClient {

    public void makeRequest(String url) throws Exception {
        // Vulnerable: Trust all certificates
        TrustManager[] trustAllCerts = new TrustManager[] {
            new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() { return null; }
                public void checkClientTrusted(X509Certificate[] certs, String authType) {}
                public void checkServerTrusted(X509Certificate[] certs, String authType) {}
            }
        };

        SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(null, trustAllCerts, new SecureRandom());

        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

        // Vulnerable: Disable hostname verification
        HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);

        // Now connecting to any server, even attackers with invalid certs
        URL requestUrl = new URL(url);
        HttpsURLConnection conn = (HttpsURLConnection) requestUrl.openConnection();
    }
}
# Vulnerable: Disabling SSL verification in Python
import requests

def vulnerable_api_call(url, data):
    # Vulnerable: SSL verification disabled
    response = requests.post(url, json=data, verify=False)
    return response.json()

# Vulnerable: Not checking redirect destination
def vulnerable_follow_redirect(initial_url):
    response = requests.get(initial_url, allow_redirects=True)
    # May be redirected to malicious site
    return response.text
// Vulnerable: CORS allowing all origins
const express = require('express');
const app = express();

// Vulnerable: Accepts requests from any origin
app.use((req, res, next) => {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Credentials', 'true');
    next();
});

// Sensitive API endpoint now accessible from malicious sites
app.get('/api/user/data', (req, res) => {
    res.json(getUserData(req.session.userId));
});
// Vulnerable: IP-based authentication only
func vulnerableHandler(w http.ResponseWriter, r *http.Request) {
    // Vulnerable: IP addresses can be spoofed
    clientIP := r.RemoteAddr

    if isAllowedIP(clientIP) {
        // Perform privileged operation
        performAdminAction()
        w.Write([]byte("Action completed"))
    } else {
        http.Error(w, "Forbidden", 403)
    }
}

func isAllowedIP(ip string) bool {
    // Attacker can forge X-Forwarded-For or spoof IP
    allowedIPs := []string{"10.0.0.1", "10.0.0.2"}
    for _, allowed := range allowedIPs {
        if strings.Contains(ip, allowed) {
            return true
        }
    }
    return false
}

Fixed Code

<!-- Fixed: Restrictive cross-domain policy -->
<!-- crossdomain.xml -->
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM
  "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
    <!-- Fixed: Only allow specific trusted domains -->
    <allow-access-from domain="www.trusted-partner.com" secure="true"/>
    <allow-access-from domain="api.trusted-partner.com" secure="true"/>
    <!-- Never use domain="*" -->
</cross-domain-policy>
// Fixed: Android BroadcastReceiver with sender verification
public class FixedReceiver extends BroadcastReceiver {

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

    @Override
    public void onReceive(Context context, Intent intent) {
        // Fixed: Verify the sender
        String callingPackage = null;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            // Android 11+
            callingPackage = intent.getPackage();
        } else {
            // For older versions, use permissions
        }

        // Fixed: Validate sender
        if (!EXPECTED_PACKAGE.equals(callingPackage)) {
            Log.w(TAG, "Rejecting intent from unknown package: " + callingPackage);
            return;
        }

        String action = intent.getAction();
        if ("com.app.DELETE_USER".equals(action)) {
            String userId = intent.getStringExtra("user_id");
            deleteUserAccount(userId);
        }
    }
}

// AndroidManifest.xml - use signature-level permission
// <permission
//     android:name="com.app.permission.DELETE_USER"
//     android:protectionLevel="signature"/>
//
// <receiver android:name=".FixedReceiver"
//     android:exported="true"
//     android:permission="com.app.permission.DELETE_USER">
// Fixed: Proper SSL certificate validation
public class FixedHttpClient {

    public void makeRequest(String url) throws Exception {
        // Fixed: Use default trust manager that validates certificates
        URL requestUrl = new URL(url);
        HttpsURLConnection conn = (HttpsURLConnection) requestUrl.openConnection();

        // Default SSLSocketFactory validates certificates properly

        // For extra security, implement certificate pinning
        conn.setSSLSocketFactory(getPinnedSSLSocketFactory());

        // Connection proceeds only if certificate is valid
    }

    private SSLSocketFactory getPinnedSSLSocketFactory() throws Exception {
        // Load pinned certificate
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        InputStream caInput = getClass().getResourceAsStream("/trusted_cert.pem");
        Certificate ca = cf.generateCertificate(caInput);

        // Create KeyStore containing trusted cert
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(null, null);
        keyStore.setCertificateEntry("ca", ca);

        // Create TrustManager that trusts our cert
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(
            TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(keyStore);

        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, tmf.getTrustManagers(), null);

        return context.getSocketFactory();
    }
}
# Fixed: Proper SSL verification in Python
import requests
from urllib.parse import urlparse

def fixed_api_call(url, data):
    # Fixed: SSL verification enabled (default)
    response = requests.post(url, json=data, verify=True)
    # Or specify CA bundle: verify='/path/to/ca-bundle.crt'
    return response.json()

# Fixed: Validate redirect destinations
ALLOWED_REDIRECT_DOMAINS = {'trusted.com', 'api.trusted.com'}

def fixed_follow_redirect(initial_url):
    response = requests.get(initial_url, allow_redirects=False)

    while response.is_redirect:
        redirect_url = response.headers.get('Location')

        # Fixed: Validate redirect destination
        parsed = urlparse(redirect_url)
        if parsed.netloc not in ALLOWED_REDIRECT_DOMAINS:
            raise SecurityError(f"Redirect to untrusted domain: {parsed.netloc}")

        response = requests.get(redirect_url, allow_redirects=False)

    return response.text
// Fixed: Restrictive CORS configuration
const express = require('express');
const cors = require('cors');
const app = express();

// Fixed: Whitelist specific origins
const allowedOrigins = [
    'https://www.trusted-site.com',
    'https://app.trusted-site.com'
];

const corsOptions = {
    origin: function (origin, callback) {
        // Allow requests with no origin (mobile apps, curl, etc.)
        // Or implement stricter policy
        if (!origin || allowedOrigins.includes(origin)) {
            callback(null, true);
        } else {
            callback(new Error('Not allowed by CORS'));
        }
    },
    credentials: true,
    methods: ['GET', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization']
};

app.use(cors(corsOptions));

app.get('/api/user/data', (req, res) => {
    // Fixed: Origin is validated
    res.json(getUserData(req.session.userId));
});
// Fixed: Proper authentication beyond IP
func fixedHandler(w http.ResponseWriter, r *http.Request) {
    // Fixed: Require authentication token
    authHeader := r.Header.Get("Authorization")
    if !validateAuthToken(authHeader) {
        http.Error(w, "Unauthorized", 401)
        return
    }

    // Fixed: IP as additional defense layer, not sole auth
    clientIP := getClientIP(r)
    if !isAllowedIP(clientIP) {
        log.Printf("Warning: Request from unexpected IP: %s", clientIP)
        // Log but don't necessarily block if auth is valid
    }

    performAdminAction()
    w.Write([]byte("Action completed"))
}

func getClientIP(r *http.Request) string {
    // Fixed: Don't trust X-Forwarded-For blindly
    // Only trust it if behind known proxy
    if behindTrustedProxy() {
        forwarded := r.Header.Get("X-Forwarded-For")
        if forwarded != "" {
            // Take first IP (client)
            parts := strings.Split(forwarded, ",")
            return strings.TrimSpace(parts[0])
        }
    }

    // Use direct connection IP
    ip, _, _ := net.SplitHostPort(r.RemoteAddr)
    return ip
}

func validateAuthToken(token string) bool {
    // Implement proper JWT or API key validation
    return verifyJWT(token)
}

CVE Examples

  • CVE-2022-30319: IP allowlist bypassed using forged source addresses.
  • CVE-2012-5810: Mobile banking application missing hostname verification in SSL.
  • CVE-2014-1266: Apple's "goto fail" bug caused certificate validation to always succeed.

  • CWE-284: Improper Access Control (parent)
  • CWE-291: Reliance on IP Address for Authentication (child)
  • CWE-297: Improper Validation of Certificate with Host Mismatch (child)
  • CWE-300: Channel Accessible by Non-Endpoint (child)
  • CWE-940: Improper Verification of Source of a Communication Channel (child)

References

  1. MITRE Corporation. "CWE-923: Improper Restriction of Communication Channel to Intended Endpoints." https://cwe.mitre.org/data/definitions/923.html
  2. OWASP. "Transport Layer Protection Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html
  3. OWASP. "Cross-Origin Resource Sharing (CORS)." https://owasp.org/www-community/attacks/CORS_OriginHeaderScrutiny