Understanding Caller ID Spoofing
Caller ID spoofing is the practice of causing the telephone network to display a phone number different from the actual originating number. While spoofing has legitimate uses (businesses displaying a main callback number, doctors calling from personal phones), it's heavily abused by scammers, robocallers, and fraudsters.
How Spoofing Works
Traditional telephone networks were built on trust - the calling party provides their caller ID, and the network transmits it without verification. This design flaw enables spoofing through several methods:
- VoIP manipulation - SIP headers can specify any desired caller ID
- PRI/T1 configuration - Business phone systems can set outbound caller ID
- Spoofing services - Commercial services provide spoofing as a feature
- SS7 exploitation - Direct manipulation of signaling protocols
Spoofing Attack Types
| Attack Type | Description | Goal |
|---|---|---|
| Neighbor Spoofing | Display a local number matching victim's area code | Increase answer rate |
| Business Impersonation | Spoof a bank, IRS, or company number | Build false trust |
| Victim Impersonation | Spoof the victim's own number | Bypass call screening |
| Random Spoofing | Use any number to mask identity | Avoid traceback |
| Sequential Spoofing | Rotate through number ranges | Evade blocklists |
STIR/SHAKEN: Cryptographic Caller ID Verification
STIR/SHAKEN (Secure Telephone Identity Revisited / Signature-based Handling of Asserted information using toKENs) is an industry framework that uses digital certificates to cryptographically sign and verify caller ID.
How STIR/SHAKEN Works
- Signing (STIR) - The originating carrier creates a digital signature attesting to the caller ID
- Transport (SHAKEN) - The signature is transmitted with the call through the network
- Verification - The terminating carrier validates the signature using the originating carrier's certificate
- Attestation Display - Results are passed to the end device/application
Attestation Levels Explained
| Level | Name | Verification | Spoofing Risk |
|---|---|---|---|
| A | Full Attestation | Carrier authenticated caller AND verified right to use number | Low |
| B | Partial Attestation | Carrier authenticated caller but cannot verify number rights | Medium |
| C | Gateway Attestation | Carrier is entry point; cannot verify originator | Higher |
| None | No Attestation | No STIR/SHAKEN information provided | Highest |
def verify_stir_shaken(call_data):
"""Verify STIR/SHAKEN attestation and return spoofing risk."""
stir_shaken = call_data.get('stir_shaken', {})
attestation = stir_shaken.get('attestation')
verified = stir_shaken.get('verified', False)
certificate_valid = stir_shaken.get('certificate_valid', False)
# Build verification result
result = {
'attestation_level': attestation,
'signature_verified': verified,
'certificate_valid': certificate_valid,
'spoofing_indicators': []
}
# Evaluate spoofing risk
if attestation is None:
result['spoofing_risk'] = 'high'
result['spoofing_indicators'].append('No STIR/SHAKEN attestation')
elif attestation == 'C':
result['spoofing_risk'] = 'elevated'
result['spoofing_indicators'].append('Gateway attestation only')
elif attestation == 'B':
if not verified:
result['spoofing_risk'] = 'medium'
result['spoofing_indicators'].append('Partial attestation, verification failed')
else:
result['spoofing_risk'] = 'low'
elif attestation == 'A':
if verified and certificate_valid:
result['spoofing_risk'] = 'very_low'
else:
result['spoofing_risk'] = 'medium'
result['spoofing_indicators'].append('Full attestation but verification issue')
return result
STIR/SHAKEN Limitations
While powerful, STIR/SHAKEN has limitations to understand:
- Legitimate spoofers can get "A" attestation - A bad actor with a legitimate VoIP account can still get full attestation for numbers they control
- Coverage gaps - Not all carriers fully implement STIR/SHAKEN; international calls often lack attestation
- Gateway issues - Calls transiting through gateways lose attestation fidelity
- Does not verify intent - A call can be fully attested and still be spam/fraud
Detect spoofed caller IDs in real-time. VeriRoute Intel provides spam scores, carrier data, and reputation signals.
Get Free API KeyPhone Intelligence for Spoofing Detection
Beyond STIR/SHAKEN, phone number intelligence provides additional spoofing detection signals by revealing mismatches between the displayed number and actual call characteristics.
Line Type Verification
If the displayed caller ID is a landline but the call characteristics indicate VoIP origin, spoofing is likely:
def detect_line_type_mismatch(caller_number, call_metadata):
"""Detect spoofing through line type mismatch."""
# Look up the displayed caller ID
phone_data = lookup_phone_intelligence(caller_number)
displayed_line_type = phone_data.get('line_type')
# Analyze call metadata for actual origin
actual_indicators = analyze_call_origin(call_metadata)
mismatches = []
# Check for VoIP characteristics on non-VoIP number
if displayed_line_type == 'landline':
if actual_indicators.get('codec') in ['G.729', 'OPUS']:
mismatches.append('VoIP codec detected for landline number')
if actual_indicators.get('rtt_jitter') > 50:
mismatches.append('Network jitter indicates VoIP origin')
# Check for mobile characteristics
if displayed_line_type == 'mobile':
if not actual_indicators.get('handoff_detected'):
mismatches.append('No cellular handoff for mobile number')
return {
'line_type_match': len(mismatches) == 0,
'displayed_type': displayed_line_type,
'mismatches': mismatches
}
Carrier Consistency Check
Verify that the originating carrier matches the expected carrier for the displayed number:
- LRN lookup - Identify which carrier currently owns the displayed number
- Compare to actual origin - Check if call originated from that carrier's network
- Flag mismatches - Carrier mismatch is a strong spoofing indicator
Geographic Consistency
Analyze whether the caller's location matches the number's geography:
- NPA-NXX analysis - Number's assigned geography
- IP geolocation - For VoIP calls, originating IP location
- Network routing - Where did the call enter the network?
def check_geographic_consistency(caller_number, call_origin):
"""Detect geographic inconsistencies suggesting spoofing."""
# Parse the displayed number's geography
npa = caller_number[1:4] # Area code
number_location = lookup_npa_geography(npa)
# Get call origin information
origin_location = call_origin.get('geo')
origin_ip = call_origin.get('source_ip')
inconsistencies = []
# Check IP geolocation vs number geography
if origin_ip:
ip_location = geolocate_ip(origin_ip)
distance = calculate_distance(number_location, ip_location)
if distance > 1000: # More than 1000 miles apart
inconsistencies.append({
'type': 'ip_geography_mismatch',
'number_location': number_location,
'ip_location': ip_location,
'distance_miles': distance
})
# Check for international origin with domestic number
if call_origin.get('international_entry'):
inconsistencies.append({
'type': 'international_origin',
'entry_country': call_origin.get('entry_country')
})
return {
'geographically_consistent': len(inconsistencies) == 0,
'inconsistencies': inconsistencies
}
Behavioral Spoofing Detection
Analyzing patterns across multiple calls reveals spoofing campaigns that might not be detectable from a single call.
Call Pattern Analysis
| Pattern | Legitimate Use | Spoofing Indicator |
|---|---|---|
| Same number, many locations | Mobile user traveling | Simultaneous calls from different locations |
| Sequential number usage | Rare | Calls from +1555000001, 002, 003... |
| High call velocity | Call center operations | 100+ calls/hour from single number |
| Number reuse | Consistent business number | Dormant number suddenly high activity |
Impossible Travel Detection
Track the apparent location of calls from the same number:
def detect_impossible_travel(caller_number, call_history):
"""Detect physically impossible movement patterns."""
MAX_TRAVEL_SPEED_MPH = 600 # Faster than commercial flight
# Sort calls by timestamp
sorted_calls = sorted(call_history, key=lambda x: x['timestamp'])
violations = []
for i in range(1, len(sorted_calls)):
prev_call = sorted_calls[i-1]
curr_call = sorted_calls[i]
# Calculate time difference in hours
time_diff_hours = (curr_call['timestamp'] - prev_call['timestamp']).total_seconds() / 3600
if time_diff_hours > 0:
# Calculate distance between call origins
distance_miles = calculate_distance(
prev_call['origin_location'],
curr_call['origin_location']
)
required_speed = distance_miles / time_diff_hours
if required_speed > MAX_TRAVEL_SPEED_MPH:
violations.append({
'from_location': prev_call['origin_location'],
'to_location': curr_call['origin_location'],
'time_hours': time_diff_hours,
'distance_miles': distance_miles,
'required_speed_mph': required_speed
})
return {
'impossible_travel_detected': len(violations) > 0,
'violations': violations
}
Spoofing Campaign Detection
Identify coordinated spoofing campaigns targeting your organization:
- Number rotation patterns - Sequential or patterned number usage
- Common origin clusters - Multiple spoofed numbers from same infrastructure
- Timing correlations - Calls that follow predictable schedules
- Target patterns - Systematic targeting of your number ranges
CNAM Verification
CNAM (Caller Name) data provides another verification layer for spoofing detection.
CNAM Anomalies
- Missing CNAM - Legitimate established numbers usually have CNAM; absence is suspicious
- Generic CNAM - Values like "WIRELESS CALLER" or "UNKNOWN" on calls claiming to be from specific organizations
- CNAM mismatch - Caller claims to be "ABC Bank" but CNAM shows different entity
- Recently changed CNAM - CNAM changed right before a suspicious call campaign
def verify_cnam_consistency(caller_number, claimed_identity):
"""Check if CNAM supports the caller's claimed identity."""
phone_data = lookup_phone_intelligence(caller_number)
cnam = phone_data.get('cnam', {})
result = {
'cnam_name': cnam.get('name'),
'cnam_type': cnam.get('type'),
'claimed_identity': claimed_identity,
'verification_signals': []
}
# Check if CNAM exists
if not cnam.get('name'):
result['verification_signals'].append({
'signal': 'no_cnam',
'severity': 'medium',
'detail': 'No CNAM record found'
})
# Check CNAM type vs claimed type
if claimed_identity.get('type') == 'business':
if cnam.get('type') == 'person':
result['verification_signals'].append({
'signal': 'type_mismatch',
'severity': 'high',
'detail': 'Claims business but CNAM shows person'
})
# Check name similarity
if cnam.get('name') and claimed_identity.get('name'):
similarity = calculate_name_similarity(
cnam.get('name'),
claimed_identity.get('name')
)
if similarity < 0.3:
result['verification_signals'].append({
'signal': 'name_mismatch',
'severity': 'high',
'detail': f'CNAM name does not match claimed identity (similarity: {similarity:.2f})'
})
result['likely_spoofed'] = len(result['verification_signals']) >= 2
return result
Implementing a Spoofing Detection System
A comprehensive spoofing detection system combines all available signals:
class SpoofingDetector:
"""Multi-layer caller ID spoofing detection."""
def __init__(self, phone_api_key):
self.phone_api = PhoneIntelligenceAPI(phone_api_key)
self.history_store = CallHistoryStore()
def analyze_call(self, call_data):
"""Complete spoofing analysis for an incoming call."""
caller_number = call_data['caller_number']
# Gather all detection signals
signals = {
'stir_shaken': self._check_stir_shaken(call_data),
'phone_intelligence': self._check_phone_intelligence(caller_number),
'geographic': self._check_geographic_consistency(caller_number, call_data),
'behavioral': self._check_behavioral_patterns(caller_number),
'cnam': self._check_cnam(caller_number, call_data.get('claimed_identity'))
}
# Calculate composite spoofing score
spoofing_score = self._calculate_spoofing_score(signals)
# Determine action
if spoofing_score >= 80:
action = 'block'
confidence = 'high'
elif spoofing_score >= 60:
action = 'challenge'
confidence = 'medium'
elif spoofing_score >= 40:
action = 'flag'
confidence = 'low'
else:
action = 'allow'
confidence = 'none'
# Store for behavioral analysis
self.history_store.record_call(caller_number, call_data, signals)
return {
'caller_number': caller_number,
'spoofing_score': spoofing_score,
'likely_spoofed': spoofing_score >= 60,
'confidence': confidence,
'action': action,
'signals': signals
}
def _calculate_spoofing_score(self, signals):
"""Weighted scoring of spoofing signals."""
weights = {
'stir_shaken': 0.30,
'phone_intelligence': 0.25,
'geographic': 0.20,
'behavioral': 0.15,
'cnam': 0.10
}
score = 0
for signal_type, weight in weights.items():
signal_data = signals.get(signal_type, {})
signal_score = signal_data.get('spoofing_score', 0)
score += signal_score * weight
return min(100, score)
Response Strategies for Spoofed Calls
Graduated Response Framework
| Spoofing Score | Response | User Experience |
|---|---|---|
| 0-39 | Allow with monitoring | Normal call experience |
| 40-59 | Flag and warn | Display warning to recipient |
| 60-79 | Challenge caller | Require DTMF or voice verification |
| 80-100 | Block silently | Caller hears ringing, call never connects |
Challenge Methods
- DTMF challenge - "Press 1 to complete your call"
- Voice challenge - "Please say your name"
- Callback verification - "We'll call you back at this number"
- Out-of-band verification - "Enter the code we just texted"
Best Practices
- Never rely on caller ID alone - Treat it as one signal among many
- Implement STIR/SHAKEN verification - Use attestation as a primary signal
- Layer detection methods - Combine technical and behavioral analysis
- Track patterns over time - Single-call analysis misses campaign patterns
- Update thresholds regularly - Spoofing techniques evolve
- Balance security and UX - Aggressive blocking frustrates legitimate callers
- Report spoofing - Contribute to industry databases and notify carriers
Frequently Asked Questions
Is caller ID spoofing illegal?
In the US, caller ID spoofing is illegal when done "with the intent to defraud, cause harm, or wrongfully obtain anything of value" under the Truth in Caller ID Act. However, spoofing for legitimate purposes (like a doctor calling from a personal phone but displaying the office number) is legal. The challenge is that enforcement is difficult, especially for international spoofing operations.
Can STIR/SHAKEN prevent all spoofing?
No, STIR/SHAKEN has limitations. A caller with legitimate access to a phone number can still get "A" attestation and make spam/scam calls. International calls often lack attestation entirely. Additionally, not all carriers have fully implemented STIR/SHAKEN. It's a powerful tool but should be combined with phone intelligence, reputation data, and behavioral analysis.
Why would someone spoof my own number to call me?
This technique, sometimes called "mirror spoofing," is used because people are more likely to answer calls from their own number out of curiosity. Some systems that filter spam calls may also whitelist the user's own number. It's a social engineering tactic to bypass normal call screening behaviors.
How do I protect my business number from being spoofed by others?
You cannot directly prevent others from spoofing your number, but you can mitigate damage: (1) Register your numbers in STIR/SHAKEN databases so carriers can validate legitimate calls from you, (2) Monitor for reports of your number being used in spam/scam calls, (3) Work with your carrier on proactive blocking, (4) Consider number reputation services that alert you to misuse.