Missing Source Correlation of Multiple Independent Data

Description

Missing Source Correlation of Multiple Independent Data occurs when a product relies on a single source of data, preventing the ability to detect if an adversary has compromised that data source. While implicit signing can ensure data wasn't tampered during transit, it cannot guarantee the source itself wasn't compromised. The mitigation involves querying multiple independent sources and comparing responses. When responses diverge, the system should flag potentially compromised sources. If insufficient responses exist to establish a majority or plurality, all sources warrant reporting as potentially compromised. The number of independent sources should scale with the impact severity of integrity failures.

Risk

Single-source reliance has severe security implications. Single point-of-failure attacks enabled. Person-in-the-Middle attacks can subvert validation. Arbitrary replies injected without detection. Data integrity cannot be verified. Compromised sources go undetected. No resilience against oracle manipulation. Critical decisions based on potentially forged data. High impact for security-critical applications like certificate validation.

Solution

Implement Practical Byzantine fault tolerance methods to request data from multiple sources, verify consistency, and report potentially compromised sources. Avoid single-source reliance. Establish reporting mechanisms for compromised sources. Ensure information sources are genuinely independent (not sharing infrastructure). Integrate minority-response reporting into incident response procedures. Scale the number of sources with the criticality of the data.

Common Consequences

ImpactDetails
ConfidentialityScope: Confidentiality

Attacker may read application data by impersonating trusted sources.
IntegrityScope: Integrity

Attacker can modify application data through compromised source.
Access ControlScope: Access Control

Privilege escalation or identity assumption via forged responses.

Example Code

Vulnerable Code

# Vulnerable: Single-source reliance for critical data

import requests
from cryptography import x509
from datetime import datetime

class VulnerableCertificateValidator:
    def __init__(self):
        # VULNERABLE: Single OCSP responder
        self.ocsp_endpoint = "https://ocsp.example.com"

    def check_certificate_revocation(self, cert):
        """Check if certificate is revoked - VULNERABLE: Single source."""
        # VULNERABLE: Only one source queried
        response = requests.post(
            self.ocsp_endpoint,
            data=self._build_ocsp_request(cert),
            headers={'Content-Type': 'application/ocsp-request'}
        )

        # VULNERABLE: Trust response without verification
        if response.status_code == 200:
            return self._parse_ocsp_response(response.content)

        # VULNERABLE: Fail open on error
        return True  # Assume not revoked if check fails

        # Attack scenario:
        # 1. Attacker compromises or impersonates OCSP responder
        # 2. Attacker returns "not revoked" for revoked certificates
        # 3. Application trusts compromised certificates
        # 4. No way to detect the attack with single source

class VulnerableDNSResolver:
    def __init__(self):
        # VULNERABLE: Single DNS server
        self.dns_server = "8.8.8.8"

    def resolve(self, domain):
        """Resolve domain - VULNERABLE: Single source."""
        import dns.resolver

        # VULNERABLE: Only querying one server
        resolver = dns.resolver.Resolver()
        resolver.nameservers = [self.dns_server]

        try:
            answer = resolver.resolve(domain, 'A')
            return [str(rdata) for rdata in answer]
        except Exception:
            return []

        # Attack: DNS poisoning on single server affects all lookups

class VulnerableTimeSync:
    def __init__(self):
        # VULNERABLE: Single NTP server
        self.ntp_server = "time.example.com"

    def get_current_time(self):
        """Get current time - VULNERABLE: Single source."""
        import ntplib

        client = ntplib.NTPClient()

        # VULNERABLE: Trust single time source
        response = client.request(self.ntp_server)
        return datetime.fromtimestamp(response.tx_time)

        # Attack: Compromised NTP server can manipulate time
        # This affects certificate validation, log timestamps, etc.
// Vulnerable: Single-source blockchain oracle

class VulnerableOracle {
    constructor() {
        // VULNERABLE: Single price oracle
        this.oracleEndpoint = "https://oracle.example.com/price";
    }

    async getAssetPrice(asset) {
        // VULNERABLE: Single source for critical financial data
        const response = await fetch(`${this.oracleEndpoint}/${asset}`);
        const data = await response.json();

        // VULNERABLE: Trust without verification
        return data.price;

        // Attack: Oracle manipulation can cause:
        // - Incorrect liquidations in DeFi
        // - Price manipulation for arbitrage
        // - Theft of funds based on false prices
    }

    async executeTrade(asset, threshold) {
        const price = await this.getAssetPrice(asset);

        // VULNERABLE: Decision based on single source
        if (price < threshold) {
            // Execute trade based on potentially manipulated price
            return this.buy(asset, price);
        }
    }
}

// Vulnerable: Single API key validation endpoint
class VulnerableAuthValidator {
    constructor() {
        this.authEndpoint = "https://auth.example.com/validate";
    }

