在程序化广告交易生态中,广告交易平台(ADX)作为连接媒体(Publisher)和广告渠道(Demand Source)的核心枢纽,面临着日益复杂的作弊挑战。根据IAB的最新报告,广告欺诈每年给行业造成超过350亿美元的损失。其中,IP不一致是常见的作弊手段之一——恶意媒体可能通过伪造请求IP或上报IP来获取不正当收益。
本文将深入探讨如何在ADX系统中构建一套完整的IP不一致检测机制,包含技术方案设计、核心代码实现和数据分析方法论。我们以一个典型场景为例:媒体请求ADX获取广告,但后续事件上报时的IP与原始请求IP不一致,如何系统性地检测和分析这类现象?
并非所有IP不一致都意味着作弊,合法场景包括:
需要警惕的异常模式:
媒体请求 → ADX接收 → 记录请求日志 → 返回广告 →
媒体展示 → 事件上报 → 关联原始请求 → IP比对 →
异常检测 → 结果存储 → 分析报表// 请求记录实体
public class AdRequest {
private String requestId;
private String mediaId;
private String clientIp; // 实际客户端IP
private String xForwardedFor; // 代理链IP
private String deviceId;
private String userAgent;
private String adUnitId;
private Instant requestTime;
// getters & setters
}
// 上报事件实体
public class TrackingEvent {
private String eventId;
private String requestId; // 关联原始请求
private EventType eventType; // IMP, CLICK等
private String reportedIp;
private Instant eventTime;
// getters & setters
}
// IP不一致记录
public class IpInconsistency {
private String requestId;
private String originalIp;
private String reportedIp;
private long timeDiffSeconds;
private boolean suspicious;
private String mediaId;
// getters & setters
}public class IpUtils {
private static final String[] IP_HEADERS = {
"X-Forwarded-For",
"Proxy-Client-IP",
"WL-Proxy-Client-IP",
"HTTP_CLIENT_IP",
"HTTP_X_FORWARDED_FOR"
};
public static String getClientIp(HttpServletRequest request) {
// 检查代理头
for (String header : IP_HEADERS) {
String ipList = request.getHeader(header);
if (ipList != null && !ipList.isEmpty()) {
return ipList.split(",")[0].trim();
}
}
return request.getRemoteAddr();
}
public static boolean isSameNetwork(String ip1, String ip2) {
// 实现IP段比对逻辑
// 可考虑使用第三方库如IPAddress
}
}@Aspect
@Component
public class RequestLoggingAspect {
@Autowired
private RequestLogRepository logRepo;
@Around("execution(* com.your.adx.controller.AdController.getAd(..))")
public Object logRequest(ProceedingJoinPoint pjp) throws Throwable {
HttpServletRequest request =
((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
AdRequest logEntry = new AdRequest();
logEntry.setRequestId(UUID.randomUUID().toString());
logEntry.setClientIp(IpUtils.getClientIp(request));
// 设置其他字段...
logRepo.save(logEntry);
// 将requestId放入响应中供媒体使用
Object result = pjp.proceed();
if (result instanceof ResponseEntity) {
((ResponseEntity)result).getHeaders().add("X-Request-ID", logEntry.getRequestId());
}
return result;
}
}@RestController
@RequestMapping("/track")
public class TrackingController {
@Autowired
private RequestLogRepository requestRepo;
@Autowired
private InconsistencyService inconsistencyService;
@GetMapping("/imp")
public ResponseEntity trackImp(
@RequestParam String requestId,
HttpServletRequest trackingRequest) {
Optional<AdRequest> originalRequest = requestRepo.findById(requestId);
if (!originalRequest.isPresent()) {
return ResponseEntity.badRequest().build();
}
String reportedIp = IpUtils.getClientIp(trackingRequest);
IpInconsistency record = new IpInconsistency();
record.setRequestId(requestId);
record.setOriginalIp(originalRequest.get().getClientIp());
record.setReportedIp(reportedIp);
record.setTimeDiffSeconds(ChronoUnit.SECONDS.between(
originalRequest.get().getRequestTime(),
Instant.now()
));
// 高级检测逻辑
record.setSuspicious(isSuspiciousInconsistency(record));
inconsistencyService.saveRecord(record);
return ResponseEntity.ok().build();
}
private boolean isSuspiciousInconsistency(IpInconsistency record) {
// 规则1: IP段完全不同且时间差短
if (!IpUtils.isSameNetwork(record.getOriginalIp(), record.getReportedIp())
&& record.getTimeDiffSeconds() < 5) {
return true;
}
// 规则2: 上报IP是已知数据中心IP
if (IpUtils.isDatacenterIp(record.getReportedIp())) {
return true;
}
// 其他自定义规则...
return false;
}
}时间序列分析:
地理位置分析:
public class GeoAnalysis {
public static boolean isUnrealisticTravel(String ip1, String ip2, long seconds) {
Location loc1 = geoService.lookup(ip1);
Location loc2 = geoService.lookup(ip2);
double distance = calculateDistance(loc1, loc2);
// 人类不可能在短时间内长距离移动
return distance > (seconds * 100 / 3600); // 假设100km/h为上限
}
}设备指纹一致性:
public class FraudPredictor {
public double predictFraudProbability(IpInconsistency record) {
FeatureVector vector = new FeatureVector();
vector.addFeature("ip_distance",
IpUtils.networkDistance(record.getOriginalIp(), record.getReportedIp()));
vector.addFeature("time_diff", record.getTimeDiffSeconds());
vector.addFeature("geo_speed",
GeoAnalysis.movementSpeed(record.getOriginalIp(),
record.getReportedIp(),
record.getTimeDiffSeconds()));
return model.predict(vector);
}
}缓存层设计:
@Cacheable(value = "ipGeoCache", key = "#ip")
public Location getIpLocation(String ip) {
// 调用第三方地理定位服务
}批量处理:对历史数据采用批处理分析
建立白名单系统:
public class WhitelistService {
@Cacheable("ipWhitelist")
public boolean isWhitelisted(String ipPattern) {
// 检查已知合法的IP模式
}
}有效的IP不一致检测不是一次性的工程,而是需要持续迭代的过程。建议的技术演进路线:
通过本文介绍的技术方案,ADX系统可以建立起从基础检测到高级分析的完整反欺诈能力,在保证正常业务不受影响的前提下,有效识别和拦截作弊流量,维护程序化广告生态的健康发晨。
技术栈参考: