User Monitoring and Protection
Overview
Instrument your services and track user activity to detect and block bad actors.
Add authenticated user information on traces to identify and block bad actors targeting your authenticated attack surface. To do this, set the user ID tag on the running APM trace, providing the necessary instrumentation for ASM to block authenticated attackers. This allows ASM to associate attacks and business logic events to users.
Track user logins and activity to detect account takeovers and business logic abuse with out-of-the-box detection rules, and to ultimately block attackers.
The custom user activity for which out-of-the-box detection rules are available are as follow:
You can add custom tags to your root span, or use the instrumentation functions described below.
Use the Java tracer’s API for adding custom tags to a root span and add user information so that you can monitor authenticated requests in the application.
User monitoring tags are applied on the root span and start with the prefix usr
followed by the name of the field. For example, usr.name
is a user monitoring tag that tracks the user’s name.
Note: Check that you have added necessary dependencies to your application.
The example below shows how to obtain the root span, add the relevant user monitoring tags, and enable user blocking capability:
import io.opentracing.Span;
import io.opentracing.util.GlobalTracer;
import datadog.appsec.api.blocking.Blocking;
import datadog.trace.api.interceptor.MutableSpan;
// Get the active span
final Span span = GlobalTracer.get().activeSpan();
if ((span instanceof MutableSpan)) {
MutableSpan localRootSpan = ((MutableSpan) span).getLocalRootSpan();
// Setting the mandatory user id tag
localRootSpan.setTag("usr.id", "d131dd02c56eec4");
// Setting optional user monitoring tags
localRootSpan.setTag("usr.name", "Jean Example");
localRootSpan.setTag("usr.email", "jean.example@example.com");
localRootSpan.setTag("usr.session_id", "987654321");
localRootSpan.setTag("usr.role", "admin");
localRootSpan.setTag("usr.scope", "read:message, write:files");
}
Blocking
.forUser("d131dd02c56eec4")
.blockIfMatch();
The .NET tracer package provides the SetUser()
function, which allows you to monitor authenticated requests by adding user information to the trace.
The example below shows how to add the relevant user monitoring tags and enable user blocking capability:
using Datadog.Trace;
// ...
var userDetails = new UserDetails()
{
// the systems internal identifier for the users
Id = "d41452f2-483d-4082-8728-171a3570e930",
// the email address of the user
Email = "test@adventure-works.com",
// the user's name, as displayed by the system
Name = "Jane Doh",
// the user's session id
SessionId = "d0632156-132b-4baa-95b2-a492c5f9cb16",
// the role the user is making the request under
Role = "standard",
};
Tracer.Instance.ActiveScope?.Span.SetUser(userDetails);
For information and options, read the .NET tracer documentation.
The Go tracer package provides the SetUser()
function, which allows you to monitor authenticated requests by adding user information to the trace. For more options, see the Go tracer documentation.
This example shows how to retrieve the current tracer span, use it to set user monitoring tags, and enable user blocking capability:
import "gopkg.in/DataDog/dd-trace-go.v1/appsec"
func handler(w http.ResponseWriter, r *http.Request) {
if appsec.SetUser(r.Context(), "my-uid") != nil {
// The user must be blocked by aborting the request handler asap.
// The blocking response is automatically handled and sent by the appsec middleware.
return
}
}
Use one of the following APIs to add user information to a trace so that you can monitor authenticated requests in the application:
Starting with ddtrace
1.1.0, the Datadog::Kit::Identity.set_user
method is available. This is the recommended API for adding user information to traces:
# Get the active trace
trace = Datadog::Tracing.active_trace
# Set mandatory user id tag
Datadog::Kit::Identity.set_user(trace, id: 'd131dd02c56eeec4')
# Or set any of these optional user monitoring tags
Datadog::Kit::Identity.set_user(
trace,
# mandatory id
id: 'd131dd02c56eeec4',
# optional tags with known semantics
name: 'Jean Example',
email:, 'jean.example@example.com',
session_id:, '987654321',
role: 'admin',
scope: 'read:message, write:files',
# optional free-form tags
another_tag: 'another_value',
)
If Datadog::Kit::Identity.set_user
does not meet your needs, you can use set_tag
instead.
User monitoring tags are applied on the trace and start with the prefix usr.
followed by the name of the field. For example, usr.name
is a user monitoring tag that tracks the user’s name.
The example below shows how to obtain the active trace and add relevant user monitoring tags:
Notes:
- Tag values must be strings.
- The
usr.id
tag is mandatory.
# Get the active trace
trace = Datadog::Tracing.active_trace
# Set mandatory user id tag
trace.set_tag('usr.id', 'd131dd02c56eeec4')
# Set optional user monitoring tags with known sematics
trace.set_tag('usr.name', 'Jean Example')
trace.set_tag('usr.email', 'jean.example@example.com')
trace.set_tag('usr.session_id', '987654321')
trace.set_tag('usr.role', 'admin')
trace.set_tag('usr.scope', 'read:message, write:files')
# Set free-form tags:
trace.set_tag('usr.another_tag', 'another_value')
The PHP tracer provides the \DDTrace\set_user()
function, which allows you to monitor and block authenticated requests.
\DDTrace\set_user()
adds the relevant user tags and metadata to the trace and automatically performs user blocking.
The following example shows how to set user monitoring tags and enable user blocking:
<?php
// Blocking is performed internally through the set_user call.
\DDTrace\set_user(
// A unique identifier of the user is required.
'123456789',
// All other fields are optional.
[
'name' => 'Jean Example',
'email' => 'jean.example@example.com',
'session_id' => '987654321',
'role' => 'admin',
'scope' => 'read:message, write:files',
]
);
?>
The Node tracer package provides the tracer.setUser(user)
function, which allows you to monitor authenticated requests by adding user information to the trace.
The example below shows how to add relevant user monitoring tags and enable user blocking capability:
const tracer = require('dd-trace').init()
function handle () {
tracer.setUser({
id: '123456789', // *REQUIRED* Unique identifier of the user.
// All other fields are optional.
email: 'jane.doe@example.com', // Email of the user.
name: 'Jane Doe', // User-friendly name of the user.
session_id: '987654321', // Session ID of the user.
role: 'admin', // Role the user is making the request under.
scope: 'read:message, write:files', // Scopes or granted authorizations the user currently possesses.
// Arbitrary fields are also accepted to attach custom data to the user (RBAC, Oauth, etc…)
custom_tag: 'custom data'
})
// Set the currently authenticated user and check whether they are blocked
if (tracer.appsec.isUserBlocked(user)) { // also set the currently authenticated user
return tracer.appsec.blockRequest(req, res) // blocking response is sent
}
}
For information and options, read the Node.js tracer documentation.
Monitor authenticated requests by adding user information to the trace with the set_user
function provided by the Python tracer package.
This example shows how to set user monitoring tags and enable user blocking capability:
from ddtrace.contrib.trace_utils import set_user
from ddtrace import tracer
# Call set_user() to trace the currently authenticated user id
user_id = "some_user_id"
set_user(tracer, user_id, name="John", email="test@test.com", scope="some_scope",
role="manager", session_id="session_id", propagate=True)
Starting in dd-trace-java v1.8.0, you can use the Java tracer’s API to track user events.
The following examples show how to track login events or custom events (using signup as an example).
import datadog.trace.api.EventTracker;
import datadog.trace.api.GlobalTracer;
public class LoginController {
private User doLogin(String userName, String password) {
// this is where you get User based on userName/password credentials
User user = checkLogin(userName, password);
Map<String, String> metadata = new HashMap<>();
metadata.put("email", user.getEmail());
// track user authentication success events
GlobalTracer
.getEventTracker()
.trackLoginSuccessEvent(user.getId(), metadata);
}
}
import datadog.trace.api.EventTracker;
import datadog.trace.api.GlobalTracer;
public class LoginController {
private User doLogin(String userName, String password) {
// this is where you get User based on userName/password credentials
User user = checkLogin(userName, password);
// if function returns null - user doesn't exist
boolean userExists = (user != null);
String userId = null;
Map<String, String> metadata = new HashMap<>();
if (userExists != null) {
userId = getUserId(userName)
metadata.put("email", user.getEmail());
} else {
userId = user.getEmail();
}
// track user authentication error events
GlobalTracer
.getEventTracker()
.trackLoginFailureEvent(userId, userExists, metadata);
}
}
import datadog.trace.api.EventTracker;
import datadog.trace.api.GlobalTracer;
public class LoginController {
private User doSignup(String userId, String email) {
// this is where you create your user account
User user = createUser(userId, email);
Map<String, String> metadata = new HashMap<>();
metadata.put("email", user.getEmail());
metadata.put("id", user.getId());
// track user signup events
GlobalTracer
.getEventTracker()
.trackCustomEvent("users.signup", metadata);
}
}
Starting in dd-trace-dotnet v2.23.0, you can use the .NET tracer’s API to track user events.
The following examples show how to track login events or custom events (using signup as an example).
using Datadog.Trace.AppSec;
void OnLogonSuccess(string userId, ...)
{
// metadata is optional
var metadata = new Dictionary<string, string>()
{
{ "customKey", "customValue" }
};
EventTrackingSdk.TrackUserLoginSuccessEvent(userId, metadata);
// ...
}
using Datadog.Trace.AppSec;
void OnLogonFailure(string userId, bool userExists, ...)
{
// If no userId can be provided, any unique user identifier (username, email...) may be used
// metadata is optional
var metadata = new Dictionary<string, string>()
{
{ "customKey", "customValue" }
};
EventTrackingSdk.TrackUserLoginFailureEvent(userId, userExists, metadata);
// ...
}
void OnUserSignupComplete(string userId, ...)
{
// the metadata parameter is optional, but adding the "usr.id"
var metadata = new Dictionary<string, string>()
{
{ "usr.id", userId }
};
// Leveraging custom business logic tracking to track user signups
EventTrackingSdk.TrackCustomEvent("users.signup", metadata);
// ...
}
Starting in dd-trace-go v1.47.0, you can use the Go tracer’s API to track user events.
The following examples show how to track login events or custom events (using signup as an example).
import "gopkg.in/DataDog/dd-trace-go.v1/appsec"
func handler(w http.ResponseWriter, r *http.Request) {
metadata := /* optional extra event metadata */
userdata := /* optional extra user data */
// Track login success, replace `my-uid` by a unique identifier of the user (such as numeric, username, and email)
if appsec.TrackUserLoginSuccessEvent(r.Context(), "my-uid", metadata, userdata) != nil {
// The given user id is blocked and the handler should be aborted asap.
// The blocking response will be sent by the appsec middleware.
return
}
}
import "gopkg.in/DataDog/dd-trace-go.v1/appsec"
func handler(w http.ResponseWriter, r *http.Request) {
exists := /* whether the given user id exists or not */
metadata := /* optional extra event metadata */
// Replace `my-uid` by a unique identifier of the user (numeric, username, email...)
appsec.TrackUserLoginFailureEvent(r.Context(), "my-uid", exists, metadata)
}
import "gopkg.in/DataDog/dd-trace-go.v1/appsec"
func handler(w http.ResponseWriter, r *http.Request) {
metadata := map[string]string{"usr.id": "my-uid"}
// Leveraging custom business logic tracking to track user signups
appsec.TrackCustomEvent(r.Context(), "users.signup", metadata)
}
Starting in dd-trace-rb v1.9.0, you can use the Ruby tracer’s API to track user events.
The following examples show how to track login events or custom events (using signup as an example).
Traces containing login success/failure events can be queried using the following query @appsec.security_activity:business_logic.users.login.success
or @appsec.security_activity:business_logic.users.login.failure
.
require 'datadog/kit/appsec/events'
trace = Datadog::Tracing.active_trace
# Replace `my_user_id` by a unique identifier of the user (numeric, username, email...)
Datadog::Kit::AppSec::Events.track_login_success(trace, user: { id: 'my_user_id' })
require 'datadog/kit/appsec/events'
trace = Datadog::Tracing.active_trace
# Replace `my_user_id` by a unique identifier of the user (numeric, username, email...)
# if the user exists
Datadog::Kit::AppSec::Events.track_login_failure(trace, user_id: 'my_user_id', user_exists: true)
# if the user doesn't exist
Datadog::Kit::AppSec::Events.track_login_failure(trace, user_id: 'my_user_id', user_exists: false)
require 'datadog/kit/appsec/events'
trace = Datadog::Tracing.active_trace
# Leveraging custom business logic tracking to track user signups
Datadog::Kit::AppSec::Events.track('users.signup', trace)
Starting in dd-trace-php v0.84.0, you can use the PHP tracer’s API to track user events.
The following examples show how to track login events or custom events (using signup as an example).
<?php
\datadog\appsec\track_user_login_success_event($id, ['email' => $email])
?>
<?php
// If no numeric userId is available, you may use any unique string as userId instead (username, email...)
// Make sure that the value is unique per user (and not per attacker/IP)
\datadog\appsec\track_user_login_failure_event($id, $exists, ['email' => $email])
?>
<?php
\datadog\appsec\track_custom_event('users.signup', ['id' => $id, 'email' => $email]);
?>
Starting in dd-trace-js v3.13.1, you can use the Node.js tracer’s API to track user events.
The following examples show how to track login events or custom events (using signup as an example).
const tracer = require('dd-trace')
// in a controller:
const user = {
id: 'user-id', // id is mandatory, if no numeric ID is available, any unique identifier will do (username, email...)
email: 'user@email.com' // other fields are optional
}
const metadata = { custom: 'value' } // optional metadata with arbitrary fields
// Log a successful user authentication event
tracer.appsec.trackUserLoginSuccessEvent(user, metadata) // metadata is optional
const tracer = require('dd-trace')
// in a controller:
const userId = 'user-id' // if no numeric ID is available, any unique identifier will do (username, email...)
const userExists = true // if the user login exists in database for example
const metadata = { custom: 'value' } // optional metadata with arbitrary fields
// metadata is optional
tracer.appsec.trackUserLoginFailureEvent(userId, userExists, metadata)
const tracer = require('dd-trace')
// in a controller:
const eventName = 'users.signup'
const metadata = { 'usr.id': 'user-id' }
tracer.appsec.trackCustomEvent(eventName, metadata)
Starting in dd-trace-py v1.9.0, you can use the Python tracer’s API to track user events.
The following examples show how to track login events or custom events (using signup as an example).
from ddtrace.appsec.trace_utils import track_user_login_success_event
from ddtrace import tracer
metadata = {"custom": "customvalue"}
# name, email, scope, role, session_id and propagate are optional arguments which
# default to None except propagate that defaults to True. They'll be
# passed to the set_user() function
track_user_login_success_event(tracer, "userid", metadata)
from ddtrace.appsec.trace_utils import track_user_login_failure_event
from ddtrace import tracer
metadata = {"custom": "customvalue"}
# exists indicates if the failed login user exists in the system
exists = False
# if no numeric userId is available, any unique identifier will do (username, email...)
track_user_login_failure_event(tracer, "userid", exists, metadata)
from ddtrace.appsec.trace_utils import track_custom_event
from ddtrace import tracer
metadata = {"usr.id": "userid"}
event_name = "users.signup"
track_custom_event(tracer, event_name, metadata)
If your service has ASM enabled and Remote Configuration enabled, you can create a custom WAF rule to flag any request it matches with a custom business logic tag. This doesn’t require any modification to your application, and can be done entirely from Datadog.
To get started, navigate to the Custom WAF Rule page and click on “Create New Rule”.
This will open a menu in which you may define your custom WAF rule. By selecting the “Business Logic” category, you will be able to configure an event type (for instance, users.password_reset
). You can then select the service you want to track, and a specific endpoint. You may also use the rule condition to target a specific parameter to identify the codeflow you want to instrument. When the condition matches, the library tags the trace and flags it to be forwarded to ASM. If you don’t need the condition, you may set a broad condition to match everything.
Once saved, the rule is deployed to instances of the service that have Remote Configuration enabled.
Automatic user activity event tracking
When ASM is enabled, Datadog Tracing Libraries attempt to detect user activity events automatically.
The events that can be automatically detected are:
users.login.success
users.login.failure
users.signup
Automatic user activity event tracking modes
Automatic user activity tracking offers the following modes:
identification
mode (short name: ident
):- This mode is the default and always collects the user ID or best effort.
- The user ID is collected on login success and login failure. With failure, the user ID is collected regardless of whether the user exists or not.
- When the instrumented framework doesn’t clearly provide a user ID, but rather a structured user object, the user ID is determined on a best effort basis based on the object field names. This list of field names are considered, ordered by priority:
- If no user ID is available or found, the user event is not emitted.
anonymization
mode (short name: anon
):- This mode is the same as
identification
, but anonymizes the user ID by hashing (SHA256) it and cropping the resulting hash.
disabled
mode:- ASM libraries do not collect any user ID from their automated instrumentations.
- User login events are not emitted.
All modes only affect automated instrumentation. The modes don't apply to manual collection. Manual collection is configured using an SDK, and those settings are not overridden by automated instrumentation.
Manual configuration
Datadog libraries allow you to configure auto-instrumentation by using the DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE
environment variable with the short name for the mode: ident
|anon
|disabled
.
The default mode is identification
mode (short name: ident
).
For example, DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE=anon
.
Deprecated modes
Previous modes are deprecated, but compatibility will be maintained until the next major release.
The following modes are deprecated:
safe
mode: The trace library does not include any PII information on the events metadata. The tracer library tries to collect the user ID, and only if the user ID is a valid GUIDextended
mode: The trace library tries to collect the user ID, and the user email. In this mode, Datadog does not check the type for the user ID to be a GUID. The trace library reports whatever value can be extracted from the event.
Note: There could be cases in which the trace library won’t be able to extract any information from the user event. The event would be reported with empty metadata. In those cases, use the SDK to manually instrument the user events.
Disabling user activity event tracking
To disable automated user activity detection through your ASM service catalog, change the automatic tracking mode environment variable DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE
to disabled
on the service you want to deactivate. All modes only affect automated instrumentation and require Remote Configuration to be enabled.
For manual configuration, you can set the environment variable DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING_ENABLED
to false
on your service and restart it. This must be set on the application hosting the Datadog Tracing Library, and not on the Datadog Agent.
Further Reading
Additional helpful documentation, links, and articles: