1
1
package com .datadog .appsec .gateway ;
2
2
3
3
import static com .datadog .appsec .event .data .MapDataBundle .Builder .CAPACITY_0_2 ;
4
- import static com .datadog .appsec .event .data .MapDataBundle .Builder .CAPACITY_3_4 ;
5
4
import static com .datadog .appsec .event .data .MapDataBundle .Builder .CAPACITY_6_10 ;
6
5
import static com .datadog .appsec .gateway .AppSecRequestContext .DEFAULT_REQUEST_HEADERS_ALLOW_LIST ;
7
6
import static com .datadog .appsec .gateway .AppSecRequestContext .REQUEST_HEADERS_ALLOW_LIST ;
8
7
import static com .datadog .appsec .gateway .AppSecRequestContext .RESPONSE_HEADERS_ALLOW_LIST ;
9
- import static datadog .trace .api .UserIdCollectionMode .ANONYMIZATION ;
10
- import static datadog .trace .api .UserIdCollectionMode .DISABLED ;
11
- import static datadog .trace .api .UserIdCollectionMode .SDK ;
12
- import static datadog .trace .api .telemetry .LogCollector .SEND_TELEMETRY ;
13
- import static datadog .trace .util .Strings .toHexString ;
14
8
15
9
import com .datadog .appsec .AppSecSystem ;
16
10
import com .datadog .appsec .api .security .ApiSecurityRequestSampler ;
28
22
import com .datadog .appsec .report .AppSecEventWrapper ;
29
23
import datadog .trace .api .Config ;
30
24
import datadog .trace .api .ProductTraceSource ;
31
- import datadog .trace .api .UserIdCollectionMode ;
32
25
import datadog .trace .api .gateway .Events ;
33
26
import datadog .trace .api .gateway .Flow ;
34
27
import datadog .trace .api .gateway .IGSpanInfo ;
48
41
import java .net .URISyntaxException ;
49
42
import java .nio .charset .Charset ;
50
43
import java .nio .charset .StandardCharsets ;
51
- import java .security .MessageDigest ;
52
- import java .security .NoSuchAlgorithmException ;
53
44
import java .util .ArrayList ;
54
45
import java .util .Arrays ;
55
46
import java .util .Collection ;
56
47
import java .util .Collections ;
48
+ import java .util .EnumMap ;
57
49
import java .util .HashMap ;
58
50
import java .util .HashSet ;
59
51
import java .util .List ;
60
52
import java .util .Map ;
61
53
import java .util .Set ;
62
54
import java .util .concurrent .ConcurrentHashMap ;
63
- import java .util .concurrent .atomic .AtomicBoolean ;
64
55
import java .util .regex .Pattern ;
65
56
import java .util .stream .Collectors ;
66
57
import org .slf4j .Logger ;
@@ -76,21 +67,16 @@ public class GatewayBridge {
76
67
private static final Pattern QUERY_PARAM_SPLITTER = Pattern .compile ("&" );
77
68
private static final Map <String , List <String >> EMPTY_QUERY_PARAMS = Collections .emptyMap ();
78
69
79
- private static final int HASH_SIZE_BYTES = 16 ; // 128 bits
80
- private static final String ANON_PREFIX = "anon_" ;
81
- private static final AtomicBoolean SHA_MISSING_REPORTED = new AtomicBoolean (false );
82
-
83
70
/** User tracking tags that will force the collection of request headers */
84
71
private static final String [] USER_TRACKING_TAGS = {
85
72
"appsec.events.users.login.success.track" , "appsec.events.users.login.failure.track"
86
73
};
87
74
88
- private static final Map <String , LoginEvent > EVENT_MAPPINGS = new HashMap <>();
75
+ private static final Map <LoginEvent , Address <?>> EVENT_MAPPINGS = new EnumMap <>(LoginEvent . class );
89
76
90
77
static {
91
- EVENT_MAPPINGS .put ("users.login.success" , LoginEvent .LOGIN_SUCCESS );
92
- EVENT_MAPPINGS .put ("users.login.failure" , LoginEvent .LOGIN_FAILURE );
93
- EVENT_MAPPINGS .put ("users.signup" , LoginEvent .SIGN_UP );
78
+ EVENT_MAPPINGS .put (LoginEvent .LOGIN_SUCCESS , KnownAddresses .LOGIN_SUCCESS );
79
+ EVENT_MAPPINGS .put (LoginEvent .LOGIN_FAILURE , KnownAddresses .LOGIN_FAILURE );
94
80
}
95
81
96
82
private static final String METASTRUCT_EXPLOIT = "exploit" ;
@@ -198,44 +184,16 @@ public void reset() {
198
184
shellCmdSubInfo = null ;
199
185
}
200
186
201
- private Flow <Void > onUser (
202
- final RequestContext ctx_ , final UserIdCollectionMode mode , final String originalUser ) {
203
- if (mode == DISABLED ) {
204
- return NoopFlow .INSTANCE ;
205
- }
206
- final String user = anonymizeUser (mode , originalUser );
207
- if (user == null ) {
208
- return NoopFlow .INSTANCE ;
209
- }
187
+ private Flow <Void > onUser (final RequestContext ctx_ , final String user ) {
210
188
final AppSecRequestContext ctx = ctx_ .getData (RequestContextSlot .APPSEC );
211
189
if (ctx == null ) {
212
190
return NoopFlow .INSTANCE ;
213
191
}
214
- final TraceSegment segment = ctx_ .getTraceSegment ();
215
-
216
- // span with ASM data
217
- segment .setTagTop (Tags .ASM_KEEP , true );
218
- segment .setTagTop (Tags .PROPAGATED_TRACE_SOURCE , ProductTraceSource .ASM );
219
-
220
- // skip event if we have an SDK one
221
- if (mode != SDK ) {
222
- segment .setTagTop ("_dd.appsec.usr.id" , user );
223
- if (ctx .getUserIdSource () == SDK ) {
224
- return NoopFlow .INSTANCE ;
225
- }
226
- }
227
-
228
- // update span tags
229
- segment .setTagTop ("usr.id" , user );
230
- segment .setTagTop ("_dd.appsec.user.collection_mode" , mode .fullName ());
231
192
232
193
// update current context with new user id
233
- ctx .setUserIdSource (mode );
234
- final boolean newUserId = !user .equals (ctx .getUserId ());
235
- if (!newUserId ) {
194
+ if (!ctx .updateUserId (user )) {
236
195
return NoopFlow .INSTANCE ;
237
196
}
238
- ctx .setUserId (user );
239
197
240
198
// call waf if we have a new user id
241
199
while (true ) {
@@ -259,96 +217,29 @@ private Flow<Void> onUser(
259
217
}
260
218
261
219
private Flow <Void > onLoginEvent (
262
- final RequestContext ctx_ ,
263
- final UserIdCollectionMode mode ,
264
- final String eventName ,
265
- final Boolean exists ,
266
- final String originalUser ,
267
- final Map <String , String > metadata ) {
268
- if (mode == DISABLED ) {
269
- return NoopFlow .INSTANCE ;
270
- }
220
+ final RequestContext ctx_ , final LoginEvent event , final String login ) {
271
221
final AppSecRequestContext ctx = ctx_ .getData (RequestContextSlot .APPSEC );
272
222
if (ctx == null ) {
273
223
return NoopFlow .INSTANCE ;
274
224
}
275
- final TraceSegment segment = ctx_ .getTraceSegment ();
276
-
277
- // span with ASM data
278
- segment .setTagTop (Tags .ASM_KEEP , true );
279
- segment .setTagTop (Tags .PROPAGATED_TRACE_SOURCE , ProductTraceSource .ASM );
280
-
281
- // update span tags
282
- segment .setTagTop ("appsec.events." + eventName + ".track" , true , true );
283
- if (metadata != null && !metadata .isEmpty ()) {
284
- segment .setTagTop ("appsec.events." + eventName , metadata , true );
285
- }
286
- if (mode == SDK ) {
287
- segment .setTagTop ("_dd.appsec.events." + eventName + ".sdk" , true , true );
288
- } else {
289
- segment .setTagTop ("_dd.appsec.events." + eventName + ".auto.mode" , mode .fullName (), true );
290
- }
291
-
292
- if (exists != null ) {
293
- if (mode == SDK || ctx .getUserLoginSource () != SDK ) {
294
- segment .setTagTop ("appsec.events." + eventName + ".usr.exists" , exists , true );
295
- }
296
- }
297
-
298
- final String user = anonymizeUser (mode , originalUser );
299
- if (user == null ) {
300
- // can happen in custom events
301
- return NoopFlow .INSTANCE ;
302
- }
303
-
304
- // parse the event (might be null for custom events sent via the SDK)
305
- final LoginEvent sourceEvent = EVENT_MAPPINGS .get (eventName );
306
-
307
- // skip event if we have an SDK one
308
- if (mode != SDK ) {
309
- segment .setTagTop ("_dd.appsec.usr.login" , user );
310
- if (ctx .getUserLoginSource () == SDK ) {
311
- return NoopFlow .INSTANCE ;
312
- }
313
- } else {
314
- if (sourceEvent == LoginEvent .LOGIN_SUCCESS ) {
315
- segment .setTagTop ("usr.id" , user , false );
316
- } else {
317
- segment .setTagTop ("appsec.events." + eventName + ".usr.id" , user , true );
318
- }
319
- segment .setTagTop ("_dd.appsec.user.collection_mode" , mode .fullName ());
320
- }
321
-
322
- // update user span tags
323
- segment .setTagTop ("appsec.events." + eventName + ".usr.login" , user , true );
324
225
325
226
// update current context with new user login
326
- ctx .setUserLoginSource (mode );
327
- if (mode == SDK ) {
328
- ctx .setUserIdSource (mode ); // we are setting the usr.id through the SDK
329
- }
330
- final boolean newUserLogin = !user .equals (ctx .getUserLogin ());
331
- if (!newUserLogin ) {
227
+ if (!ctx .updateUserLogin (login )) {
332
228
return NoopFlow .INSTANCE ;
333
229
}
334
- ctx .setUserLogin (user );
335
230
336
231
// call waf if we have a new user login
337
- final List <Address <?>> addresses = new ArrayList <>(3 );
338
- final MapDataBundle .Builder bundleBuilder = new MapDataBundle .Builder (CAPACITY_3_4 );
232
+ final List <Address <?>> addresses = new ArrayList <>(2 );
233
+ final MapDataBundle .Builder bundleBuilder = new MapDataBundle .Builder (CAPACITY_0_2 );
339
234
addresses .add (KnownAddresses .USER_LOGIN );
340
- bundleBuilder .add (KnownAddresses .USER_LOGIN , user );
341
- if (mode == SDK ) {
342
- addresses .add (KnownAddresses .USER_ID );
343
- bundleBuilder .add (KnownAddresses .USER_ID , user );
344
- }
345
- // we don't support null values for the address so we use an invalid placeholder here
346
- if (sourceEvent == LoginEvent .LOGIN_SUCCESS ) {
347
- addresses .add (KnownAddresses .LOGIN_SUCCESS );
348
- bundleBuilder .add (KnownAddresses .LOGIN_SUCCESS , "invalid" );
349
- } else if (sourceEvent == LoginEvent .LOGIN_FAILURE ) {
350
- addresses .add (KnownAddresses .LOGIN_FAILURE );
351
- bundleBuilder .add (KnownAddresses .LOGIN_FAILURE , "invalid" );
235
+ bundleBuilder .add (KnownAddresses .USER_LOGIN , login );
236
+
237
+ // parse the event
238
+ Address <?> address = EVENT_MAPPINGS .get (event );
239
+ if (address != null ) {
240
+ addresses .add (address );
241
+ // we don't support null values for the address so we use an invalid placeholder here
242
+ bundleBuilder .add (address , "invalid" );
352
243
}
353
244
final DataBundle bundle = bundleBuilder .build ();
354
245
final String subInfoKey =
@@ -1159,33 +1050,6 @@ private static int byteToDigit(byte b) {
1159
1050
return -1 ;
1160
1051
}
1161
1052
1162
- protected static String anonymizeUser (final UserIdCollectionMode mode , final String userId ) {
1163
- if (mode != ANONYMIZATION || userId == null ) {
1164
- return userId ;
1165
- }
1166
- MessageDigest digest ;
1167
- try {
1168
- // TODO avoid lookup a new instance every time
1169
- digest = MessageDigest .getInstance ("SHA-256" );
1170
- } catch (NoSuchAlgorithmException e ) {
1171
- if (!SHA_MISSING_REPORTED .getAndSet (true )) {
1172
- log .error (
1173
- SEND_TELEMETRY ,
1174
- "Missing SHA-256 digest, user collection in 'anon' mode cannot continue" ,
1175
- e );
1176
- }
1177
- return null ;
1178
- }
1179
- digest .update (userId .getBytes ());
1180
- byte [] hash = digest .digest ();
1181
- if (hash .length > HASH_SIZE_BYTES ) {
1182
- byte [] temp = new byte [HASH_SIZE_BYTES ];
1183
- System .arraycopy (hash , 0 , temp , 0 , temp .length );
1184
- hash = temp ;
1185
- }
1186
- return ANON_PREFIX + toHexString (hash );
1187
- }
1188
-
1189
1053
private static class IGAppSecEventDependencies {
1190
1054
1191
1055
private static final Map <Address <?>, Collection <datadog .trace .api .gateway .EventType <?>>>
0 commit comments