欺诈事件序列数据模型
1. 业务场景
此示例演示了一个典型的欺诈场景,可以通过分析时间顺序排列的事件序列来检测该场景。
-
恶意行为者窃取了客户的登录详细信息并获得了其账户的访问权限。该事件由
Authentication(身份验证)节点表示。 -
攻击者更改关键账户信息以获取控制权
-
更改电话号码 (
ChangePhone节点) -
更改电子邮件地址 (
ChangeEmail节点) -
更改物理地址 (
ChangeAddress节点)
-
-
攻击者添加外部账户 (
AddExternalAccount节点),以便转移受害者的资金。 -
最后,攻击者将资金转移到该外部账户 (
Transfer节点)。
每个事件节点都可以链接到其他提供上下文或详细信息的节点。例如,在 ChangePhone 事件中,会记录更改前后的电话号码。
2. 演进与扩展
该数据模型旨在保持简单,但可以扩展以包含其他事件类型或增加复杂性,从而更好地反映真实场景:
-
ChangeDrivingLicence(更改驾照)
-
ChangePassport(更改护照)
-
ContactSupport(联系支持)
-
等。
4. 节点标签与属性
5. 关系类型与属性
6. 客户增强功能
速度追踪增强
追踪账户变更速度是一个重要的改进方向,这也是关键的欺诈指标。快速连续的变更往往预示着自动攻击或协同欺诈企图。
拟议增强:
-
在连续事件之间的
:NEXT关系上添加时间属性 -
包含
timeDelta(事件之间的时间间隔) -
在滚动时间窗口内追踪累积变更速度
优势
-
通过异常快速的事件序列检测自动攻击模式
-
基于真实的时间模式识别“人 vs. 机器人”行为
-
实现基于速度的实时欺诈评分与拦截
失败尝试追踪增强
当前模型侧重于成功的事件,但应捕获失败的尝试,因为这些往往比成功的事件具有更强的欺诈指示性。
拟议增强:
-
在成功登录前为失败的身份验证尝试创建专用节点
-
追踪被拒绝的变更请求及其失败原因
-
维护所有类型尝试(不仅是成功尝试)的审计追踪
优势
-
失败的登录尝试后紧随成功的访问,通常表明存在撞库攻击 (Credential Stuffing)
-
多次被拒绝的变更请求可能预示着社交工程企图
-
对失败尝试的模式分析可以揭示攻击手段
7. 最小演示代码
以下 Cypher 代码扩展了标准“交易与账户数据模型”,使其具备基于事件的欺诈检测能力。该代码演示了典型的账户接管欺诈是如何通过时间顺序的事件序列展开的。
先决条件: 请先运行 “交易与账户数据模型”演示代码,以创建基础的客户、账户和会话数据。
//--------------------
// Match existing base model entities (created by Transaction & Account Data Model demo)
//--------------------
MATCH (c:Customer {customerId: "CUS001"})
MATCH (s:Session {sessionId: "SESS001"})
MATCH (a:Account:Internal {accountNumber: "ACC001"})
MATCH (originalPhone:Phone {number: "447971020304"})
MATCH (originalEmail:Email {address: "john@example.com"})
MATCH (originalAddr:Address {addressLine1: "123 High Street"})
MATCH (uk:Country {code: "GB"})
MATCH (us:Country {code: "US"})
//--------------------
// Create event-based extensions: Fraud Event Sequence
//--------------------
// Event 1: Fraudulent Authentication (attacker gains access to customer account)
WITH c, s, a, originalPhone, originalEmail, originalAddr, uk, us
CREATE (e1:Authentication {
method: "email",
status: "success",
createdAt: datetime("2024-03-01T14:30:00")
})
// Event 2: Change phone number (5 minutes later)
WITH c, s, a, originalPhone, originalEmail, originalAddr, uk, us, e1
CREATE (e2:ChangePhone {
createdAt: datetime("2024-03-01T14:35:00")
})
CREATE (newPhone:Phone {
number: "447800123456",
countryCode: "+44",
createdAt: datetime("2024-03-01T14:35:00")
})
// Replace customer's phone relationship (simulate successful account takeover)
WITH c, s, a, originalPhone, originalEmail, originalAddr, uk, us, e1, e2, newPhone
MATCH (c)-[r:HAS_PHONE]->(originalPhone) DELETE r
CREATE (c)-[:HAS_PHONE {since: datetime("2024-03-01T14:35:00")}]->(newPhone)
// Event 3: Change email address (2 minutes later)
WITH c, s, a, originalPhone, originalEmail, originalAddr, uk, us, e1, e2, newPhone
CREATE (e3:ChangeEmail {
createdAt: datetime("2024-03-01T14:37:00")
})
CREATE (newEmail:Email {
address: "attacker.new@protonmail.com",
domain: "protonmail.com",
createdAt: datetime("2024-03-01T14:37:00")
})
// Replace customer's email relationship (simulate successful account takeover)
WITH c, s, a, originalPhone, originalEmail, originalAddr, uk, us, e1, e2, e3, newPhone, newEmail
MATCH (c)-[r:HAS_EMAIL]->(originalEmail) DELETE r
CREATE (c)-[:HAS_EMAIL {since: datetime("2024-03-01T14:37:00")}]->(newEmail)
// Event 4: Change address (3 minutes later)
WITH c, s, a, originalPhone, originalEmail, originalAddr, uk, us, e1, e2, e3, newPhone, newEmail
CREATE (e4:ChangeAddress {
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")
})
// Update customer's address relationships (simulate successful account takeover)
WITH c, s, a, originalPhone, originalEmail, originalAddr, uk, us, e1, e2, e3, e4, newPhone, newEmail, newAddr
MATCH (c)-[r:HAS_ADDRESS]->(originalAddr) SET r.isCurrent = false, r.lastChangedAt = datetime("2024-03-01T14:40:00") DELETE r
CREATE (c)-[:HAS_ADDRESS {
addedAt: datetime("2024-03-01T14:40:00"),
lastChangedAt: datetime("2024-03-01T14:40:00"),
isCurrent: true
}]->(newAddr)
CREATE (newAddr)-[:LOCATED_IN]->(uk)
// Event 5: Add external account (10 minutes later)
WITH c, s, a, originalPhone, originalEmail, originalAddr, uk, us, e1, e2, e3, e4, newPhone, newEmail, newAddr
CREATE (e5:AddExternalAccount {
createdAt: datetime("2024-03-01T14:50:00")
})
CREATE (fraudAccount:Account:External:HighRiskJurisdiction {
accountNumber: "FRAUD123456789",
accountType: null,
openedDate: null,
closedDate: null,
suspendedDate: null
})
// Event 6: Transfer money to external account (5 minutes later)
WITH c, s, a, originalPhone, originalEmail, originalAddr, uk, us, e1, e2, e3, e4, e5, newPhone, newEmail, newAddr, fraudAccount
CREATE (e6:Transfer {
createdAt: datetime("2024-03-01T14:55:00")
})
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 event-based relationships
//--------------------
// Chain events chronologically and create all remaining relationships
WITH c, s, a, originalPhone, originalEmail, originalAddr, uk, us, e1, e2, e3, e4, e5, e6, newPhone, newEmail, newAddr, fraudAccount, fraudTransaction
CREATE (e1)-[:NEXT]->(e2)-[:NEXT]->(e3)-[:NEXT]->(e4)-[:NEXT]->(e5)-[:NEXT]->(e6)
CREATE (s)-[:HAS_AUTHENTICATION]->(e1)
CREATE (s)-[:HAS_CHANGE_PHONE]->(e2)
CREATE (s)-[:HAS_CHANGE_EMAIL]->(e3)
CREATE (s)-[:HAS_CHANGE_ADDRESS]->(e4)
CREATE (s)-[:HAS_ADD_EXTERNAL_ACCOUNT]->(e5)
CREATE (s)-[:HAS_TRANSFER]->(e6)
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 (fraudAccount)-[:IS_HOSTED]->(us)
CREATE (e5)-[:ADD_ACCOUNT]->(fraudAccount)
CREATE (e6)-[:HAS_TRANSACTION]->(fraudTransaction)
CREATE (a)-[:PERFORMS]->(fraudTransaction)-[:BENEFITS_TO]->(fraudAccount)
CREATE (c)-[:CONNECTS]->(e1)