账户接管欺诈
1. 简介
账户接管(Account Takeover, ATO)欺诈是一种复杂的身份盗窃形式,网络犯罪分子通过非法手段获取合法用户账户的访问权限。这种日益严重的威胁影响着从金融服务到社交媒体平台等各类账户,对个人和组织都造成了重大影响。根据最新研究,22% 的美国成年人曾遭受过 ATO 欺诈,平均每人损失达 12,000 美元。此类欺诈通常涉及通过网络钓鱼、数据泄露或社会工程学等手段窃取凭据,随后利用账户进行未经授权的交易或实施其他欺诈活动。随着数字服务的扩展,强大的检测和预防策略对于防范这种不断演变的威胁变得愈发关键。
2. 场景
3. 解决方案
图数据库为检测和预防账户接管欺诈提供了一种强大的方法。通过将用户行为、设备交互和账户活动的复杂网络建模为连接的网络,图技术能够识别传统系统可能漏掉的可疑模式。这种方法对于 ATO 欺诈尤为有效,因为 ATO 欺诈需要同时分析多个数据点及其之间的关联。
3.1. 图数据库如何提供帮助?
-
设备指纹识别:Neo4j 可以追踪用户账户、设备和 IP 地址之间的关联,从而识别可疑的登录模式和潜在的撞库攻击。
-
行为分析:图数据库擅长模拟正常的用户行为模式并检测异常,例如:
-
不寻常的登录时间或地点
-
交易模式的可疑变化
-
意外的账户设置修改
-
应用程序内异常的导航模式
-
预示自动化攻击的快速连续账户更改
-
-
身份验证网络:创建全面的身份图谱,连接:
-
用户账户和关联的电子邮件地址
-
电话号码和身份验证方法
-
设备指纹和登录位置
-
交易模式和受益人关系
-
-
实时检测:Neo4j 支持:
-
对照已知模式实时验证登录尝试
-
对交易序列进行实时分析
-
立即识别可疑的 IP 地址或设备
-
基于图模式的动态风险评分
-
-
网络分析:通过以下方式发现复杂的欺诈团伙:
-
识别受损账户之间的共有属性
-
检测可疑活动簇
-
追踪撞库攻击的传播路径
-
映射已知欺诈实体之间的关系
-
-
事件序列追踪:追踪按时间顺序排列的事件链,以检测账户接管模式
-
监控账户修改的速度(每分钟事件数)
-
识别从访问到资金转账的完整攻击生命周期
-
保留旧值与新值的取证轨迹,以供调查使用
-
通过恒定的时间间隔检测自动化攻击模式
-
4. 建模
本节演示了如何使用 Neo4j 构建用于检测账户接管欺诈的数据结构。示例包含一个完整的独立数据集,展示了多种欺诈模式。
模型包括:
-
基础实体:用于追踪访问模式的客户(Customer)、设备(Device)、会话(Session)、IP、位置(Location)节点
-
事件节点:身份验证(Authentication)、更改电话(ChangePhone)、更改邮箱(ChangeEmail)、更改地址(ChangeAddress)、添加外部账户(AddExternalAccount)、转账(Transfer)
-
时间链:在时间序列上链接事件的
:NEXT关系 -
取证追踪:为联系信息更改保留旧值与新值
此方法演示了基于事件的欺诈检测模式。如需更详细的规范和可扩展模型,请参阅 欺诈事件序列数据模型。
4.1. 数据模型
4.1.1 必填字段
Customer(客户)节点
-
customerId:客户唯一标识符
Device(设备)节点
-
deviceId:设备唯一标识符 -
deviceType:设备类型(移动端、桌面端、平板) -
userAgent:浏览器/应用的用户代理字符串 -
createdAt:首次记录设备的时间戳
IP 节点
-
ipAddress:IP 地址 -
createdAt:首次观察到 IP 的时间戳
ISP(互联网服务提供商)节点
-
name:互联网服务提供商名称 -
createdAt:首次记录 ISP 的时间戳
Location(位置)节点
-
city:城市名称 -
postCode:邮政编码(可选) -
country:国家代码 -
latitude:地理纬度(可选) -
longitude:地理经度(可选) -
createdAt:首次记录位置的时间戳
Session(会话)节点
-
sessionId:唯一会话标识符 -
status:会话状态(成功、失败、可疑) -
createdAt:会话启动的时间戳
Account(账户)节点
-
accountNumber:唯一账户号码
Email(邮箱)节点
-
address:电子邮件地址 -
domain:电子邮件域名 -
createdAt:首次记录邮箱的时间戳
Phone(电话)节点
-
number:电话号码 -
countryCode:国际拨号代码 -
createdAt:首次记录电话的时间戳
Address(地址)节点
-
addressLine1:地址第一行 -
addressLine2:地址详细信息(可选) -
postTown:邮政城镇或城市 -
postCode:邮政编码 -
region:县、省或行政区(可选) -
latitude:地理纬度(可选) -
longitude:地理经度(可选) -
createdAt:首次记录地址的时间戳
Transaction(交易)节点
-
transactionId:唯一交易标识符 -
amount:交易金额 -
currency:三字母货币代码(例如 "GBP", "USD") -
date:交易处理的时间戳 -
message:付款参考或说明(可选) -
type:付款方式或交易类型(可选)
Country(国家)节点
-
code:两字母 ISO 国家代码 -
name:国家名称
事件节点(用于追踪账户接管序列)
Authentication(身份验证)节点
-
method:使用的身份验证方法(例如 "email", "phone_number") -
status:身份验证尝试的状态(例如 "success", "failed") -
createdAt:建立身份验证的时间戳
ChangePhone(更改电话)节点
-
createdAt:电话号码更改的时间戳
ChangeEmail(更改邮箱)节点
-
createdAt:电子邮件地址更改的时间戳
ChangeAddress(更改地址)节点
-
createdAt:地址更改的时间戳
AddExternalAccount(添加外部账户)节点
-
createdAt:添加外部账户的时间戳
Transfer(转账)节点
-
createdAt:执行转账的时间戳
关系
-
USED_BY:客户使用的设备 -
USES_IP:会话使用的 IP 地址 -
SESSION_USES_DEVICE:会话使用的设备 -
HAS_ACCOUNT:客户拥有的账户 -
HAS_EMAIL:客户拥有的邮箱 -
HAS_PHONE:客户拥有的电话 -
HAS_ADDRESS:客户拥有的地址 -
IS_ALLOCATED_TO:分配给 ISP 的 IP -
LOCATED_IN:位于某国家的 IP/位置/地址 -
IS_HOSTED:在某国家托管的账户 -
PERFORMS:账户执行的交易 -
BENEFITS_TO:交易收益方账户 -
HAS_SESSION:客户拥有的会话
基于事件的关系
-
CONNECTS:客户通过身份验证连接 -
NEXT:按时间顺序链接事件(事件→事件) -
HAS_AUTHENTICATION:会话包含的身份验证事件 -
HAS_CHANGE_PHONE:会话包含的电话更改事件 -
HAS_CHANGE_EMAIL:会话包含的邮箱更改事件 -
HAS_CHANGE_ADDRESS:会话包含的地址更改事件 -
HAS_ADD_EXTERNAL_ACCOUNT:会话包含的外部账户添加事件 -
HAS_TRANSFER:会话包含的转账事件 -
OLD_PHONE:链接到之前的电话号码(更改电话→电话) -
NEW_PHONE:链接到新的电话号码(更改电话→电话) -
OLD_EMAIL:链接到之前的邮箱(更改邮箱→邮箱) -
NEW_EMAIL:链接到新的邮箱(更改邮箱→邮箱) -
OLD_ADDRESS:链接到之前的地址(更改地址→地址) -
NEW_ADDRESS:链接到新的地址(更改地址→地址) -
ADD_ACCOUNT:链接到添加的账户(添加外部账户→账户) -
HAS_TRANSACTION:链接到交易(转账→交易)
4.2. 演示数据
以下 Cypher 代码创建了一个完整的示例,展示了多种账户接管欺诈模式
//--------------------
// Create Countries
//--------------------
CREATE (uk:Country {code: "GB", name: "United Kingdom"})
CREATE (us:Country {code: "US", name: "United States"})
CREATE (cn:Country {code: "CN", name: "China"})
CREATE (ng:Country {code: "NG", name: "Nigeria"})
//--------------------
// Create Customers
//--------------------
CREATE (c1:Customer {customerId: "CUS001"})
CREATE (c2:Customer {customerId: "CUS002"})
CREATE (c3:Customer {customerId: "CUS003"})
//--------------------
// Create Devices
//--------------------
CREATE (d1:Device {deviceId: "DEV001", deviceType: "desktop", userAgent: "Mozilla/5.0 Chrome/91.0", createdAt: datetime("2024-03-01T09:00:00")})
CREATE (d2:Device {deviceId: "DEV002", deviceType: "mobile", userAgent: "Mozilla/5.0 Mobile Safari/537.36", createdAt: datetime("2024-03-01T09:30:00")})
CREATE (d3:Device {deviceId: "SUSPICIOUS001", deviceType: "desktop", userAgent: "Mozilla/5.0 Firefox/89.0", createdAt: datetime("2024-03-01T10:00:00")})
//--------------------
// Create ISPs and Locations
//--------------------
CREATE (isp1:ISP {name: "BT", createdAt: datetime("2024-01-01T00:00:00")})
CREATE (isp2:ISP {name: "Orange", createdAt: datetime("2024-01-01T00:00:00")})
CREATE (isp3:ISP {name: "Verizon", createdAt: datetime("2024-01-01T00:00:00")})
CREATE (isp4:ISP {name: "China Telecom", createdAt: datetime("2024-01-01T00:00:00")})
//
// Create Location nodes
//
CREATE (l1:Location {city: "London", postCode: "XX30 1XX", country: "UK", latitude: 51.5074, longitude: -0.1278, createdAt: datetime("2024-01-01T00:00:00")})
CREATE (l2:Location {city: "Paris", postCode: "75001", country: "France", latitude: 48.8566, longitude: 2.3522, createdAt: datetime("2024-01-01T00:00:00")})
CREATE (l3:Location {city: "Beijing", postCode: "100000", country: "China", latitude: 39.9042, longitude: 116.4074, createdAt: datetime("2024-01-01T00:00:00")})
CREATE (l4:Location {city: "Lagos", postCode: "100001", country: "Nigeria", latitude: 6.5244, longitude: 3.3792, createdAt: datetime("2024-01-01T00:00:00")})
CREATE (l5:Location {city: "New York", postCode: "10001", country: "USA", latitude: 40.7128, longitude: -74.0060, createdAt: datetime("2024-01-01T00:00:00")})
//--------------------
// Create IP Addresses
//--------------------
CREATE (ip1:IP {ipAddress: "192.168.1.1", createdAt: datetime("2024-03-01T09:00:00")})
CREATE (ip2:IP {ipAddress: "10.0.0.1", createdAt: datetime("2024-03-01T10:00:00")})
CREATE (ip3:IP {ipAddress: "203.0.113.1", createdAt: datetime("2024-03-01T10:05:00")})
CREATE (ip4:IP {ipAddress: "198.51.100.1", createdAt: datetime("2024-03-01T11:00:00")})
CREATE (ip5:IP {ipAddress: "172.16.0.1", createdAt: datetime("2024-03-01T11:05:00")})
//--------------------
// Create Contact Information (Original)
//--------------------
CREATE (originalPhone:Phone {number: "447971020304", countryCode: "+44", createdAt: datetime("2024-01-01T00:00:00")})
CREATE (originalEmail:Email {address: "john@example.com", domain: "example.com", emailType: "personal", createdAt: datetime("2024-01-01T00:00:00")})
CREATE (originalAddr:Address {
addressLine1: "123 High Street",
addressLine2: "Flat 4B",
postTown: "London",
postCode: "SW1A 1AA",
region: "Greater London",
latitude: 51.5074,
longitude: -0.1278,
createdAt: datetime("2024-01-01T00:00:00")
})
//--------------------
// Create Contact Information (Changed by Attacker)
//--------------------
CREATE (newPhone:Phone {number: "447800123456", countryCode: "+44", createdAt: datetime("2024-03-01T14:35:00")})
CREATE (newEmail:Email {address: "attacker.new@protonmail.com", domain: "protonmail.com", emailType: "personal", createdAt: datetime("2024-03-01T14:40:00")})
CREATE (newAddr:Address {
addressLine1: "999 Fraud Street",
addressLine2: "Unit 13",
postTown: "London",
postCode: "E1 6XX",
region: "Greater London",
latitude: 51.5171,
longitude: -0.0574,
createdAt: datetime("2024-03-01T14:40:00")
})
//--------------------
// Create Accounts
//--------------------
CREATE (a1:Account:Internal {accountNumber: "ACC001", accountType: "CURRENT", openedDate: datetime("2024-01-01T00:00:00"), closedDate: null, suspendedDate: null})
CREATE (a2:Account:Internal {accountNumber: "ACC002", accountType: "CURRENT", openedDate: datetime("2024-01-01T00:00:00"), closedDate: null, suspendedDate: null})
CREATE (a3:Account:Internal {accountNumber: "ACC003", accountType: "CURRENT", openedDate: datetime("2024-01-01T00:00:00"), closedDate: null, suspendedDate: null})
CREATE (fraudAccount:Account:External:HighRiskJurisdiction {accountNumber: "FRAUD123456789", accountType: null, openedDate: null, closedDate: null, suspendedDate: null})
//--------------------
// Create Sessions (Normal, Failed, and ATO Session)
//--------------------
CREATE (s1:Session {sessionId: "SESS001", status: "success", createdAt: datetime("2024-03-01T14:30:00")})
CREATE (s2:Session {sessionId: "SESS002", status: "success", createdAt: datetime("2024-03-01T10:05:00")})
CREATE (s3:Session {sessionId: "SESS003", status: "failed", createdAt: datetime("2024-03-01T11:00:00")})
CREATE (s4:Session {sessionId: "SESS004", status: "failed", createdAt: datetime("2024-03-01T11:05:00")})
CREATE (s5:Session {sessionId: "SESS005", status: "failed", createdAt: datetime("2024-03-01T11:10:00")})
//--------------------
// Create Fraud Event Sequence (for Customer 1 - ATO Victim)
//--------------------
CREATE (e1:Authentication {method: "email", status: "success", createdAt: datetime("2024-03-01T14:30:00")})
CREATE (e2:ChangePhone {createdAt: datetime("2024-03-01T14:35:00")})
CREATE (e3:ChangeEmail {createdAt: datetime("2024-03-01T14:37:00")})
CREATE (e4:ChangeAddress {createdAt: datetime("2024-03-01T14:40:00")})
CREATE (e5:AddExternalAccount {createdAt: datetime("2024-03-01T14:50:00")})
CREATE (e6:Transfer {createdAt: datetime("2024-03-01T14:55:00")})
//--------------------
// Create Transaction (Fraudulent Transfer)
//--------------------
CREATE (fraudTransaction:Transaction {
transactionId: "TXN_FRAUD_001",
amount: 15000.00,
currency: "GBP",
date: datetime("2024-03-01T14:55:00"),
message: "Emergency transfer",
type: "SWIFT"
})
//--------------------
// Create Relationships: Countries
//--------------------
CREATE (l1)-[:LOCATED_IN]->(uk)
CREATE (l2)-[:LOCATED_IN]->(uk)
CREATE (l3)-[:LOCATED_IN]->(cn)
CREATE (l4)-[:LOCATED_IN]->(ng)
CREATE (l5)-[:LOCATED_IN]->(us)
CREATE (originalAddr)-[:LOCATED_IN]->(uk)
CREATE (newAddr)-[:LOCATED_IN]->(uk)
CREATE (a1)-[:IS_HOSTED]->(uk)
CREATE (a2)-[:IS_HOSTED]->(uk)
CREATE (a3)-[:IS_HOSTED]->(uk)
CREATE (fraudAccount)-[:IS_HOSTED]->(us)
//--------------------
// Create Relationships: Customer Identity
//--------------------
CREATE (c1)-[:HAS_PHONE {since: datetime("2024-03-01T14:35:00")}]->(newPhone)
CREATE (c1)-[:HAS_EMAIL {since: datetime("2024-03-01T14:37:00")}]->(newEmail)
CREATE (c1)-[:HAS_ADDRESS {addedAt: datetime("2024-03-01T14:40:00"), lastChangedAt: datetime("2024-03-01T14:40:00"), isCurrent: true}]->(newAddr)
CREATE (c1)-[:HAS_ACCOUNT {role: "owner", since: datetime("2024-01-01T00:00:00")}]->(a1)
CREATE (c2)-[:HAS_ACCOUNT {role: "owner", since: datetime("2024-01-01T00:00:00")}]->(a2)
CREATE (c3)-[:HAS_ACCOUNT {role: "owner", since: datetime("2024-01-01T00:00:00")}]->(a3)
//--------------------
// Create Relationships: Devices
//--------------------
CREATE (d1)-[:USED_BY {lastUsed: datetime("2024-03-01T09:00:00")}]->(c1)
CREATE (d2)-[:USED_BY {lastUsed: datetime("2024-03-01T09:30:00")}]->(c2)
// Pattern 1: Credential Stuffing - Single device accessing multiple accounts
CREATE (d3)-[:USED_BY {lastUsed: datetime("2024-03-01T10:00:00")}]->(c1)
CREATE (d3)-[:USED_BY {lastUsed: datetime("2024-03-01T10:02:00")}]->(c2)
CREATE (d3)-[:USED_BY {lastUsed: datetime("2024-03-01T10:04:00")}]->(c3)
//--------------------
// Create Relationships: Sessions to Customers
//--------------------
CREATE (c1)-[:HAS_SESSION]->(s1)
CREATE (c1)-[:HAS_SESSION]->(s2)
CREATE (c2)-[:HAS_SESSION]->(s3)
CREATE (c2)-[:HAS_SESSION]->(s4)
CREATE (c2)-[:HAS_SESSION]->(s5)
//--------------------
// Create Relationships: Sessions to Devices and IPs
//--------------------
CREATE (s1)-[:SESSION_USES_DEVICE]->(d1)
CREATE (s1)-[:USES_IP]->(ip1)
// Pattern 2: Impossible travel - UK to China in 5 minutes
CREATE (s2)-[:SESSION_USES_DEVICE]->(d3)
CREATE (s2)-[:USES_IP]->(ip3)
// Pattern 3: Multiple failed login attempts from different IPs and locations
CREATE (s3)-[:SESSION_USES_DEVICE]->(d2)
CREATE (s3)-[:USES_IP]->(ip2)
CREATE (s4)-[:SESSION_USES_DEVICE]->(d2)
CREATE (s4)-[:USES_IP]->(ip4)
CREATE (s5)-[:SESSION_USES_DEVICE]->(d2)
CREATE (s5)-[:USES_IP]->(ip5)
//--------------------
// Create Relationships: IPs to ISPs and Locations
//--------------------
CREATE (ip1)-[:IS_ALLOCATED_TO {createdAt: datetime("2024-01-01T00:00:00")}]->(isp1)
CREATE (ip1)-[:LOCATED_IN {createdAt: datetime("2024-03-01T09:00:00")}]->(l1)
CREATE (ip2)-[:IS_ALLOCATED_TO {createdAt: datetime("2024-01-01T00:00:00")}]->(isp2)
CREATE (ip2)-[:LOCATED_IN {createdAt: datetime("2024-03-01T10:00:00")}]->(l2)
CREATE (ip3)-[:IS_ALLOCATED_TO {createdAt: datetime("2024-01-01T00:00:00")}]->(isp4)
CREATE (ip3)-[:LOCATED_IN {createdAt: datetime("2024-03-01T10:05:00")}]->(l3)
CREATE (ip4)-[:IS_ALLOCATED_TO {createdAt: datetime("2024-01-01T00:00:00")}]->(isp3)
CREATE (ip4)-[:LOCATED_IN {createdAt: datetime("2024-03-01T11:00:00")}]->(l4)
CREATE (ip5)-[:IS_ALLOCATED_TO {createdAt: datetime("2024-01-01T00:00:00")}]->(isp3)
CREATE (ip5)-[:LOCATED_IN {createdAt: datetime("2024-03-01T11:05:00")}]->(l5)
//--------------------
// Create Relationships: Event Chain and Session Events
//--------------------
CREATE (e1)-[:NEXT]->(e2)-[:NEXT]->(e3)-[:NEXT]->(e4)-[:NEXT]->(e5)-[:NEXT]->(e6)
CREATE (s1)-[:HAS_AUTHENTICATION]->(e1)
CREATE (s1)-[:HAS_CHANGE_PHONE]->(e2)
CREATE (s1)-[:HAS_CHANGE_EMAIL]->(e3)
CREATE (s1)-[:HAS_CHANGE_ADDRESS]->(e4)
CREATE (s1)-[:HAS_ADD_EXTERNAL_ACCOUNT]->(e5)
CREATE (s1)-[:HAS_TRANSFER]->(e6)
CREATE (c1)-[:CONNECTS]->(e1)
//--------------------
// Create Relationships: Event Change Tracking (Old vs New)
//--------------------
CREATE (e2)-[:OLD_PHONE]->(originalPhone)
CREATE (e2)-[:NEW_PHONE]->(newPhone)
CREATE (e3)-[:OLD_EMAIL]->(originalEmail)
CREATE (e3)-[:NEW_EMAIL]->(newEmail)
CREATE (e4)-[:OLD_ADDRESS]->(originalAddr)
CREATE (e4)-[:NEW_ADDRESS]->(newAddr)
//--------------------
// Create Relationships: Account Addition and Transaction
//--------------------
CREATE (e5)-[:ADD_ACCOUNT]->(fraudAccount)
CREATE (e6)-[:HAS_TRANSACTION]->(fraudTransaction)
CREATE (a1)-[:PERFORMS]->(fraudTransaction)-[:BENEFITS_TO]->(fraudAccount)
5. Cypher 查询
5.1. 单个设备登录多个不同账户
在此查询中,我们将识别用于访问多个不同用户账户的设备,这是撞库攻击和账户接管尝试中的常见模式。
查看图谱
// Find all devices and the customers who use them MATCH (d:Device)-[:USED_BY]→(c:Customer)
MATCH (d:Device)-[:USED_BY]->(c:Customer)
// Count how many different customers use each device WITH d, count(DISTINCT c) as customerCount
WITH d, count(DISTINCT c) as customerCount
// Flag devices used by more than one customer WHERE customerCount > 1
WHERE customerCount > 1
// Retrieve the full network: device → customers → accounts MATCH path=(d)-[:USED_BY]→(c:Customer)-[:HAS_ACCOUNT]→(a:Account)
MATCH path=(d)-[:USED_BY]->(c:Customer)-[:HAS_ACCOUNT]->(a:Account)
// Return the suspicious pattern for investigation RETURN path
RETURN path
查看统计数据
// Get detailed statistics about devices accessing multiple accounts
MATCH (d:Device)-[:USED_BY]->(c:Customer)-[:HAS_ACCOUNT]->(a:Account)
WITH d,
count(c) as uniqueAccounts,
collect(c.customerId) as compromisedCustomers,
d.deviceType as deviceType,
d.userAgent as userAgent
WHERE uniqueAccounts > 1
RETURN d.deviceId as DeviceID,
deviceType as DeviceType,
userAgent as UserAgent,
uniqueAccounts as NumberOfAccounts,
compromisedCustomers as CompromisedCustomerIDs
ORDER BY uniqueAccounts DESC
5.2. 可疑会话模式
在这些查询中,我们分析会话模式,通过异常的会话行为、失败的登录尝试以及会话内的可疑位置变更来识别潜在的账户接管尝试。
查看失败的登录尝试
// Show clusters of failed login attempts within a time window
MATCH (c:Customer)-[:HAS_SESSION]->(s:Session)
WHERE s.status = 'failed'
WITH c, s
ORDER BY s.createdAt
WITH c,
collect({
sessionId: s.sessionId,
sessionTime: s.createdAt,
status: s.status
}) as attempts
WHERE size(attempts) >= 3
RETURN c.customerId as CustomerID,
attempts,
size(attempts) as FailedAttempts
ORDER BY FailedAttempts DESC
查看位置变更
// Detect rapid location changes within sessions (impossible travel)
MATCH (c:Customer)-[:HAS_SESSION]->(s:Session)-[:USES_IP]->(ip:IP)-[:LOCATED_IN]->(l:Location)
WITH c, s, l
ORDER BY s.createdAt
WITH c,
collect({
location: l.city + ', ' + l.country,
sessionTime: s.createdAt
}) as locations
WHERE size(locations) > 1
RETURN c.customerId as CustomerID,
locations,
size(locations) as LocationChanges
ORDER BY LocationChanges DESC
查看会话时间轴
// Analyse session patterns over time
MATCH (c:Customer)-[:HAS_SESSION]->(s:Session)-[:SESSION_USES_DEVICE]->(d:Device)
WITH c, d, s
RETURN c.customerId as CustomerID,
d.deviceId as DeviceID,
d.deviceType as DeviceType,
s.createdAt as SessionTime,
s.status as SessionStatus
ORDER BY s.createdAt
5.3. 来自不同 IP 的多次失败登录尝试
在这些查询中,我们分析针对同一账户的来自不同 IP 地址的失败登录尝试模式,这是暴力破解攻击的常见指标。
查看失败登录模式
// Show accounts with multiple failed login attempts from different IPs
MATCH (c:Customer)-[:HAS_SESSION]->(s:Session)-[:USES_IP]->(ip:IP)
WHERE s.status = 'failed'
WITH c, count(DISTINCT ip) as uniqueIPs, collect(DISTINCT ip.ipAddress) as ipAddresses,
count(s) as totalFailedAttempts
WHERE uniqueIPs >= 2
RETURN c.customerId as TargetCustomer,
totalFailedAttempts as FailedAttempts,
uniqueIPs as NumberOfUniqueIPs,
ipAddresses as IPAddresses
ORDER BY totalFailedAttempts DESC
查看详细时间轴
// Show detailed timeline of failed attempts with location context
MATCH (c:Customer)-[:HAS_SESSION]->(s:Session)-[:USES_IP]->(ip:IP),
(ip)-[:LOCATED_IN]->(l:Location),
(ip)-[:IS_ALLOCATED_TO]->(isp:ISP)
WHERE s.status = 'failed'
WITH c, count(DISTINCT ip) as uniqueIPs
WHERE uniqueIPs >= 2
MATCH (c)-[:HAS_SESSION]->(s:Session)-[:USES_IP]->(ip:IP),
(ip)-[:LOCATED_IN]->(l:Location),
(ip)-[:IS_ALLOCATED_TO]->(isp:ISP)
WHERE s.status = 'failed'
RETURN c.customerId as TargetCustomer,
s.createdAt as AttemptTime,
ip.ipAddress as IPAddress,
l.city + ', ' + l.country as Location,
isp.name as ISP
ORDER BY c.customerId, s.createdAt
查看地理分布
// Show geographic distribution of failed attempts
MATCH (c:Customer)-[:HAS_SESSION]->(s:Session)-[:USES_IP]->(ip:IP)-[:LOCATED_IN]->(l:Location)
WHERE s.status = 'failed'
WITH c, l, count(s) as attemptsFromLocation
WITH c,
count(DISTINCT l) as uniqueLocations,
collect(DISTINCT {
location: l.city + ', ' + l.country,
attempts: attemptsFromLocation
}) as locationBreakdown
WHERE uniqueLocations >= 2
RETURN c.customerId as TargetCustomer,
uniqueLocations as NumberOfLocations,
locationBreakdown as LocationBreakdown
ORDER BY uniqueLocations DESC
5.4. 事件速度分析
使用时间顺序事件链检测快速的连续账户变更。此查询识别出多个关键账户修改在短时间内连续发生的各种可疑模式,这预示着自动化攻击。
检测快速事件序列
// Find sessions with rapid event sequences (multiple events within 30 minutes)
MATCH (c:Customer)-[:CONNECTS]->(firstEvent)
WHERE firstEvent:Authentication
MATCH path = (firstEvent)-[:NEXT*]->(lastEvent)
WITH c, firstEvent, lastEvent,
duration.inSeconds(firstEvent.createdAt, lastEvent.createdAt).seconds AS durationSeconds,
length(path) + 1 AS eventCount
WHERE durationSeconds <= 1800 AND eventCount >= 3
RETURN c.customerId AS CustomerID,
firstEvent.createdAt AS FirstEventTime,
lastEvent.createdAt AS LastEventTime,
eventCount AS NumberOfEvents,
durationSeconds / 60.0 AS DurationMinutes,
eventCount / (durationSeconds / 60.0) AS EventsPerMinute
ORDER BY EventsPerMinute DESC
查看带有时间增量的事件时间轴
// Show detailed event timeline with time between consecutive events
MATCH (c:Customer)-[:CONNECTS]->(firstEvent:Authentication)
MATCH path = (firstEvent)-[:NEXT*0..]->(event)
WITH c, event,
[(event)-[:NEXT]->(nextEvent) | duration.inSeconds(event.createdAt, nextEvent.createdAt).seconds][0] AS secondsToNext
ORDER BY event.createdAt
RETURN c.customerId AS CustomerID,
labels(event) AS EventType,
event.createdAt AS EventTime,
secondsToNext AS SecondsToNextEvent,
secondsToNext / 60.0 AS MinutesToNextEvent
ORDER BY c.customerId, EventTime
5.5. 账户接管生命周期检测
识别从身份验证到资金转账的完整 ATO 攻击模式。这可以检测到经典的账户接管序列:未经授权的访问 → 联系方式更改 → 添加外部账户 → 欺诈性转账。
检测完整 ATO 模式
// Find complete account takeover sequences ending in transfers
MATCH (c:Customer)-[:CONNECTS]->(auth:Authentication)
WHERE auth.status = "success"
WITH c, auth
MATCH (auth)-[:NEXT*1..10]->(changeEvent)
WHERE changeEvent:ChangePhone OR changeEvent:ChangeEmail OR changeEvent:ChangeAddress
WITH c, auth, changeEvent
MATCH (changeEvent)-[:NEXT*1..5]->(add:AddExternalAccount)
WITH c, auth, add
MATCH (add)-[:NEXT*1..3]->(transfer:Transfer)-[:HAS_TRANSACTION]->(t:Transaction)
RETURN DISTINCT c.customerId AS VictimCustomer,
auth.createdAt AS InitialAccessTime,
transfer.createdAt AS FraudTransferTime,
t.amount AS TransferAmount,
t.currency AS Currency,
duration.inSeconds(auth.createdAt, transfer.createdAt).seconds / 60.0 AS MinutesFromAccessToTransfer
ORDER BY TransferAmount DESC
查看完整攻击链
// Show complete event chain for suspected account takeover
MATCH (c:Customer)-[:CONNECTS]->(auth:Authentication)
MATCH path = (auth)-[:NEXT*]->(finalEvent)
WHERE finalEvent:Transfer
WITH c, auth, path, finalEvent
MATCH (finalEvent)-[:HAS_TRANSACTION]->(t:Transaction)-[:BENEFITS_TO]->(externalAccount:Account:External)
UNWIND nodes(path) AS event
RETURN c.customerId AS CustomerID,
labels(event) AS EventType,
event.createdAt AS EventTime,
t.amount AS FraudAmount,
externalAccount.accountNumber AS DestinationAccount
ORDER BY c.customerId, EventTime
5.6. 变更历史分析
追踪旧值与新值以进行取证调查。这使欺诈分析师能够清楚地看到账户接管期间发生了哪些变动,从而为调查和客户恢复提供关键证据。
查看电话号码变更
// Show phone number changes with old and new values
MATCH (c:Customer)-[:HAS_SESSION]->(s:Session)-[:HAS_CHANGE_PHONE]->(change:ChangePhone)
MATCH (change)-[:OLD_PHONE]->(oldPhone:Phone)
MATCH (change)-[:NEW_PHONE]->(newPhone:Phone)
RETURN c.customerId AS CustomerID,
change.createdAt AS ChangeTime,
oldPhone.number AS OldPhoneNumber,
newPhone.number AS NewPhoneNumber,
s.sessionId AS SessionID
ORDER BY change.createdAt DESC
查看邮箱变更
// Show email address changes with old and new values
MATCH (c:Customer)-[:HAS_SESSION]->(s:Session)-[:HAS_CHANGE_EMAIL]->(change:ChangeEmail)
MATCH (change)-[:OLD_EMAIL]->(oldEmail:Email)
MATCH (change)-[:NEW_EMAIL]->(newEmail:Email)
RETURN c.customerId AS CustomerID,
change.createdAt AS ChangeTime,
oldEmail.address AS OldEmailAddress,
newEmail.address AS NewEmailAddress,
newEmail.domain AS NewEmailDomain,
s.sessionId AS SessionID
ORDER BY change.createdAt DESC
查看地址变更
// Show address changes with old and new values
MATCH (c:Customer)-[:HAS_SESSION]->(s:Session)-[:HAS_CHANGE_ADDRESS]->(change:ChangeAddress)
MATCH (change)-[:OLD_ADDRESS]->(oldAddr:Address)
MATCH (change)-[:NEW_ADDRESS]->(newAddr:Address)
RETURN c.customerId AS CustomerID,
change.createdAt AS ChangeTime,
oldAddr.addressLine1 + ', ' + oldAddr.postTown + ' ' + oldAddr.postCode AS OldAddress,
newAddr.addressLine1 + ', ' + newAddr.postTown + ' ' + newAddr.postCode AS NewAddress,
s.sessionId AS SessionID
ORDER BY change.createdAt DESC
查看客户的所有变更
// Show complete change history for a specific customer
MATCH (c:Customer {customerId: "CUS001"})-[:HAS_SESSION]->(s:Session)
OPTIONAL MATCH (s)-[:HAS_CHANGE_PHONE]->(phoneChange:ChangePhone)
OPTIONAL MATCH (phoneChange)-[:OLD_PHONE]->(oldPhone:Phone)
OPTIONAL MATCH (phoneChange)-[:NEW_PHONE]->(newPhone:Phone)
OPTIONAL MATCH (s)-[:HAS_CHANGE_EMAIL]->(emailChange:ChangeEmail)
OPTIONAL MATCH (emailChange)-[:OLD_EMAIL]->(oldEmail:Email)
OPTIONAL MATCH (emailChange)-[:NEW_EMAIL]->(newEmail:Email)
OPTIONAL MATCH (s)-[:HAS_CHANGE_ADDRESS]->(addrChange:ChangeAddress)
OPTIONAL MATCH (addrChange)-[:OLD_ADDRESS]->(oldAddr:Address)
OPTIONAL MATCH (addrChange)-[:NEW_ADDRESS]->(newAddr:Address)
RETURN c.customerId AS CustomerID,
coalesce(phoneChange.createdAt, emailChange.createdAt, addrChange.createdAt) AS ChangeTime,
CASE
WHEN phoneChange IS NOT NULL THEN 'Phone'
WHEN emailChange IS NOT NULL THEN 'Email'
WHEN addrChange IS NOT NULL THEN 'Address'
END AS ChangeType,
oldPhone.number AS OldPhone,
newPhone.number AS NewPhone,
oldEmail.address AS OldEmail,
newEmail.address AS NewEmail,
oldAddr.addressLine1 AS OldAddress,
newAddr.addressLine1 AS NewAddress
ORDER BY ChangeTime