    async validateToken(token) {
        // VULNERABLE: Single validation source
        const response = await fetch(this.authEndpoint, {
            method: 'POST',
            body: JSON.stringify({ token }),
            headers: { 'Content-Type': 'application/json' }
        });

        // VULNERABLE: Trust response completely
        return response.ok;

        // Attack: Compromised auth server allows invalid tokens
    }
}

Fixed Code

# Fixed: Multi-source verification with Byzantine fault tolerance

import requests
import hashlib
from typing import List, Dict, Any, Optional, Tuple
from collections import Counter
from dataclasses import dataclass
from datetime import datetime
import asyncio
import aiohttp

@dataclass
class SourceResponse:
    source: str
    data: Any
    timestamp: datetime
    signature: Optional[bytes] = None
    error: Optional[str] = None

class SecureCertificateValidator:
    def __init__(self):
        # FIXED: Multiple independent OCSP responders
        self.ocsp_endpoints = [
            "https://ocsp1.example.com",
            "https://ocsp2.example.com",
            "https://ocsp3.example.com",
            "https://ocsp4.different-provider.com",  # Different provider!
            "https://ocsp5.another-provider.com"     # Another provider!
        ]
        # FIXED: Minimum required consensus
        self.min_consensus = 3
        self.max_divergence = 1  # Max disagreeing sources before alert

    async def check_certificate_revocation(self, cert) -> Tuple[bool, List[str]]:
        """Check certificate revocation with multi-source verification."""
        # FIXED: Query all sources in parallel
        responses = await self._query_all_sources(cert)

        # FIXED: Analyze responses for consensus
        valid_responses = [r for r in responses if r.error is None]

        if len(valid_responses) < self.min_consensus:
            # FIXED: Not enough responses - fail safe
            await self._alert_insufficient_sources(responses)
            return False, ["Insufficient source responses"]

        # FIXED: Check for consensus
        revocation_status = Counter(r.data['revoked'] for r in valid_responses)

        # Get majority opinion
        majority_status, majority_count = revocation_status.most_common(1)[0]
        minority_count = len(valid_responses) - majority_count

        compromised_sources = []

        # FIXED: Detect potentially compromised sources
        if minority_count > 0:
            # Some sources disagree
            minority_status = not majority_status
            compromised_sources = [
                r.source for r in valid_responses
                if r.data['revoked'] == minority_status
            ]

            if minority_count > self.max_divergence:
                # FIXED: Too much divergence - report all as suspicious
                await self._alert_divergence(responses, majority_count, minority_count)

        return majority_status, compromised_sources

    async def _query_all_sources(self, cert) -> List[SourceResponse]:
        """Query all OCSP sources in parallel."""
        async with aiohttp.ClientSession() as session:
            tasks = [
                self._query_source(session, endpoint, cert)
                for endpoint in self.ocsp_endpoints
            ]
            return await asyncio.gather(*tasks)

    async def _query_source(self, session, endpoint, cert) -> SourceResponse:
        """Query a single OCSP source."""
        try:
            async with session.post(
                endpoint,
                data=self._build_ocsp_request(cert),
                timeout=aiohttp.ClientTimeout(total=5)
            ) as response:
                if response.status == 200:
                    content = await response.read()
                    return SourceResponse(
                        source=endpoint,
                        data=self._parse_ocsp_response(content),
                        timestamp=datetime.utcnow()
                    )
                else:
                    return SourceResponse(
                        source=endpoint,
                        data=None,
                        timestamp=datetime.utcnow(),
                        error=f"HTTP {response.status}"
                    )
        except Exception as e:
            return SourceResponse(
                source=endpoint,
                data=None,
                timestamp=datetime.utcnow(),
                error=str(e)
            )

    async def _alert_divergence(self, responses, majority, minority):
        """Alert security team about source divergence."""
        alert_data = {
            "type": "OCSP_SOURCE_DIVERGENCE",
            "majority_count": majority,
            "minority_count": minority,
            "responses": [
                {"source": r.source, "data": r.data, "error": r.error}
                for r in responses
            ]
        }
        await send_security_alert(alert_data)

class SecureDNSResolver:
    def __init__(self):
        # FIXED: Multiple independent DNS resolvers
        self.dns_servers = [
            "8.8.8.8",        # Google
            "1.1.1.1",        # Cloudflare
            "9.9.9.9",        # Quad9
            "208.67.222.222", # OpenDNS
        ]
        self.consensus_threshold = 3

    def resolve(self, domain: str) -> Tuple[List[str], bool]:
        """Resolve domain with multi-source verification."""
        import dns.resolver

        all_results = {}

        # FIXED: Query all DNS servers
        for server in self.dns_servers:
            resolver = dns.resolver.Resolver()
            resolver.nameservers = [server]

            try:
                answer = resolver.resolve(domain, 'A')
                ips = frozenset(str(rdata) for rdata in answer)
                all_results[server] = ips
            except Exception as e:
                all_results[server] = None

        # FIXED: Find consensus
        valid_results = {k: v for k, v in all_results.items() if v is not None}

        if len(valid_results) < self.consensus_threshold:
            # Not enough responses
            return [], False

        # FIXED: Check if results match
        ip_sets = list(valid_results.values())
        consensus_ips = ip_sets[0]

        all_agree = all(ips == consensus_ips for ips in ip_sets)

        if not all_agree:
            # FIXED: Divergence detected - report and find intersection
            self._alert_dns_divergence(domain, valid_results)
            # Return only IPs that all sources agree on
            consensus_ips = set.intersection(*[set(ips) for ips in ip_sets])

        return list(consensus_ips), all_agree
