Impossible travel, rapid transaction sequences, and unusually large transfer amounts are established signals of account compromise or fraud. Without anomaly detection, an attacker who obtains a valid session token — through phishing, session fixation, or XSS — can execute transactions that would immediately appear suspicious to any human reviewer but proceed unchallenged by the application. CWE-307 (Improper Restriction of Excessive Authentication Attempts) and NIST SI-4 (System Monitoring) address active threat detection requirements. PCI-DSS Req 10.7 requires that anomalies trigger alerts. Suspicious activity step-up provides a dynamic second factor that activates precisely when risk is highest, without adding friction to normal user behavior.
Low because suspicious activity detection is a defense-in-depth layer — its absence does not enable initial access, but it removes the runtime signal that would detect and interrupt an active attack on an already-compromised session.
Add at least two anomaly detection rules in src/lib/fraudDetection.ts — impossible travel is the highest-signal, lowest-false-positive starting point:
export async function checkSuspiciousActivity(
userId: string,
currentLocation: { lat: number; lng: number }
): Promise<boolean> {
const lastSession = await db.sessions.findFirst({
where: { userId },
orderBy: { loginAt: 'desc' },
skip: 1 // skip current
});
if (!lastSession?.location) return false;
const elapsedMin =
(Date.now() - lastSession.loginAt.getTime()) / 60000;
const distanceKm = geoDistance(lastSession.location, currentLocation);
const speedKmMin = distanceKm / elapsedMin;
return speedKmMin > 15; // >15 km/min is physically impossible
}
When checkSuspiciousActivity returns true, call requireStepUp() before allowing any financial operation to proceed. Add a second rule (e.g., transactions above a threshold amount) to meet the two-rule minimum.
finserv-session-security.session-integrity.suspicious-activity-step-uplow"0 anomaly detection rules — impossible-travel logins not detected, no fraud monitoring"src/lib/fraudDetection.ts):
// lib/fraudDetection.ts
export async function checkSuspiciousActivity(userId: string, action: string) {
const recentSessions = await db.sessions.findMany({
where: { userId, expiresAt: { gt: new Date() } },
orderBy: { loginAt: 'desc' },
take: 1
});
const lastSession = recentSessions[0];
if (!lastSession) return false; // First login, not suspicious
const timeSinceLastLogin = (Date.now() - lastSession.loginAt.getTime()) / 1000 / 60; // minutes
const distance = geoDistance(lastSession.location, currentLocation); // km
const speedOfTravel = distance / timeSinceLastLogin; // km/min
if (speedOfTravel > 15) { // Impossible travel (>15 km/min)
return true; // Flag as suspicious
}
return false;
}