Anthropic Impossible Travel Login

Last updated 4 days ago on 2026-09-21
Created 10 days ago on 2026-09-15

About

Detects successful Anthropic magic link or SSO sign-ins for the same user email from source IP addresses whose query-time geo-locations (via IP_LOCATION) are separated by at least 1,000 km, with implied travel faster than 800 km/h, within a 24-hour window. That pattern can indicate account sharing, VPN or proxy egress mismatches, or an adversary authenticating from a geography far from the legitimate user's baseline.
Tags
Domain: GenAIDomain: IdentityPlatform: AnthropicData Source: Anthropic Audit LogsUse Case: Identity and Access AuditUse Case: Threat DetectionRule Type: ES|QLTactic: Initial AccessLanguage: esql
Severity
high
Risk Score
73
MITRE ATT&CK™

Initial Access (TA0001)(external, opens in a new tab or window)

False Positive Examples
Users on VPN or proxy egress that geo-resolves through a region distant from the user's physical location. Mobile clients on cellular networks that peer through regional hubs may geo-resolve differently than the user's location.
License
Elastic License v2(external, opens in a new tab or window)

Definition

Integration Pack
Prebuilt Security Detection Rules
Related Integrations

anthropic(external, opens in a new tab or window)

Query
text code block:
from logs-anthropic.audit-* | where data_stream.dataset == "anthropic.audit" and mv_contains(event.category, "authentication") and event.outcome == "success" and event.action in ("magic_link_login_succeeded", "sso_login_succeeded") and user.email is not null and source.ip is not null | IP_LOCATION geo = source.ip with { "properties": ["country_name", "city_name", "location"] } | eval Esql.source_geo_lat = st_y(geo.location), Esql.source_geo_lon = st_x(geo.location) | where Esql.source_geo_lat is not null and Esql.source_geo_lon is not null | stats Esql.first_lat = first(Esql.source_geo_lat, @timestamp), Esql.first_lon = first(Esql.source_geo_lon, @timestamp), Esql.last_lat = last(Esql.source_geo_lat, @timestamp), Esql.last_lon = last(Esql.source_geo_lon, @timestamp), Esql.event_count = count(*), Esql.event_id_values = values(event.id), Esql.event_action_values = values(event.action), Esql.source_ip_values = values(source.ip), Esql.source_geo_country_name_values = values(geo.country_name), Esql.source_geo_city_name_values = values(geo.city_name), Esql.user_agent_original_values = values(user_agent.original), Esql.anthropic_audit_actor_type_values = values(anthropic.audit.actor.type), Esql.timestamp_first_seen = min(@timestamp), Esql.timestamp_last_seen = max(@timestamp) by user.email | where Esql.event_count >= 2 | eval Esql.p1 = to_geopoint(concat("POINT(", to_string(Esql.first_lon), " ", to_string(Esql.first_lat), ")")), Esql.p2 = to_geopoint(concat("POINT(", to_string(Esql.last_lon), " ", to_string(Esql.last_lat), ")")) | eval Esql.distance_km = round(st_distance(Esql.p1, Esql.p2) / 1000.0, 0), Esql.window_minutes = date_diff("minute", Esql.timestamp_first_seen, Esql.timestamp_last_seen), Esql.travel_kmh = case(Esql.window_minutes > 0, round(Esql.distance_km * 60.0 / Esql.window_minutes, 0), null) | where Esql.distance_km >= 1000 and Esql.travel_kmh >= 800 | keep user.email, Esql.*

Install detection rules in Elastic Security

Detect Anthropic Impossible Travel Login in the Elastic Security detection engine by installing this rule into your Elastic Stack.

To setup this rule, check out the installation guide for Prebuilt Security Detection Rules(external, opens in a new tab or window).