Skip to content

Commit 65cb319

Browse files
authored
Merge pull request #4625 from DataDog/appsec-57549-add-session-tracking-and-fingerprinting
[APPSEC-57549] Add session tracking and session fingerprinting
2 parents 222a2f7 + 9e87f46 commit 65cb319

File tree

13 files changed

+399
-17
lines changed

13 files changed

+399
-17
lines changed

.github/workflows/system-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ permissions: {}
2222
env:
2323
REGISTRY: ghcr.io
2424
REPO: ghcr.io/datadog/dd-trace-rb
25-
SYSTEM_TESTS_REF: main # This must always be set to `main` on dd-trace-rb's master branch
25+
SYSTEM_TESTS_REF: appsec-57504-enable-session-tracking-and-fingerprinting # This must always be set to `main` on dd-trace-rb's master branch
2626

2727
jobs:
2828
changes:

lib/datadog/appsec/component.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ def create_processor(settings, telemetry)
6363

6464
# NOTE: This is a temporary solution before the RuleMerger refactoring
6565
# with new RemoteConfig setup
66-
processors = rules.delete('processors')
67-
scanners = rules.delete('scanners')
66+
processors = rules['processors']
67+
scanners = rules['scanners']
6868

6969
ruleset = AppSec::Processor::RuleMerger.merge(
7070
rules: [rules],

lib/datadog/appsec/contrib/devise/ext.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module Ext
1818
TAG_DD_LOGIN_FAILURE_MODE = '_dd.appsec.events.users.login.failure.auto.mode'
1919

2020
TAG_USR_ID = 'usr.id'
21+
TAG_SESSION_ID = 'usr.session_id'
2122
TAG_SIGNUP_TRACK = 'appsec.events.users.signup.track'
2223
TAG_SIGNUP_USR_ID = 'appsec.events.users.signup.usr.id'
2324
TAG_SIGNUP_USR_LOGIN = 'appsec.events.users.signup.usr.login'

lib/datadog/appsec/contrib/devise/tracking_middleware.rb

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module Devise
1010
# A Rack middleware capable of tracking currently signed user
1111
class TrackingMiddleware
1212
WARDEN_KEY = 'warden'
13+
SESSION_ID_KEY = 'session_id'
1314

1415
def initialize(app)
1516
@app = app
@@ -32,16 +33,28 @@ def call(env)
3233
return @app.call(env)
3334
end
3435

36+
# NOTE: Rails session id will be set for unauthenticated users as well,
37+
# so we need to make sure we are tracking only authenticated users.
3538
id = transform(extract_id(env[WARDEN_KEY]))
39+
session_id = env[WARDEN_KEY].raw_session[SESSION_ID_KEY] if id
40+
3641
if id
37-
unless context.span.has_tag?(Ext::TAG_USR_ID)
38-
context.span[Ext::TAG_USR_ID] = id
42+
# NOTE: There is no option to set session id without setting user id via SDK.
43+
unless context.span.has_tag?(Ext::TAG_USR_ID) && context.span.has_tag?(Ext::TAG_SESSION_ID)
44+
user_id = context.span[Ext::TAG_USR_ID] || id
45+
user_session_id = context.span[Ext::TAG_SESSION_ID] || session_id
46+
47+
# FIXME: The current implementation of event arguments is forsing us
48+
# to bloat User class, and pass nil-value instead of skip
49+
# passing them at first place.
50+
# This is a temporary situation until we refactor events model.
3951
AppSec::Instrumentation.gateway.push(
40-
'identity.set_user', AppSec::Instrumentation::Gateway::User.new(id, nil)
52+
'identity.set_user', AppSec::Instrumentation::Gateway::User.new(user_id, nil, user_session_id)
4153
)
4254
end
4355

44-
context.span[Ext::TAG_DD_USR_ID] = id.to_s
56+
context.span[Ext::TAG_USR_ID] ||= id
57+
context.span[Ext::TAG_DD_USR_ID] = id
4558
context.span[Ext::TAG_DD_COLLECTION_MODE] ||= Configuration.auto_user_instrumentation_mode
4659
end
4760

lib/datadog/appsec/instrumentation/gateway/argument.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,17 @@ class Gateway
88
class Argument; end # rubocop:disable Lint/EmptyClass
99

1010
# Gateway User argument
11+
# NOTE: This class is a subject of elimination and will be removed when
12+
# the event system is refactored.
1113
class User < Argument
12-
attr_reader :id, :login
14+
attr_reader :id, :login, :session_id
1315

14-
def initialize(id, login)
16+
def initialize(id, login = nil, session_id = nil)
1517
super()
1618

1719
@id = id
1820
@login = login
21+
@session_id = session_id
1922
end
2023
end
2124
end

lib/datadog/appsec/monitor/gateway/watcher.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,26 @@ def watch_user_id(gateway = Instrumentation.gateway)
2121
gateway.watch('identity.set_user', :appsec) do |stack, user|
2222
context = AppSec.active_context
2323

24-
if user.id.nil? && user.login.nil?
24+
if user.id.nil? && user.login.nil? && user.session_id.nil?
2525
Datadog.logger.debug { 'AppSec: skipping WAF check because no user information was provided' }
2626
next stack.call(user)
2727
end
2828

2929
persistent_data = {}
3030
persistent_data['usr.id'] = user.id if user.id
3131
persistent_data['usr.login'] = user.login if user.login
32+
persistent_data['usr.session_id'] = user.session_id if user.session_id
3233

3334
result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
3435

35-
if result.match?
36-
AppSec::Event.tag_and_keep!(context, result)
37-
36+
if result.match? || !result.derivatives.empty?
3837
context.events.push(
3938
AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
4039
)
40+
end
4141

42+
if result.match?
43+
AppSec::Event.tag_and_keep!(context, result)
4244
AppSec::ActionsHandler.handle(result.actions)
4345
end
4446

lib/datadog/kit/identity.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def set_user(
7070
if Datadog::AppSec.active_context
7171
active_span.set_tag('_dd.appsec.user.collection_mode', 'sdk')
7272

73-
user = ::Datadog::AppSec::Instrumentation::Gateway::User.new(id, others[:login])
73+
user = ::Datadog::AppSec::Instrumentation::Gateway::User.new(id, others[:login], session_id)
7474
::Datadog::AppSec::Instrumentation.gateway.push('identity.set_user', user)
7575
end
7676
end

sig/datadog/appsec/contrib/devise/ext.rbs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ module Datadog
1111

1212
TAG_USR_ID: ::String
1313

14+
TAG_SESSION_ID: ::String
15+
1416
TAG_LOGIN_SUCCESS_TRACK: ::String
1517

1618
TAG_LOGIN_SUCCESS_USR_LOGIN: ::String

sig/datadog/appsec/contrib/devise/tracking_middleware.rbs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ module Datadog
55
class TrackingMiddleware
66
WARDEN_KEY: ::String
77

8+
SESSION_ID_KEY: ::String
9+
810
@app: untyped
911

1012
def initialize: (untyped app) -> void

sig/datadog/appsec/instrumentation/gateway/argument.rbs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,15 @@ module Datadog
1111

1212
@login: String?
1313

14+
@session_id: String?
15+
1416
attr_reader id: String
1517

1618
attr_reader login: String?
1719

18-
def initialize: (String id, String? login) -> void
20+
attr_reader session_id: String?
21+
22+
def initialize: (String id, String? login, String? session_id) -> void
1923
end
2024
end
2125
end

0 commit comments

Comments
 (0)