| | | 1 | | using Intentum.Core.Behavior; |
| | | 2 | | using Intentum.Core.Models; |
| | | 3 | | |
| | | 4 | | namespace Intentum.Core.Fraud; |
| | | 5 | | |
| | | 6 | | /// <summary> |
| | | 7 | | /// Pre-built fraud detection rules for common attack patterns. |
| | | 8 | | /// Use with RuleBasedIntentModel or ChainedIntentModel. |
| | | 9 | | /// </summary> |
| | | 10 | | public static class FraudRules |
| | | 11 | | { |
| | | 12 | | /// <summary> |
| | | 13 | | /// Detects account takeover attempts: multiple failed logins + password reset + IP/device change. |
| | | 14 | | /// </summary> |
| | | 15 | | public static Func<BehaviorSpace, RuleMatch?> AccountTakeover( |
| | | 16 | | int minFailedLogins = 3, |
| | 4 | 17 | | double confidence = 0.9) => space => |
| | 3 | 18 | | { |
| | 3 | 19 | | var failedLogins = space.Events.Count(e => |
| | 12 | 20 | | e.Action.Contains("login.failed", StringComparison.OrdinalIgnoreCase) || |
| | 12 | 21 | | e.Action.Contains("FailedLogin", StringComparison.OrdinalIgnoreCase)); |
| | 3 | 22 | | var hasIpChange = space.Events.Any(e => |
| | 12 | 23 | | e.Action.Contains("ip.changed", StringComparison.OrdinalIgnoreCase) || |
| | 12 | 24 | | e.Action.Contains("device.new", StringComparison.OrdinalIgnoreCase) || |
| | 12 | 25 | | e.Action.Contains("NewDevice", StringComparison.OrdinalIgnoreCase)); |
| | 3 | 26 | | var hasPasswordReset = space.Events.Any(e => |
| | 12 | 27 | | e.Action.Contains("password.reset", StringComparison.OrdinalIgnoreCase) || |
| | 12 | 28 | | e.Action.Contains("PasswordReset", StringComparison.OrdinalIgnoreCase)); |
| | 4 | 29 | | |
| | 3 | 30 | | if (failedLogins >= minFailedLogins && (hasIpChange || hasPasswordReset)) |
| | 2 | 31 | | return new RuleMatch("AccountTakeover", confidence, |
| | 2 | 32 | | $"Failed logins: {failedLogins}, IP change: {hasIpChange}, password reset: {hasPasswordReset}"); |
| | 1 | 33 | | return null; |
| | 7 | 34 | | }; |
| | | 35 | | |
| | | 36 | | /// <summary> |
| | | 37 | | /// Detects credential stuffing: high-frequency login attempts from different actors. |
| | | 38 | | /// </summary> |
| | | 39 | | public static Func<BehaviorSpace, RuleMatch?> CredentialStuffing( |
| | | 40 | | int minAttempts = 5, |
| | 2 | 41 | | double confidence = 0.85) => space => |
| | 1 | 42 | | { |
| | 1 | 43 | | var loginAttempts = space.Events.Count(e => |
| | 6 | 44 | | e.Action.Contains("login", StringComparison.OrdinalIgnoreCase)); |
| | 6 | 45 | | var uniqueActors = space.Events.Select(e => e.Actor).Distinct().Count(); |
| | 2 | 46 | | |
| | 1 | 47 | | if (loginAttempts >= minAttempts && uniqueActors >= 3) |
| | 1 | 48 | | return new RuleMatch("CredentialStuffing", confidence, |
| | 1 | 49 | | $"Login attempts: {loginAttempts} from {uniqueActors} actors"); |
| | 0 | 50 | | return null; |
| | 3 | 51 | | }; |
| | | 52 | | |
| | | 53 | | /// <summary> |
| | | 54 | | /// Detects payment fraud: rapid high-value transactions, card testing patterns. |
| | | 55 | | /// </summary> |
| | | 56 | | public static Func<BehaviorSpace, RuleMatch?> PaymentFraud( |
| | | 57 | | int minTransactions = 3, |
| | 2 | 58 | | double confidence = 0.8) => space => |
| | 1 | 59 | | { |
| | 1 | 60 | | var payments = space.Events.Count(e => |
| | 6 | 61 | | e.Action.Contains("payment", StringComparison.OrdinalIgnoreCase) || |
| | 6 | 62 | | e.Action.Contains("transaction", StringComparison.OrdinalIgnoreCase)); |
| | 1 | 63 | | var declines = space.Events.Count(e => |
| | 6 | 64 | | e.Action.Contains("declined", StringComparison.OrdinalIgnoreCase) || |
| | 6 | 65 | | e.Action.Contains("failed", StringComparison.OrdinalIgnoreCase)); |
| | 2 | 66 | | |
| | 1 | 67 | | if (payments >= minTransactions && declines >= 2) |
| | 1 | 68 | | return new RuleMatch("PaymentFraud", confidence, |
| | 1 | 69 | | $"Payments: {payments}, declines: {declines}"); |
| | 0 | 70 | | return null; |
| | 3 | 71 | | }; |
| | | 72 | | |
| | | 73 | | /// <summary> |
| | | 74 | | /// Detects legitimate account recovery: failed login + password reset + successful login. |
| | | 75 | | /// </summary> |
| | | 76 | | public static Func<BehaviorSpace, RuleMatch?> AccountRecovery( |
| | 2 | 77 | | double confidence = 0.85) => space => |
| | 1 | 78 | | { |
| | 1 | 79 | | var hasFailed = space.Events.Any(e => |
| | 2 | 80 | | e.Action.Contains("login.failed", StringComparison.OrdinalIgnoreCase)); |
| | 1 | 81 | | var hasReset = space.Events.Any(e => |
| | 3 | 82 | | e.Action.Contains("password.reset", StringComparison.OrdinalIgnoreCase)); |
| | 1 | 83 | | var hasSuccess = space.Events.Any(e => |
| | 4 | 84 | | e.Action.Contains("login.success", StringComparison.OrdinalIgnoreCase)); |
| | 2 | 85 | | |
| | 1 | 86 | | if (hasFailed && hasReset && hasSuccess) |
| | 1 | 87 | | return new RuleMatch("AccountRecovery", confidence, |
| | 1 | 88 | | "Pattern: failed login -> password reset -> successful login"); |
| | 0 | 89 | | return null; |
| | 3 | 90 | | }; |
| | | 91 | | |
| | | 92 | | /// <summary> |
| | | 93 | | /// Returns all standard fraud detection rules in recommended evaluation order. |
| | | 94 | | /// </summary> |
| | | 95 | | public static IReadOnlyList<Func<BehaviorSpace, RuleMatch?>> AllRules() => |
| | 1 | 96 | | [ |
| | 1 | 97 | | AccountTakeover(), |
| | 1 | 98 | | CredentialStuffing(), |
| | 1 | 99 | | PaymentFraud(), |
| | 1 | 100 | | AccountRecovery() |
| | 1 | 101 | | ]; |
| | | 102 | | } |