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
| Impact | Details |
|---|---|
| Access Control | Scope: Access Control Gain Privileges or Assume Identity - Attackers can impersonate trusted sources to gain unauthorized access to functionality. |
| Integrity | Scope: Integrity Modify Application Data - Cache poisoning or data injection from spoofed sources can corrupt application state. |
| Access Control | Scope: 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.
Related CWEs
- 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
- MITRE Corporation. "CWE-940: Improper Verification of Source of a Communication Channel." https://cwe.mitre.org/data/definitions/940.html
- OWASP. "Input Validation Cheat Sheet."
- RFC 5452. "Measures for Making DNS More Resilient against Forged Answers."