Detection rules › Panther
Panther rules: salesforce
Salesforce Admin Login As User
#Salesforce detection that alerts when an admin logs in as another user.
Detection logic
def rule(event):
return event.get("EVENT_TYPE", "<NO_EVENT_TYPE_FOUND>") == "LoginAs"
def title(event):
admin = event.get("DELEGATED_USER_NAME", "<NO_ADMIN_FOUND>")
user_id = event.get("USER_ID", "<NO_USER_ID_FOUND>")
return f"Salesforce admin [{admin}] logged in as a regular user with the user id [{user_id}]."
Rule specification
AnalysisType: rule
Description: "Salesforce detection that alerts when an admin logs in as another user. "
DisplayName: "Salesforce Admin Login As User"
Enabled: true
Filename: salesforce_admin_login_as_user.py
Runbook: "Please do an indicator search on USER_ID to find which user was assumed. "
Reference: https://help.salesforce.com/s/articleView?id=sf.logging_in_as_another_user.htm&type=5
Severity: Info
DedupPeriodMinutes: 60
LogTypes:
- Salesforce.LoginAs
RuleID: "Salesforce.Admin.Login.As.User"
Threshold: 1
Stages and Predicates
Fires on Salesforce.LoginAs events when the condition below holds.
Condition
EVENT_TYPEisLoginAs
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EVENT_TYPE | eq |
| field:"EVENT_TYPE" kind:eq value:"LoginAs" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field |
|---|
DELEGATED_USER_NAME |
USER_ID |
Response runbook
Please do an indicator search on USER_ID to find which user was assumed.
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"CLIENT_IP": "12.12.12.12",
"CPU_TIME": 19,
"DELEGATED_USER_ID": "0054x000001L78a",
"DELEGATED_USER_ID_DERIVED": "0054x000001L78aAAC",
"DELEGATED_USER_NAME": "admin.user@yourcompany.io",
"EVENT_TYPE": "LoginAs",
"LOGIN_KEY": "n5sqLw+tah0tY/q9",
"ORGANIZATION_ID": "000elibsdfkjsd",
"REQUEST_ID": "4dmdpWcNWWjoaQWObxO2k-",
"RUN_TIME": 1088,
"SESSION_KEY": "f6wkL1crc62p7/Vj",
"TIMESTAMP": "2021-08-19 09:05:03.392",
"TIMESTAMP_DERIVED": "2021-08-19 09:05:03.392",
"URI": "/secur/logout.jsp",
"URI_ID_DERIVED": "",
"USER_ID": "fdokawnjf",
"USER_ID_DERIVED": "fdokawnjf",
"p_any_ip_addresses": [
"12.12.12.12"
],
"p_any_trace_ids": [
"4dmdpWcNWWjoaQWObxO2k-"
],
"p_any_usernames": [
"admin.user@yourcompany.io"
],
"p_event_time": "2021-08-19 09:05:03.392",
"p_log_type": "Salesforce.LoginAs",
"p_parse_time": "2021-08-19 11:11:32.69",
"p_row_id": "1ac2cc960bb0ddf7dbeaeadb0ba701",
"p_source_id": "2e4c927c-7461-4810-86fe-45f6d5c5fe5b",
"p_source_label": "release-1-21"
}
Salesforce API Anomaly Detection (RET Passthrough)
#Salesforce Real-Time Event Monitoring has detected anomalous API activity. This could indicate compromised credentials, automated abuse, data exfiltration attempts, or other suspicious API usage patterns.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Credential Access | |
| Collection | |
| Exfiltration |
Detection logic
def rule(event):
# Alert on any Salesforce API Anomaly Event
# These are generated by Salesforce's Real-Time Event Monitoring
# when it detects anomalous API activity
return event.get("EVENT_TYPE") == "ApiAnomalyEventStore"
def title(event):
# Create descriptive title with user and anomaly details
user = event.get("USERNAME", event.get("USER_ID", "<UNKNOWN_USER>"))
summary = event.get("SUMMARY", "API Anomaly Detected")
score = event.get("SCORE", "<UNKNOWN>")
return f"Salesforce API Anomaly: {summary} (Score: {score}) - User: {user}"
def severity(event):
# Map anomaly score to Panther severity
# Salesforce scores range from 0-100, higher = more anomalous
score = event.get("SCORE", 0)
# Ensure score is numeric
score = score if isinstance(score, (int, float)) else 0
if score >= 80:
return "CRITICAL"
if score >= 60:
return "HIGH"
if score >= 40:
return "MEDIUM"
if score >= 20:
return "LOW"
return "DEFAULT"
def dedup(event):
# Deduplicate by unique event identifier
# Use a combination of user, session, and timestamp to avoid duplicate alerts
user_id = event.get("USER_ID", "unknown")
session_key = event.get("SESSION_KEY", "unknown")
timestamp = event.get("TIMESTAMP", "unknown")
return f"SF_API_ANOMALY_{user_id}_{session_key}_{timestamp}"
def alert_context(event):
# Provide key context for investigation
return {
"User ID": event.get("USER_ID"),
"Username": event.get("USERNAME"),
"Session Key": event.get("SESSION_KEY"),
"Source IP": event.get("SOURCE_IP"),
"User Type": event.get("USER_TYPE"),
"Anomaly Score": event.get("SCORE"),
"Summary": event.get("SUMMARY"),
"Request ID": event.get("REQUEST_ID"),
"Organization ID": event.get("ORGANIZATION_ID"),
"Event Date": event.get("EVENT_DATE"),
}
Rule specification
AnalysisType: rule
Description: "Salesforce Real-Time Event Monitoring has detected anomalous API activity. This could indicate compromised credentials, automated abuse, data exfiltration attempts, or other suspicious API usage patterns."
DisplayName: "Salesforce API Anomaly Detection (RET Passthrough)"
Enabled: true
Filename: salesforce_api_anomaly_passthrough.py
Runbook: |
1. Review the anomaly score and summary to understand what triggered the alert
2. Investigate the user's recent API activity in Salesforce EventLogFile
3. Check if the source IP is expected for this user
4. Review session details and authentication method
5. Look for other anomalous activity from the same user or session
6. If confirmed malicious, reset user credentials and review data access
7. Consider enabling additional API monitoring or rate limiting
Reference: https://developer.salesforce.com/docs/atlas.en-us.platform_events.meta/platform_events/sforce_api_objects_apianomalyevent.htm
Severity: Medium
DedupPeriodMinutes: 60
LogTypes:
- Salesforce.RealtimeEvent
RuleID: "Salesforce.API.Anomaly.Passthrough"
Threshold: 1
Tags:
- Salesforce
- API Security
- Real-Time Event Monitoring
- Anomaly Detection
Reports:
MITRE ATT&CK:
- TA0001:T1078 # Initial Access: Valid Accounts
- TA0006:T1110 # Credential Access: Brute Force
- TA0009:T1530 # Collection: Data from Cloud Storage Object
- TA0010:T1567 # Exfiltration: Exfiltration Over Web Service
Stages and Predicates
Fires on Salesforce.RealtimeEvent events when the condition below holds.
Condition
EVENT_TYPEisApiAnomalyEventStore
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EVENT_TYPE | eq |
| field:"EVENT_TYPE" kind:eq value:"ApiAnomalyEventStore" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
User ID | USER_ID |
Username | USERNAME |
Session Key | SESSION_KEY |
Source IP | SOURCE_IP |
User Type | USER_TYPE |
Anomaly Score | SCORE |
Summary | SUMMARY |
Request ID | REQUEST_ID |
Organization ID | ORGANIZATION_ID |
Event Date | EVENT_DATE |
Response runbook
1. Review the anomaly score and summary to understand what triggered the alert
2. Investigate the user's recent API activity in Salesforce EventLogFile
3. Check if the source IP is expected for this user
4. Review session details and authentication method
5. Look for other anomalous activity from the same user or session
6. If confirmed malicious, reset user credentials and review data access
7. Consider enabling additional API monitoring or rate limiting
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"EVENT_DATE": "2024-01-15",
"EVENT_TYPE": "ApiAnomalyEventStore",
"ORGANIZATION_ID": "00D5f000005uVo7",
"REQUEST_ID": "5tlEQPuEcPPzVPH-nPNWK-",
"SCORE": 85.5,
"SECURITY_EVENT_DATA": "{\"anomaly_type\":\"volume\",\"threshold_exceeded\":true,\"queries_per_hour\":1250}",
"SESSION_KEY": "fJ8kL2drc73p8/Wk",
"SOURCE_IP": "45.67.89.123",
"SUMMARY": "Unusual API query volume and pattern detected",
"TIMESTAMP": "2024-01-15 14:23:45.123",
"TIMESTAMP_DERIVED": "2024-01-15 14:23:45.123",
"USERNAME": "suspicious.user@company.com",
"USER_ID": "0055f00000CyENt",
"USER_ID_DERIVED": "0055f00000CyENtAAN",
"USER_TYPE": "Standard",
"p_any_actor_ids": [
"0055f00000CyENt"
],
"p_any_ip_addresses": [
"45.67.89.123"
],
"p_any_trace_ids": [
"5tlEQPuEcPPzVPH-nPNWK-"
],
"p_any_usernames": [
"suspicious.user@company.com"
],
"p_event_time": "2024-01-15 14:23:45.123",
"p_log_type": "Salesforce.ApiAnomalyEventStore",
"p_parse_time": "2024-01-15 14:24:12.456",
"p_row_id": "a8f3d45e7bc9f2e1a3d6f8b4c7e9a1b2",
"p_source_id": "f7e3c18d-837b-461f-9c2e-7f2g4ffa2c17",
"p_source_label": "Salesforce - Production"
}
Salesforce Bulk API Data Exfiltration
#Detects Salesforce Bulk API operations that could indicate data exfiltration attempts. The Bulk API allows users to process large volumes of records (up to millions) asynchronously, making it a common vector for data theft. This detection triggers on all Bulk API job completions and adjusts severity based on: - Operation type (query operations are highest risk for exfiltration) - Volume of records processed - Entity/object type being accessed
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Collection | |
| Exfiltration |
Detection logic
def rule(event):
# Alert on Salesforce Bulk API Result Events
# These events are generated when bulk API jobs complete
# and can indicate large-scale data exfiltration attempts
return event.get("EVENT_TYPE") == "BulkApiResultEventStore"
def title(event):
# Create descriptive title with operation and volume details
user = event.get("USER_NAME", event.get("USER_ID", "<UNKNOWN_USER>"))
operation = event.get("OPERATION_TYPE", "<UNKNOWN_OPERATION>")
records = event.get("RECORDS_PROCESSED", 0)
entity = event.get("ENTITY_NAME", "<UNKNOWN_ENTITY>")
return f"Salesforce Bulk API: {operation} on {entity} ({records:,} records) - User: {user}"
def severity(event):
# Map based on operation type and volume
operation = event.get("OPERATION_TYPE", "")
records = event.get("RECORDS_PROCESSED", 0)
# Ensure records is numeric
records = records if isinstance(records, (int, float)) else 0
# Query operations are most concerning for data exfiltration
if operation in ["query", "queryAll"]:
if records >= 100000:
severity_level = "CRITICAL"
elif records >= 50000:
severity_level = "HIGH"
elif records >= 10000:
severity_level = "MEDIUM"
else:
severity_level = "DEFAULT"
# Other operations (insert, update, delete) are also notable
elif operation in ["delete", "hardDelete"]:
if records >= 10000:
severity_level = "HIGH"
elif records >= 1000:
severity_level = "MEDIUM"
else:
severity_level = "DEFAULT"
# Default for other operations
else:
if records >= 50000:
severity_level = "HIGH"
elif records >= 10000:
severity_level = "MEDIUM"
else:
severity_level = "DEFAULT"
return severity_level
def dedup(event):
# Deduplicate by job ID to avoid duplicate alerts for the same job
job_id = event.get("JOB_ID", "unknown")
return f"SF_BULK_API_{job_id}"
def alert_context(event):
# Provide comprehensive context for investigation
return {
"Job ID": event.get("JOB_ID"),
"User ID": event.get("USER_ID"),
"Username": event.get("USER_NAME"),
"Operation Type": event.get("OPERATION_TYPE"),
"Entity Name": event.get("ENTITY_NAME"),
"Records Processed": event.get("RECORDS_PROCESSED"),
"Number of Batches": event.get("NUMBER_OF_BATCHES"),
"API Version": event.get("API_VERSION"),
"Source IP": event.get("SOURCE_IP"),
"Request ID": event.get("REQUEST_ID"),
"Organization ID": event.get("ORGANIZATION_ID"),
"Job Type": event.get("JOB_TYPE"),
}
Rule specification
AnalysisType: rule
Description: |
Detects Salesforce Bulk API operations that could indicate data exfiltration attempts. The Bulk API allows users to process large volumes of records (up to millions) asynchronously, making it a common vector for data theft.
This detection triggers on all Bulk API job completions and adjusts severity based on:
- Operation type (query operations are highest risk for exfiltration)
- Volume of records processed
- Entity/object type being accessed
DisplayName: "Salesforce Bulk API Data Exfiltration"
Enabled: true
Filename: salesforce_bulk_data_exfiltration.py
Runbook: |
1. Review the operation type and number of records processed
2. Identify if this is expected behavior for the user (data analyst, integration user, etc.)
3. Check the user's recent activity for other suspicious patterns
4. Verify the source IP is expected for this user
5. Review what entity/object was accessed (sensitive data like Contacts, Accounts, etc.)
6. Check if data was exported or if this was an internal operation
7. If confirmed malicious:
- Immediately disable the user's API access
- Reset credentials
- Review all data accessed in the session
- Check for data exfiltration to external systems
8. Consider implementing IP restrictions or rate limits for bulk API access
Reference: https://developer.salesforce.com/docs/atlas.en-us.api_asynch.meta/api_asynch/
Severity: Medium
DedupPeriodMinutes: 60
LogTypes:
- Salesforce.RealtimeEvent
RuleID: "Salesforce.BulkAPI.DataExfiltration"
Threshold: 1
Tags:
- Salesforce
- Data Exfiltration
- Bulk API
- Insider Threat
Reports:
MITRE ATT&CK:
- TA0010:T1567 # Exfiltration: Exfiltration Over Web Service
- TA0009:T1530 # Collection: Data from Cloud Storage Object
- TA0010:T1020 # Exfiltration: Automated Exfiltration
Stages and Predicates
Fires on Salesforce.RealtimeEvent events when the condition below holds.
Condition
EVENT_TYPEisBulkApiResultEventStore
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EVENT_TYPE | eq |
| field:"EVENT_TYPE" kind:eq value:"BulkApiResultEventStore" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
Job ID | JOB_ID |
User ID | USER_ID |
Username | USER_NAME |
Operation Type | OPERATION_TYPE |
Entity Name | ENTITY_NAME |
Records Processed | RECORDS_PROCESSED |
Number of Batches | NUMBER_OF_BATCHES |
API Version | API_VERSION |
Source IP | SOURCE_IP |
Request ID | REQUEST_ID |
Organization ID | ORGANIZATION_ID |
Job Type | JOB_TYPE |
Response runbook
1. Review the operation type and number of records processed
2. Identify if this is expected behavior for the user (data analyst, integration user, etc.)
3. Check the user's recent activity for other suspicious patterns
4. Verify the source IP is expected for this user
5. Review what entity/object was accessed (sensitive data like Contacts, Accounts, etc.)
6. Check if data was exported or if this was an internal operation
7. If confirmed malicious:
- Immediately disable the user's API access
- Reset credentials
- Review all data accessed in the session
- Check for data exfiltration to external systems
8. Consider implementing IP restrictions or rate limits for bulk API access
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"API_VERSION": "59.0",
"CONCURRENCY_MODE": "Parallel",
"ENTITY_NAME": "Contact",
"EVENT_DATE": "2024-01-20",
"EVENT_TYPE": "BulkApiResultEventStore",
"JOB_ID": "7505f000008xKLmAAM",
"JOB_TYPE": "V2",
"NUMBER_OF_BATCHES": 15,
"OPERATION_TYPE": "query",
"ORGANIZATION_ID": "00D5f000005uVo7",
"RECORDS_PROCESSED": 150000,
"REQUEST_ID": "9xpIUTyIfTT3ZTL-rTRaN-",
"SOURCE_IP": "203.45.67.89",
"TIMESTAMP": "2024-01-20 15:45:30.123",
"TIMESTAMP_DERIVED": "2024-01-20 15:45:30.123",
"USER_ID": "0055f00000HyJSw",
"USER_ID_DERIVED": "0055f00000HyJSwXXY",
"USER_NAME": "suspicious.user@company.com",
"USER_TYPE": "Standard",
"p_any_actor_ids": [
"0055f00000HyJSw"
],
"p_any_ip_addresses": [
"203.45.67.89"
],
"p_any_trace_ids": [
"9xpIUTyIfTT3ZTL-rTRaN-"
],
"p_any_usernames": [
"suspicious.user@company.com"
],
"p_event_time": "2024-01-20 15:45:30.123",
"p_log_type": "Salesforce.BulkApiResultEventStore",
"p_parse_time": "2024-01-20 15:46:12.456",
"p_row_id": "e2j7h89i1fg3j6i5e7h0j2f8g1i3e5f6",
"p_source_id": "f7e3c18d-837b-461f-9c2e-7f2g4ffa2c17",
"p_source_label": "Salesforce - Production"
}
Salesforce OAuth Credential Abuse Detection
#Detects OAuth credential abuse and suspicious token usage patterns in Salesforce. OAuth tokens provide API access and can be abused if compromised, making this detection critical for: - Stolen or leaked OAuth tokens - Token replay attacks - Excessive API usage indicating automated abuse - Failed token refresh attempts (potential brute force) - Unauthorized token revocations This detection triggers on OAuth-related security events and adjusts severity based on: - Token revocation events (may indicate compromise response) - Failed OAuth operations (potential attack attempts) - Excessive API usage patterns
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Credential Access | |
| Lateral Movement | |
| Exfiltration |
Detection logic
def rule(event):
# Alert on OAuth-related events that may indicate credential abuse
event_type = event.get("EVENT_TYPE", "")
# Monitor OAuth token usage and authentication events
oauth_events = [
"OAuthTokenRevoked",
"OAuthTokenRefreshFailed",
"ApiTotalUsage",
"ApiConnectedApp",
]
return event_type in oauth_events or "oauth" in str(event_type).lower()
def title(event):
# Create descriptive title based on event type
event_type = event.get("EVENT_TYPE", "<UNKNOWN_EVENT>")
user = event.get("USER_NAME", event.get("USER_ID", "<UNKNOWN_USER>"))
app_name = event.get("CONNECTED_APP_NAME", event.get("CLIENT_NAME", "<UNKNOWN_APP>"))
# Special handling for different event types
if "Revoked" in event_type:
return f"Salesforce OAuth Token Revoked: {app_name} - User: {user}"
if "Failed" in event_type:
return f"Salesforce OAuth Token Refresh Failed: {app_name} - User: {user}"
return f"Salesforce OAuth Activity: {event_type} - {app_name} - User: {user}"
def severity(event):
# Map based on event type and context
event_type = event.get("EVENT_TYPE", "")
status = str(event.get("STATUS", "")).lower()
# Token revocation may indicate compromise
if "Revoked" in event_type:
return "HIGH"
# Failed token operations are suspicious
if "Failed" in event_type or "fail" in status:
return "MEDIUM"
# Excessive API usage may indicate abuse
api_calls = event.get("API_TOTAL_COUNT", 0)
# Ensure api_calls is numeric
api_calls = api_calls if isinstance(api_calls, (int, float)) else 0
if api_calls > 10000:
return "HIGH"
if api_calls > 5000:
return "MEDIUM"
return "DEFAULT"
def dedup(event):
# Deduplicate by event type, user, and app
event_type = event.get("EVENT_TYPE", "unknown")
user_id = event.get("USER_ID", "unknown")
app_id = event.get("CONNECTED_APP_ID", event.get("CLIENT_ID", "unknown"))
return f"SF_OAUTH_ABUSE_{event_type}_{user_id}_{app_id}"
def alert_context(event):
# Provide comprehensive context for investigation
return {
"Event Type": event.get("EVENT_TYPE"),
"User ID": event.get("USER_ID"),
"Username": event.get("USER_NAME"),
"Connected App ID": event.get("CONNECTED_APP_ID"),
"Connected App Name": event.get("CONNECTED_APP_NAME"),
"Client ID": event.get("CLIENT_ID"),
"Client Name": event.get("CLIENT_NAME"),
"Source IP": event.get("SOURCE_IP"),
"Status": event.get("STATUS"),
"API Total Count": event.get("API_TOTAL_COUNT"),
"Request ID": event.get("REQUEST_ID"),
"Organization ID": event.get("ORGANIZATION_ID"),
"Session Key": event.get("SESSION_KEY"),
}
Rule specification
AnalysisType: rule
Description: |
Detects OAuth credential abuse and suspicious token usage patterns in Salesforce. OAuth tokens provide API access and can be abused if compromised, making this detection critical for:
- Stolen or leaked OAuth tokens
- Token replay attacks
- Excessive API usage indicating automated abuse
- Failed token refresh attempts (potential brute force)
- Unauthorized token revocations
This detection triggers on OAuth-related security events and adjusts severity based on:
- Token revocation events (may indicate compromise response)
- Failed OAuth operations (potential attack attempts)
- Excessive API usage patterns
DisplayName: "Salesforce OAuth Credential Abuse Detection"
Enabled: true
Filename: salesforce_oauth_credential_abuse.py
Runbook: |
1. Identify the event type and severity of the OAuth activity
2. Determine if this is a revocation (possible compromise) or failure (attack attempt)
3. Review the user and connected app involved
4. Check for multiple failed attempts from the same source
5. Investigate the source IP and geolocation
6. Review API usage patterns for anomalies
7. If token compromise suspected:
- Immediately revoke all active OAuth tokens for the affected app/user
- Reset user credentials
- Review all API calls made using the compromised token
- Check for data exfiltration or unauthorized changes
- Contact the connected app owner if third-party
8. Consider implementing OAuth token rotation policies and IP restrictions
Reference: https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_oauthtoken.htm
Severity: Medium
DedupPeriodMinutes: 60
LogTypes:
- Salesforce.RealtimeEvent
RuleID: "Salesforce.OAuth.Credential.Abuse"
Threshold: 1
Tags:
- Salesforce
- OAuth
- Credential Abuse
- Token Theft
- API Abuse
Reports:
MITRE ATT&CK:
- TA0006:T1528 # Credential Access: Steal Application Access Token
- TA0006:T1110 # Credential Access: Brute Force
- TA0005:T1550 # Defense Evasion: Use Alternate Authentication Material
- TA0010:T1020 # Exfiltration: Automated Exfiltration
Stages and Predicates
Fires on Salesforce.RealtimeEvent events when any of the conditions below holds.
Condition
any of:
EVENT_TYPEis one ofOAuthTokenRevoked,OAuthTokenRefreshFailed,ApiTotalUsage,ApiConnectedAppEVENT_TYPEcontainsoauth
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EVENT_TYPE | contains |
| field:"EVENT_TYPE" kind:contains value:"oauth" |
EVENT_TYPE | in |
| field:"EVENT_TYPE" kind:in |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
Event Type | EVENT_TYPE |
User ID | USER_ID |
Username | USER_NAME |
Connected App ID | CONNECTED_APP_ID |
Connected App Name | CONNECTED_APP_NAME |
Client ID | CLIENT_ID |
Client Name | CLIENT_NAME |
Source IP | SOURCE_IP |
Status | STATUS |
API Total Count | API_TOTAL_COUNT |
Request ID | REQUEST_ID |
Organization ID | ORGANIZATION_ID |
Session Key | SESSION_KEY |
Response runbook
1. Identify the event type and severity of the OAuth activity
2. Determine if this is a revocation (possible compromise) or failure (attack attempt)
3. Review the user and connected app involved
4. Check for multiple failed attempts from the same source
5. Investigate the source IP and geolocation
6. Review API usage patterns for anomalies
7. If token compromise suspected:
- Immediately revoke all active OAuth tokens for the affected app/user
- Reset user credentials
- Review all API calls made using the compromised token
- Check for data exfiltration or unauthorized changes
- Contact the connected app owner if third-party
8. Consider implementing OAuth token rotation policies and IP restrictions
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"CLIENT_ID": "3MVG9yZ.WNe6byQCPj8xYzKlMqR",
"CONNECTED_APP_ID": "0H05f000000XzDqEEN",
"CONNECTED_APP_NAME": "CompromisedApp",
"EVENT_DATE": "2024-01-30",
"EVENT_TYPE": "OAuthTokenRevoked",
"ORGANIZATION_ID": "00D5f000005uVo7",
"REQUEST_ID": "9HzSED8SpDD3JDW-CCZkX-",
"SESSION_KEY": "kN3pP6huf17t3/Ao",
"SOURCE_IP": "10.10.10.10",
"STATUS": "Success",
"TIMESTAMP": "2024-01-30 17:45:30.123",
"TIMESTAMP_DERIVED": "2024-01-30 17:45:30.123",
"USER_ID": "0055f00000RzTCE",
"USER_ID_DERIVED": "0055f00000RzTCEIII",
"USER_NAME": "security.admin@company.com",
"USER_TYPE": "Standard",
"p_any_actor_ids": [
"0055f00000RzTCE"
],
"p_any_ip_addresses": [
"10.10.10.10"
],
"p_any_trace_ids": [
"9HzSED8SpDD3JDW-CCZkX-"
],
"p_any_usernames": [
"security.admin@company.com"
],
"p_event_time": "2024-01-30 17:45:30.123",
"p_log_type": "Salesforce.OAuthTokenRevoked",
"p_parse_time": "2024-01-30 17:46:15.456",
"p_row_id": "o2t7r89s1pq3t5s4o7r0t2p6s9r1o3p5",
"p_source_id": "f7e3c18d-837b-461f-9c2e-7f2g4ffa2c17",
"p_source_label": "Salesforce - Production"
}
Salesforce Third-Party Integration Monitoring
#Monitors third-party integrations and OAuth connected apps accessing Salesforce. Connected apps use OAuth for authorization and can access data on behalf of users, making them a potential vector for: - Unauthorized data access - Shadow IT applications - Compromised OAuth tokens - Over-privileged integrations This detection triggers on connected app usage events and adjusts severity based on: - Connection type (refresh tokens are higher risk) - App authorization events - Suspicious app naming patterns
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Persistence | |
| Credential Access | |
| Lateral Movement |
Detection logic
def rule(event):
# Alert on Connected App usage events
# These track OAuth authorizations and third-party integrations
return event.get("EVENT_TYPE") in [
"ConnectedAppUsageEventStore",
"ApiConnectedApp",
]
def title(event):
# Create descriptive title with app and user details
app_id = event.get("CONNECTED_APP_ID", "<UNKNOWN_APP>")
app_name = event.get("CONNECTED_APP_NAME", app_id)
user = event.get("USER_NAME", event.get("USER_ID", "<UNKNOWN_USER>"))
connection_type = event.get("CONNECTION_TYPE", "<UNKNOWN_TYPE>")
return f"Salesforce Connected App Access: {app_name} via {connection_type} - User: {user}"
def severity(event):
# Map based on connection type and context
connection_type = str(event.get("CONNECTION_TYPE", "")).lower()
app_name = str(event.get("CONNECTED_APP_NAME", "")).lower()
# OAuth refresh token grants are sensitive (persistent access)
if "refresh" in connection_type:
return "HIGH"
# New app authorizations are notable
if "authorization" in connection_type:
return "MEDIUM"
# Unknown or suspicious app names
# Check for common test/development naming patterns that may indicate
# non-production or unapproved apps
suspicious_keywords = ["test", "dev", "temp", "demo", "unknown", "sandbox", "trial"]
# Only flag if the app name starts with or exactly matches these keywords
# to reduce false positives from legitimate apps containing these terms
if any(app_name.startswith(keyword) or app_name == keyword for keyword in suspicious_keywords):
return "MEDIUM"
return "DEFAULT"
def dedup(event):
# Deduplicate by app, user, and connection type
app_id = event.get("CONNECTED_APP_ID", "unknown")
user_id = event.get("USER_ID", "unknown")
connection = event.get("CONNECTION_TYPE", "unknown")
return f"SF_CONNECTED_APP_{app_id}_{user_id}_{connection}"
def alert_context(event):
# Provide comprehensive context for investigation
return {
"Connected App ID": event.get("CONNECTED_APP_ID"),
"Connected App Name": event.get("CONNECTED_APP_NAME"),
"Connection Type": event.get("CONNECTION_TYPE"),
"User ID": event.get("USER_ID"),
"Username": event.get("USER_NAME"),
"Source IP": event.get("SOURCE_IP"),
"User Type": event.get("USER_TYPE"),
"Request ID": event.get("REQUEST_ID"),
"Organization ID": event.get("ORGANIZATION_ID"),
"API Version": event.get("API_VERSION"),
"OAuth Scopes": event.get("OAUTH_SCOPES"),
}
Rule specification
AnalysisType: rule
Description: |
Monitors third-party integrations and OAuth connected apps accessing Salesforce. Connected apps use OAuth for authorization and can access data on behalf of users, making them a potential vector for:
- Unauthorized data access
- Shadow IT applications
- Compromised OAuth tokens
- Over-privileged integrations
This detection triggers on connected app usage events and adjusts severity based on:
- Connection type (refresh tokens are higher risk)
- App authorization events
- Suspicious app naming patterns
DisplayName: "Salesforce Third-Party Integration Monitoring"
Enabled: true
Filename: salesforce_third_party_integration.py
Runbook: |
1. Identify the connected app and review its purpose and authorization
2. Verify the app is approved and expected for this user
3. Review OAuth scopes granted to the app (what permissions it has)
4. Check if the source IP is expected for this integration
5. Investigate the user's intent for authorizing this app
6. Review the app's access history and data accessed
7. If unauthorized or suspicious:
- Revoke the OAuth token immediately
- Review all data accessed by the app
- Disable the connected app if not needed
- Educate the user on approved applications
8. Consider implementing connected app policies and IP restrictions
Reference: https://help.salesforce.com/s/articleView?id=sf.connected_app_overview.htm
Severity: Medium
DedupPeriodMinutes: 60
LogTypes:
- Salesforce.RealtimeEvent
RuleID: "Salesforce.ThirdParty.Integration.Monitoring"
Threshold: 1
Tags:
- Salesforce
- OAuth
- Connected Apps
- Third-Party Integration
- Shadow IT
Reports:
MITRE ATT&CK:
- TA0001:T1199 # Initial Access: Trusted Relationship
- TA0003:T1098 # Persistence: Account Manipulation
- TA0005:T1550 # Defense Evasion: Use Alternate Authentication Material
- TA0006:T1528 # Credential Access: Steal Application Access Token
Stages and Predicates
Fires on Salesforce.RealtimeEvent events when the condition below holds.
Condition
EVENT_TYPEis one ofConnectedAppUsageEventStore,ApiConnectedApp
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EVENT_TYPE | in |
| field:"EVENT_TYPE" kind:in |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
Connected App ID | CONNECTED_APP_ID |
Connected App Name | CONNECTED_APP_NAME |
Connection Type | CONNECTION_TYPE |
User ID | USER_ID |
Username | USER_NAME |
Source IP | SOURCE_IP |
User Type | USER_TYPE |
Request ID | REQUEST_ID |
Organization ID | ORGANIZATION_ID |
API Version | API_VERSION |
OAuth Scopes | OAUTH_SCOPES |
Response runbook
1. Identify the connected app and review its purpose and authorization
2. Verify the app is approved and expected for this user
3. Review OAuth scopes granted to the app (what permissions it has)
4. Check if the source IP is expected for this integration
5. Investigate the user's intent for authorizing this app
6. Review the app's access history and data accessed
7. If unauthorized or suspicious:
- Revoke the OAuth token immediately
- Review all data accessed by the app
- Disable the connected app if not needed
- Educate the user on approved applications
8. Consider implementing connected app policies and IP restrictions
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"API_VERSION": "59.0",
"CONNECTED_APP_ID": "0H05f000000XyZmCAK",
"CONNECTED_APP_NAME": "ThirdPartyDataSync",
"CONNECTION_TYPE": "oauth_refresh_token",
"EVENT_DATE": "2024-01-25",
"EVENT_TYPE": "ConnectedAppUsageEventStore",
"OAUTH_SCOPES": "api refresh_token",
"ORGANIZATION_ID": "00D5f000005uVo7",
"REQUEST_ID": "4CuNZY3NkYY8EYQ-xXWfS-",
"SOURCE_IP": "203.45.67.89",
"TIMESTAMP": "2024-01-25 16:30:45.123",
"TIMESTAMP_DERIVED": "2024-01-25 16:30:45.123",
"USER_ID": "0055f00000MyOXz",
"USER_ID_DERIVED": "0055f00000MyOXzCCD",
"USER_NAME": "oauth.user@company.com",
"USER_TYPE": "Standard",
"p_any_actor_ids": [
"0055f00000MyOXz"
],
"p_any_ip_addresses": [
"203.45.67.89"
],
"p_any_trace_ids": [
"4CuNZY3NkYY8EYQ-xXWfS-"
],
"p_any_usernames": [
"oauth.user@company.com"
],
"p_event_time": "2024-01-25 16:30:45.123",
"p_log_type": "Salesforce.ConnectedAppUsageEventStore",
"p_parse_time": "2024-01-25 16:31:22.456",
"p_row_id": "j7o2m34n6kl8o0n9j2m5o7k1n4m6j8k0",
"p_source_id": "f7e3c18d-837b-461f-9c2e-7f2g4ffa2c17",
"p_source_label": "Salesforce - Production"
}