// Fixed: Multi-source oracle with Byzantine fault tolerance

class SecureOracle {
    constructor() {
        // FIXED: Multiple independent oracles
        this.oracles = [
            { url: "https://oracle1.chainlink.com/price", weight: 1 },
            { url: "https://oracle2.band-protocol.com/price", weight: 1 },
            { url: "https://oracle3.api3.org/price", weight: 1 },
            { url: "https://oracle4.tellor.io/price", weight: 1 },
            { url: "https://oracle5.uma.xyz/price", weight: 1 }
        ];

        // FIXED: Byzantine fault tolerance parameters
        this.minResponses = 3;
        this.maxDeviation = 0.02; // 2% max price deviation
    }

    async getAssetPrice(asset) {
        // FIXED: Query all oracles in parallel
        const responses = await Promise.allSettled(
            this.oracles.map(oracle => this.queryOracle(oracle, asset))
        );

        // FIXED: Filter successful responses
        const validPrices = responses
            .filter(r => r.status === 'fulfilled' && r.value !== null)
            .map(r => r.value);

        if (validPrices.length < this.minResponses) {
            throw new Error(`Insufficient oracle responses: ${validPrices.length}`);
        }

        // FIXED: Check for consensus using median
        const sortedPrices = validPrices.map(p => p.price).sort((a, b) => a - b);
        const medianPrice = sortedPrices[Math.floor(sortedPrices.length / 2)];

        // FIXED: Detect outliers
        const outliers = validPrices.filter(p => {
            const deviation = Math.abs(p.price - medianPrice) / medianPrice;
            return deviation > this.maxDeviation;
        });

        if (outliers.length > 0) {
            // FIXED: Alert on potential manipulation
            await this.alertOutliers(asset, outliers, medianPrice);
        }

        // FIXED: Use median price (resistant to outliers)
        return {
            price: medianPrice,
            sources: validPrices.length,
            outliers: outliers.length,
            confidence: this.calculateConfidence(validPrices, medianPrice)
        };
    }

    async queryOracle(oracle, asset) {
        try {
            const response = await fetch(`${oracle.url}/${asset}`, {
                timeout: 5000
            });

            if (!response.ok) return null;

            const data = await response.json();
            return {
                source: oracle.url,
                price: data.price,
                timestamp: data.timestamp,
                weight: oracle.weight
            };
        } catch (e) {
            return null;
        }
    }

    calculateConfidence(prices, median) {
        // FIXED: Calculate confidence based on price agreement
        const deviations = prices.map(p =>
            Math.abs(p.price - median) / median
        );
        const avgDeviation = deviations.reduce((a, b) => a + b) / deviations.length;

        // Higher agreement = higher confidence
        return Math.max(0, 1 - (avgDeviation * 10));
    }

    async executeTrade(asset, threshold) {
        const priceData = await this.getAssetPrice(asset);

        // FIXED: Check confidence before executing
        if (priceData.confidence < 0.8) {
            throw new Error(`Insufficient price confidence: ${priceData.confidence}`);
        }

        // FIXED: Check outlier count
        if (priceData.outliers > 1) {
            throw new Error(`Too many price outliers: ${priceData.outliers}`);
        }

        if (priceData.price < threshold) {
            return this.buy(asset, priceData.price);
        }
    }
}

CVE Examples

  • CVE-2020-8286: cURL's OCSP stapling feature relied on single source without multi-perspective validation.
  • CVE-2018-16875: DNS resolver trusted single upstream server, enabling cache poisoning.

  • CWE-345: Insufficient Verification of Data Authenticity (parent)
  • CWE-654: Reliance on a Single Factor in a Security Decision (peer)
  • CWE-300: Channel Accessible by Non-Endpoint (related)
  • CWE-295: Improper Certificate Validation (related)

References

  1. MITRE Corporation. "CWE-1293: Missing Source Correlation of Multiple Independent Data." https://cwe.mitre.org/data/definitions/1293.html
  2. Let's Encrypt. "Multi-Perspective Validation"
  3. Castro, M., & Liskov, B. "Practical Byzantine Fault Tolerance" (2002)