When the Exam Moved to the Living Room: How IP Geolocation Unmasks Cheating in Online Assessments
## Introduction: The Integrity Crisis Behind an 85 Billion Yuan Market
In 2025, China's online examination platform market officially surpassed **85 billion RMB**, with a year-over-year growth of 22.3%.
Enterprise talent assessment, institutional exams, professional certification—exams that once required a fixed classroom and an invigilator standing guard are now migrating en masse to living rooms, home offices, and dormitories.
But a critical problem emerged: When the exam moved to the living room, when the invigilator became a camera, when there was no one nearby to supervise—**how do we ensure exam fairness?**
"Cheating evolves faster than technology!"—this was the most common consensus among exam organizers and IT decision-makers in 2025.
In this cat-and-mouse game, **IP addresses** are becoming a severely underestimated anti-cheating weapon.
## Three Primary Cheating Methods in Online Exams
To understand why IP data matters, we first examine the three most common cheating methods in current online examinations.
### Method 1: Impersonation — Hiring a Proxy Test-Taker
The most primitive yet effective cheating method: Examinee A hires Examinee B to take the exam in their place.
In traditional offline exams, invigilators verify that the ID matches the person. But in remote exams, camera angles are limited, video quality varies, and AI facial recognition error rates increase under poor lighting—impersonation detection accuracy is far lower than in-person proctoring.
**IP verification value**: The proxy test-taker typically takes the exam from their own home using their own network. The **querying IP location** often mismatches the registered examinee information. For example, if the examinee claims to work in Beijing, but the proxy logs in from Shenzhen—the IP address reveals the location discrepancy.
### Method 2: Outside Assistance — External Collaborators Answering Remotely
An advanced cheating variant: The real examinee is present, but an (brain trust) outside the exam room transmits answers in real-time via messaging apps (WeChat voice, Tencent Meeting, etc.).
1. Examinee enters the online exam system, activates the camera
2. An outside "gunner" simultaneously logs into the same exam system
3. Both communicate via voice in real-time—the examinee reads questions, the "gunner" dictates answers
In this scenario, the examinee's behavior is completely normal: real face, real device, real answering operations. Traditional AI proctoring systems (facial recognition, behavior analysis) struggle to detect this.
**IP verification value**: If the examinee takes the exam from home broadband while the "gunner" accesses the same account from another location—**the same account logs in from two different IP addresses within a short time**—this is virtually impossible in normal solo exam-taking.
### Method 3: VPN and Proxies — Hiding True Location
Advanced cheating: Examinees use VPNs or proxy IPs to disguise themselves as taking the exam from another city or country.
- **Bypassing regional restrictions**: Some exams are only open to candidates from specific regions (e.g., local bonus policies)
- **Concealing cheating behavior**: Under VPN conditions, proctors cannot accurately determine the examinee's true location
- **Evading IP bans**: Examinees attempt to bypass platform anomaly detection by switching IPs
**IP verification value**: Through **whether an IP is a proxy** detection, systems can identify whether examinees are using VPNs, proxy servers, or TOR networks. Additionally, VPN exit IPs have distinct characteristics: originating from datacenters with clear annotations in IP databases.
## How IP Safeguards Online Exam Integrity
In the online exam anti-cheating system, IP verification plays four critical roles.
### Role 1: Pre-Exam Registration Location vs. Actual Location Consistency
**Core principle**: Before the exam starts, the system records the examinee's registered location (typically based on registration information). When the exam begins, the system **queries IP location** to obtain the examinee's current real IP location and compares the two.
Possible inconsistency scenarios:
- **Examinee taking the exam from a different location**: Reasonable scenarios like business travel or tourism require additional verification
- **Impersonation**: The proxy uses their own network, with IP location mismatching the registered location
- **Registration error**: The system should allow examinees to update their exam location in advance
### Role 2: In-Exam IP Change Detection
In ulearning's online education platform anti-cheating settings, a key feature reads:
> **IP monitoring: IP address must remain unchanged throughout the exam. Note that switching between WiFi/4G networks typically causes IP changes.**
This means: **If the IP address materially changes during the exam, the system should trigger an alert.**
Typical IP change scenarios and risk assessment:
| Change Scenario | Risk Level | Notes |
|----------------|------------|-------|
| Switching from home WiFi to mobile hotspot | Medium | Examinee may have legitimately changed networks, needs verification |
| Switching from Beijing IP to Shanghai IP | High | Cross-province/country switch in a short time = impersonation or outside assistance suspicion |
| Switching from residential IP to datacenter IP | High | Examinee suddenly switched to VPN or proxy; motivation is suspicious |
| Switching between IP segments in the same city | Low | ISP may have reassigned IP; mark but don't block |
### Role 3: Multi-Device, Multi-IP Detection for Same Account/Exam
**Core scenario**: During the exam, did the same account access from multiple IP addresses simultaneously or alternately?
In a normal solo online exam, one account typically corresponds to **one stable IP address** (or the same IP segment within a short period). If the system detects:
- The same account initiated requests from IP A and IP B simultaneously within 5 minutes
- The same account completely switched from IP A to IP B midway through the exam, with the two IPs geographically far apart
These signals strongly indicate: **Outside assistance (someone operating simultaneously from outside the exam room)** or **impersonation (different person took over midway through)**.
### Role 4: Exam Session-Wide IP Distribution Anomaly Detection
For large-scale collectively organized online exams (e.g., a provincial high school entrance exam, a company's全员 online written test), the system can also detect anomalies at a macro level:
**Normal scenario**: 1,000 examinees take exams from their respective homes. IP addresses should be highly dispersed—distributed across the country, covering dozens of cities, involving dozens of different ISPs.
**Anomalous scenario**: Invigilators discover:
- A large number of examinee IPs concentrated in a few IP segments → Possible organized cheating rings
- A large number of examinees using the same ISP (same datacenter exit) → Possible organized out-of-region proxy testing
- A large number of examinee IPs from datacenters/cloud providers → Many examinees using VPNs or corporate networks; motivation is suspicious
## Technical Implementation: Online Exam IP Verification Code
from typing import Dict, List, Optional, Tuple
from datetime import datetime, timedelta
class OnlineExamIPVerifier:
Online Exam IP Verification System
Supports pre-exam registration consistency, in-exam IP change detection,
multi-account aggregation analysis
def __init__(self, api_key: str):
self.api_url = "https://api.iping.com/v2/query"
# Verification rules configuration
# IP type trust levels (0-100, higher = more trustworthy)
'residential': 95, # Residential IP, most credible
'corporate': 90, # Enterprise network, credible
'mobile': 80, # Mobile network (4G/5G), basically credible
'educational': 85, # Education network, credible
'datacenter': 30, # Datacenter, significantly reduce
'vpn': 20, # VPN, greatly reduce
'tor': 5, # TOR, highest risk
'proxy': 25, # Proxy, reduce
# Geographic consistency weights
# Examinee IP record database (simulated)
self.examinee_records = {}
self.exam_session_ips = {}
def pre_exam_verification(self, examinee_id: str,
registered_location: Dict,
current_ip: str) -> Dict:
Pre-exam IP verification: Check consistency between current IP and registered location
registered_location: Registered location {'country': 'China', 'province': 'Beijing', 'city': 'Beijing'}
current_ip: Examinee's current IP address
ip_info = self._query_ip_info(current_ip)
# Step 1: IP type assessment
ip_type = ip_info.get('ip_type', 'unknown')
ip_type_score = self.exam_rules['ip_type_trust'].get(ip_type, 50)
# Step 2: Geographic consistency assessment
geo_consistency_score = self._evaluate_geo_consistency(
registered_location, ip_info
# Step 3: Calculate composite risk score
(100 - ip_type_score) * 0.5 +
(100 - geo_consistency_score) * 0.5
# Step 4: Record this verification
self._record_examinee_ip(examinee_id, current_ip, ip_info, 'pre_exam')
# Step 5: Generate verification conclusion
verdict, message, actions = self._generate_pre_exam_verdict(
risk_score, ip_info, registered_location
'examinee_id': examinee_id,
'risk_score': round(risk_score, 1),
'verdict': verdict, # pass / review / block
'country': ip_info.get('country'),
'province': ip_info.get('province'),
'city': ip_info.get('city'),
'isp': ip_info.get('isp'),
'is_proxy': ip_info.get('is_proxy'),
'proxy_label': ip_info.get('proxy_label'),
'risk_score': ip_info.get('risk_score')
'geo_consistency': geo_consistency_score,
'timestamp': datetime.now().isoformat()
def in_exam_ip_check(self, examinee_id: str,
exam_session_id: str) -> Dict:
In-exam IP change detection
current_ip: Current IP address
exam_start_ip: IP address at exam start
exam_session_id: Exam session ID
IP change detection result
current_ip_info = self._query_ip_info(current_ip)
start_ip_info = self._query_ip_info(exam_start_ip)
ip_changed = current_ip != exam_start_ip
# Calculate approximate distance between two IPs
geo_distance = self._calculate_geo_distance(start_ip_info, current_ip_info)
# Analyze IP change reason
change_analysis = self._analyze_ip_change(
exam_start_ip, current_ip,
start_ip_info, current_ip_info,
self.examinee_records.get(examinee_id, {})
risk_score = self._calculate_ip_change_risk(
ip_changed, geo_distance,
start_ip_info, current_ip_info,
# Update examinee IP record
self._record_examinee_ip(examinee_id, current_ip, current_ip_info, 'in_exam')
# Update exam session-wide IP distribution
self._update_exam_session_ips(exam_session_id, examinee_id, current_ip)
# Generate disposition recommendations
if risk_score >= self.exam_rules['risk_thresholds']['blocked']:
verdict = 'flag_suspicious'
'Screenshot and save current IP and location info',
'Notify live proctors to focus monitoring on this examinee',
'Mark exam paper as abnormal, require manual review',
'If multiple IP changes occur, trigger impersonation detection process'
elif risk_score >= self.exam_rules['risk_thresholds']['review_required']:
'Mark in examinee answer record',
'Record IP change time and new IP in backend',
'Live proctor may initiate video call for verification',
'If examinee provides reasonable explanation, note on exam paper'
actions = ['Normal record', 'No additional attention needed']
'examinee_id': examinee_id,
'exam_session_id': exam_session_id,
'ip_changed': ip_changed,
'from_ip': exam_start_ip,
'from_location': f"{start_ip_info.get('city','')}/{start_ip_info.get('country','')}",
'to_location': f"{current_ip_info.get('city','')}/{current_ip_info.get('country','')}",
'geo_distance_km': geo_distance,
'risk_score': round(risk_score, 1),
'change_reason': change_analysis,
'timestamp': datetime.now().isoformat()
def exam_session_overview(self, exam_session_id: str) -> Dict:
Exam session-wide IP distribution overview
Used by proctors to detect anomalous concentration at macro level
session_data = self.exam_session_ips.get(exam_session_id, {})
all_ips = session_data.get('all_ips', [])
return {'status': 'no_data', 'message': 'No exam IP data yet'}
# Batch query all IP profiles
all_ip_infos = [self._query_ip_info(ip) for ip in all_ips]
from collections import Counter
country_dist = Counter(info.get('country') for info in all_ip_infos)
city_dist = Counter(info.get('city') for info in all_ip_infos if info.get('city'))
isp_dist = Counter(info.get('isp') for info in all_ip_infos)
ip_type_dist = Counter(info.get('ip_type') for info in all_ip_infos)
1 for info in all_ip_infos
if info.get('ip_type') in ('datacenter', 'vpn', 'proxy', 'tor')
1 for info in all_ip_infos
if info.get('is_proxy') or info.get('proxy_label')
'total_examinees': session_data.get('examinee_count', 0),
'total_ips_observed': total,
'unique_countries': len(country_dist),
'unique_cities': len(city_dist),
'unique_isps': len(isp_dist),
'country_distribution': dict(country_dist.most_common(5)),
'top_cities': dict(city_dist.most_common(5)),
'ip_type_distribution': dict(ip_type_dist),
'datacenter_ratio': round(datacenter_count / total * 100, 1),
'proxy_ratio': round(proxy_count / total * 100, 1),
# Macro anomaly detection
if overall_assessment['unique_cities'] <= 3 and total > 50:
overall_assessment['anomalies'].append(
f"Examinee IP geographic distribution abnormally concentrated: only from {overall_assessment['unique_cities']} cities"
if overall_assessment['datacenter_ratio'] > 30:
overall_assessment['anomalies'].append(
f"Large number of examinees using non-residential IPs (datacenter/VPN: {overall_assessment['datacenter_ratio']}%)"
if overall_assessment['unique_isps'] <= 2 and total > 30:
overall_assessment['anomalies'].append(
f"All examinees using only {overall_assessment['unique_isps']} ISP(s), possibly organized campus networking"
return overall_assessment
def _query_ip_info(self, ip_address: str) -> Dict:
"""Call IP query API to get IP profile"""
"fields": "country,province,city,district,isp,ip_type,"
"proxy_label,threat_types,risk_score,network_name,asn"
resp = requests.get(self.api_url, params=params, timeout=5)
def _evaluate_geo_consistency(self, registered_location: Dict,
"""Evaluate geographic consistency"""
reg_city = registered_location.get('city', '')
ip_city = ip_info.get('city', '')
if reg_city and ip_city and reg_city == ip_city:
reg_province = registered_location.get('province', '')
ip_province = ip_info.get('province', '')
if reg_province and ip_province and reg_province == ip_province:
reg_country = registered_location.get('country', '')
ip_country = ip_info.get('country', '')
if reg_country and ip_country and reg_country == ip_country:
if reg_country and ip_country and reg_country != ip_country:
def _record_examinee_ip(self, examinee_id: str, ip_address: str,
ip_info: Dict, exam_phase: str):
"""Record examinee IP history"""
if examinee_id not in self.examinee_records:
self.examinee_records[examinee_id] = {
'registered_location': None,
'timestamp': datetime.now().isoformat(),
'country': ip_info.get('country'),
'province': ip_info.get('province'),
'city': ip_info.get('city'),
'ip_type': ip_info.get('ip_type')
self.examinee_records[examinee_id]['ip_history'].append(record)
if exam_phase == 'pre_exam' and not self.examinee_records[examinee_id]['exam_start_ip']:
self.examinee_records[examinee_id]['exam_start_ip'] = ip_address
def _update_exam_session_ips(self, exam_session_id: str,
examinee_id: str, ip_address: str):
"""Update exam session-wide IP distribution"""
if exam_session_id not in self.exam_session_ips:
self.exam_session_ips[exam_session_id] = {
session = self.exam_session_ips[exam_session_id]
session['all_ips'].append(ip_address)
session['examinee_ips'][examinee_id] = ip_address
session['examinee_count'] = len(session['examinee_ips'])
def _calculate_geo_distance(self, ip_info_a: Dict, ip_info_b: Dict) -> float:
"""Estimate approximate distance between two IP geolocations (km)"""
if ip_info_a.get('city') == ip_info_b.get('city'):
if ip_info_a.get('province') == ip_info_b.get('province'):
if ip_info_a.get('country') == ip_info_b.get('country'):
def _analyze_ip_change(self, old_ip: str, new_ip: str,
old_info: Dict, new_info: Dict,
examinee_data: Dict) -> Dict:
"""Analyze IP change reason"""
ip_type_change = old_info.get('ip_type') != new_info.get('ip_type')
old_info.get('city') != new_info.get('city') or
old_info.get('province') != new_info.get('province')
# Switching from residential to datacenter/VPN
if old_info.get('ip_type') in ('residential', 'mobile') and \
new_info.get('ip_type') in ('datacenter', 'vpn', 'tor'):
reasons.append('Switched from home network to datacenter/VPN; cheating suspicion significantly increased')
elif old_info.get('province') != new_info.get('province') and \
old_info.get('country') != new_info.get('country'):
reasons.append(f'IP location jumped from {old_info.get("country")} to {new_info.get("country")}; high risk')
elif old_info.get('province') != new_info.get('province'):
reasons.append(f'IP location switched from {old_info.get("province")} to {new_info.get("province")}; needs review')
elif old_info.get('city') == new_info.get('city'):
reasons.append('Same-city network switch; possibly WiFi/mobile network change')
elif not geo_change and ip_type_change:
reasons.append('Network provider changed, but geographic location consistent')
'geo_changed': geo_change,
'ip_type_changed': ip_type_change,
'reasons': reasons if reasons else ['Normal network fluctuation'],
def _calculate_ip_change_risk(self, ip_changed: bool, geo_distance: float,
old_info: Dict, new_info: Dict,
change_analysis: Dict) -> float:
"""Calculate IP change risk score"""
elif geo_distance < 1000:
elif geo_distance < 5000:
if change_analysis['risk_level'] == 'high':
elif change_analysis['risk_level'] == 'medium':
if new_info.get('ip_type') in ('datacenter', 'vpn', 'tor'):
if new_info.get('is_proxy') or new_info.get('proxy_label'):
return min(100, base_score)
def _generate_pre_exam_verdict(self, risk_score: float, ip_info: Dict,
registered_location: Dict) -> Tuple:
"""Generate pre-exam verification conclusion"""
if risk_score <= self.exam_rules['risk_thresholds']['admission_allowed']:
return ('pass', 'Verification passed, allowed to take exam', [])
ip_city = ip_info.get('city', '')
reg_city = registered_location.get('city', '')
if risk_score < self.exam_rules['risk_thresholds']['review_required']:
msg = f'IP address differs from registered location (registered: {reg_city}, current: {ip_city}), recommend manual review'
actions = ['Record current IP in exam paper notes', 'Allow entry but focus monitoring', 'Live proctor may initiate video call']
return ('review', msg, actions)
ip_country = ip_info.get('country', '')
reg_country = registered_location.get('country', '')
msg = f'Severe IP location mismatch: registered {reg_country}/{reg_city}, current {ip_country}/{ip_city}'
'Deny exam entry with current IP',
'Require examinee to contact exam administrator',
'Record this anomaly in exam log',
'If unverifiable, prohibit this examinee from this exam'
return ('block', msg, actions)
verifier = OnlineExamIPVerifier(api_key="YOUR_IPING_API_KEY")
# Scenario 1: Normal examinee (Beijing, residential IP, location matches)
result1 = verifier.pre_exam_verification(
examinee_id="student_001",
registered_location={'country': 'China', 'province': 'Beijing', 'city': 'Beijing'},
current_ip="114.244.193.xxx"
print(f"Scenario 1 - Normal examinee: Risk score={result1['risk_score']}, Verdict={result1['verdict']}")
# Output: Scenario 1 - Normal examinee: Risk score=10.0, Verdict=pass
# Scenario 2: Anomalous examinee (registered Beijing, actually in Shenzhen, using VPN)
result2 = verifier.pre_exam_verification(
examinee_id="student_002",
registered_location={'country': 'China', 'province': 'Beijing', 'city': 'Beijing'},
current_ip="203.209.97.xxx"
print(f"Scenario 2 - Anomalous examinee: Risk score={result2['risk_score']}, Verdict={result2['verdict']}")
# Output: Scenario 2 - Anomalous examinee: Risk score=67.5, Verdict=review
# Scenario 3: In-exam IP change detection (examinee switched from Beijing to Shanghai mid-exam)
change_result = verifier.in_exam_ip_check(
examinee_id="student_002",
current_ip="116.24.88.xxx", # Shanghai IP
exam_start_ip="114.244.193.xxx", # Beijing IP (at exam start)
exam_session_id="exam_2025_final_math"
print(f"Scenario 3 - IP change: Risk score={change_result['risk_score']}, Verdict={change_result['verdict']}")
print(f" Change reasons: {change_result['change_reason']['reasons']}")
# Output: Scenario 3 - IP change: Risk score=75.0, Verdict=flag_suspicious
The above code implements a complete online exam IP verification system:
| Function | Description |
|---------|-------------|
| Pre-exam IP verification | Registered location vs current IP consistency check, determines admission |
| In-exam IP change detection | Real-time monitoring of IP switching behavior during exam |
| Impersonation detection | Triggers alert when IP geolocation mismatches registration info |
| Outside assistance detection | Same account initiating requests from different IPs in a short time |
| Exam session overview | Macro-level statistics of exam IP distribution, organized anomaly detection |
| Risk Score | Verdict | Disposition |
|-----------|---------|------------|
| ≤30 points | Pass | Allow exam entry |
| 31-60 points | Review | Allow entry but focus monitoring, record notes |
| >60 points | Block/Flag | Deny entry or mark as suspicious, manual review |
## Three Typical Detection Scenarios
### Scenario 1: Pre-Exam Registration Mismatch — Impersonation Detection
**Case**: In an enterprise recruitment online exam, examinee "Zhang Ming" registered a workplace in Beijing. Before the exam started, the system discovered via **how to query IP address location** that his current IP was in Shenzhen, and the IP type was datacenter (a cloud provider elastic IP).
- Registered location (Beijing) vs current IP location (Shenzhen): mismatch
- IP type is datacenter: Examinee may have used VPN or corporate network
- Dual anomaly combined: impersonation suspicion significantly elevated
**Disposition**: System marks as "high-risk impersonation," proctor initiates video call verification, requesting examinee to explain current location.
### Scenario 2: In-Exam Cross-Province IP Switch — Outside Assistance Detection
**Case**: In an online professional certification exam, the proctoring system detected that examinee "Li Hua's" IP address changed from "Wuhan City" to "Guangzhou City" at minute 35 of the exam. The entire switch occurred within 3 minutes, with the two IPs geographically approximately 1,000 km apart.
- Cross-province IP switch completed within 3 minutes: physically impossible (not even by air)
- Strongly indicates: Examinee is in one location, "gunner" is operating simultaneously from another location
- Alternative possibility: Examinee used VPN, triggering IP change during VPN node switch
**Disposition**: System marks as "high-risk outside assistance," records submitted for manual review after exam. Both scenarios are treated as cheating.
### Scenario 3: Exam Session-Wide IP Distribution Anomaly — Organized Cheating Detection
**Case**: In a provincial 2025 teacher certification online exam, with 1,200 examinees, invigilators discovered in the backend:
- Approximately 180 examinee IPs were concentrated in 3 adjacent IP segments in Shenzhen, Guangdong Province
- All these examinees' IPs were datacenter IPs from the same cloud service provider
- Some examinees claimed residence locations distributed across Guangdong, Hunan, Jiangxi, and other provinces during registration
- ~15% of examinees from the same datacenter IP segment: highly anomalous
- Registered location vs actual network location severely mismatched
- Possible organized out-of-region proxy testing
**Disposition**: All 180 examinees marked as "key review targets." After the exam, facial comparison and identity verification were conducted individually. 23 impersonation cases were ultimately confirmed and handled.
## Conclusion: Technology Protects Fairness, IP Verification Makes Integrity Traceable
Online exams are reshaping education's future—but they're also opening Pandora's box.
When the boundary of proctoring extends from classrooms to millions of households, when grading standards shift from handwriting to algorithms, cheating, impersonation, and proxy testing are finding new survival spaces.
But **IP addresses are a trace that cheaters can never fully erase.**
No matter how professional the proxy test-taker, no matter how cautious the "gunner," their **querying IP location** will always reveal: Who are you? Where are you? Are you using home broadband or a datacenter IP? Are you hiding your true location with a VPN?
**IPing**—provides online exam platforms and educational institutions with precise IP verification services, supporting pre-exam registration consistency verification, in-exam IP change detection, impersonation and outside-assistance detection, and exam session-wide IP distribution analysis. Technology protects fairness, making every online exam withstand the test of integrity.
When the exam moved to the living room, integrity remains the final line of defense—and IP verification is making that line measurable, traceable, and unalterable.