Skip to content

Commit 01d9133

Browse files
Add fingerprint support to the WAF (#7436)
Add fingerprint support in the WAF
1 parent f152e54 commit 01d9133

10 files changed

Lines changed: 185 additions & 20 deletions

File tree

dd-java-agent/appsec/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ dependencies {
1515
implementation project(':internal-api')
1616
implementation project(':communication')
1717
implementation project(':telemetry')
18-
implementation group: 'io.sqreen', name: 'libsqreen', version: '10.1.0'
18+
implementation group: 'io.sqreen', name: 'libsqreen', version: '11.0.0'
1919
implementation libs.moshi
2020

2121
testImplementation libs.bytebuddy

dd-java-agent/appsec/src/main/java/com/datadog/appsec/config/AppSecConfigServiceImpl.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@
99
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_DD_RULES;
1010
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_EXCLUSIONS;
1111
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_EXCLUSION_DATA;
12+
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_HEADER_FINGERPRINT;
1213
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_IP_BLOCKING;
14+
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_NETWORK_FINGERPRINT;
1315
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_RASP_SQLI;
1416
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_REQUEST_BLOCKING;
1517
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_TRUSTED_IPS;
1618
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_USER_BLOCKING;
19+
import static datadog.remoteconfig.Capabilities.CAPABILITY_ENDPOINT_FINGERPRINT;
1720

1821
import com.datadog.appsec.AppSecSystem;
1922
import com.datadog.appsec.api.security.ApiSecurityRequestSampler;
@@ -104,7 +107,12 @@ private void subscribeConfigurationPoller() {
104107
| CAPABILITY_ASM_CUSTOM_RULES
105108
| CAPABILITY_ASM_CUSTOM_BLOCKING_RESPONSE
106109
| CAPABILITY_ASM_TRUSTED_IPS
107-
| CAPABILITY_ASM_RASP_SQLI);
110+
| CAPABILITY_ASM_RASP_SQLI
111+
| CAPABILITY_ENDPOINT_FINGERPRINT
112+
// TODO enable when usr.id and usr.session_id addresses are added
113+
// | CAPABILITY_ASM_SESSION_FINGERPRINT
114+
| CAPABILITY_ASM_NETWORK_FINGERPRINT
115+
| CAPABILITY_ASM_HEADER_FINGERPRINT);
108116
}
109117

110118
private void subscribeRulesAndData() {
@@ -345,7 +353,12 @@ public void close() {
345353
| CAPABILITY_ASM_TRUSTED_IPS
346354
| CAPABILITY_ASM_API_SECURITY_SAMPLE_RATE
347355
| CAPABILITY_ASM_RASP_SQLI
348-
| CAPABILITY_ASM_AUTO_USER_INSTRUM_MODE);
356+
| CAPABILITY_ASM_AUTO_USER_INSTRUM_MODE
357+
| CAPABILITY_ENDPOINT_FINGERPRINT
358+
// TODO enable when usr.id and usr.session_id addresses are added
359+
// | CAPABILITY_ASM_SESSION_FINGERPRINT
360+
| CAPABILITY_ASM_NETWORK_FINGERPRINT
361+
| CAPABILITY_ASM_HEADER_FINGERPRINT);
349362
this.configurationPoller.removeListeners(Product.ASM_DD);
350363
this.configurationPoller.removeListeners(Product.ASM_DATA);
351364
this.configurationPoller.removeListeners(Product.ASM);

dd-java-agent/appsec/src/main/java/com/datadog/appsec/gateway/AppSecRequestContext.java

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.datadog.appsec.gateway;
22

3+
import static java.util.Collections.emptySet;
4+
35
import com.datadog.appsec.event.data.Address;
46
import com.datadog.appsec.event.data.DataBundle;
57
import com.datadog.appsec.report.AppSecEvent;
@@ -104,7 +106,7 @@ public class AppSecRequestContext implements DataBundle, Closeable {
104106
private boolean convertedReqBodyPublished;
105107
private boolean respDataPublished;
106108
private boolean pathParamsPublished;
107-
private Map<String, String> apiSchemas;
109+
private Map<String, String> derivatives;
108110

109111
private final AtomicBoolean rateLimited = new AtomicBoolean(false);
110112
private volatile boolean throttled;
@@ -499,24 +501,29 @@ StackTraceCollection transferStackTracesCollection() {
499501
}
500502
}
501503

502-
public void reportApiSchemas(Map<String, String> schemas) {
503-
if (schemas == null || schemas.isEmpty()) return;
504+
public void reportDerivatives(Map<String, String> data) {
505+
if (data == null || data.isEmpty()) return;
504506

505-
if (apiSchemas == null) {
506-
apiSchemas = schemas;
507+
if (derivatives == null) {
508+
derivatives = data;
507509
} else {
508-
apiSchemas.putAll(schemas);
510+
derivatives.putAll(data);
509511
}
510512
}
511513

512-
boolean commitApiSchemas(TraceSegment traceSegment) {
513-
if (traceSegment == null || apiSchemas == null) {
514+
boolean commitDerivatives(TraceSegment traceSegment) {
515+
if (traceSegment == null || derivatives == null) {
514516
return false;
515517
}
516-
apiSchemas.forEach(traceSegment::setTagTop);
518+
derivatives.forEach(traceSegment::setTagTop);
517519
return true;
518520
}
519521

522+
// Mainly used for testing and logging
523+
Set<String> getDerivativeKeys() {
524+
return derivatives == null ? emptySet() : new HashSet<>(derivatives.keySet());
525+
}
526+
520527
public boolean isThrottled(RateLimiter rateLimiter) {
521528
if (rateLimiter != null && rateLimited.compareAndSet(false, true)) {
522529
throttled = rateLimiter.isThrottled();

dd-java-agent/appsec/src/main/java/com/datadog/appsec/gateway/GatewayBridge.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -481,9 +481,9 @@ private NoopFlow onRequestEnded(RequestContext ctx_, IGSpanInfo spanInfo) {
481481
// Report minimum set of collected request headers
482482
writeRequestHeaders(traceSeg, DEFAULT_REQUEST_HEADERS_ALLOW_LIST, ctx.getRequestHeaders());
483483
}
484-
// If extracted any Api Schemas - commit them
485-
if (!ctx.commitApiSchemas(traceSeg)) {
486-
log.debug("Unable to commit, api security schemas and will be skipped");
484+
// If extracted any derivatives - commit them
485+
if (!ctx.commitDerivatives(traceSeg)) {
486+
log.debug("Unable to commit, derivatives will be skipped {}", ctx.getDerivativeKeys());
487487
}
488488

489489
if (ctx.isBlocked()) {

dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFModule.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,8 +512,8 @@ public void onDataAvailable(
512512
}
513513
}
514514

515-
if (resultWithData != null && resultWithData.schemas != null) {
516-
reqCtx.reportApiSchemas(resultWithData.schemas);
515+
if (resultWithData.derivatives != null) {
516+
reqCtx.reportDerivatives(resultWithData.derivatives);
517517
}
518518
}
519519

dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/config/AppSecConfigServiceImplSpecification.groovy

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,14 @@ import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_CUSTOM_RULES
2323
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_DD_RULES
2424
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_EXCLUSIONS
2525
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_EXCLUSION_DATA
26+
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_HEADER_FINGERPRINT
2627
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_IP_BLOCKING
28+
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_NETWORK_FINGERPRINT
2729
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_RASP_SQLI
2830
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_REQUEST_BLOCKING
2931
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_TRUSTED_IPS
3032
import static datadog.remoteconfig.Capabilities.CAPABILITY_ASM_USER_BLOCKING
33+
import static datadog.remoteconfig.Capabilities.CAPABILITY_ENDPOINT_FINGERPRINT
3134
import static datadog.remoteconfig.PollingHinterNoop.NOOP
3235
import static datadog.trace.api.UserIdCollectionMode.ANONYMIZATION
3336
import static datadog.trace.api.UserIdCollectionMode.DISABLED
@@ -261,7 +264,11 @@ class AppSecConfigServiceImplSpecification extends DDSpecification {
261264
| CAPABILITY_ASM_CUSTOM_RULES
262265
| CAPABILITY_ASM_CUSTOM_BLOCKING_RESPONSE
263266
| CAPABILITY_ASM_TRUSTED_IPS
264-
| CAPABILITY_ASM_RASP_SQLI)
267+
| CAPABILITY_ASM_RASP_SQLI
268+
| CAPABILITY_ENDPOINT_FINGERPRINT
269+
// | CAPABILITY_ASM_SESSION_FINGERPRINT
270+
| CAPABILITY_ASM_NETWORK_FINGERPRINT
271+
| CAPABILITY_ASM_HEADER_FINGERPRINT)
265272
0 * _._
266273
initialWafConfig.get() != null
267274

@@ -406,7 +413,11 @@ class AppSecConfigServiceImplSpecification extends DDSpecification {
406413
| CAPABILITY_ASM_CUSTOM_RULES
407414
| CAPABILITY_ASM_CUSTOM_BLOCKING_RESPONSE
408415
| CAPABILITY_ASM_TRUSTED_IPS
409-
| CAPABILITY_ASM_RASP_SQLI)
416+
| CAPABILITY_ASM_RASP_SQLI
417+
| CAPABILITY_ENDPOINT_FINGERPRINT
418+
// | CAPABILITY_ASM_SESSION_FINGERPRINT
419+
| CAPABILITY_ASM_NETWORK_FINGERPRINT
420+
| CAPABILITY_ASM_HEADER_FINGERPRINT)
410421
0 * _._
411422

412423
when:
@@ -474,7 +485,11 @@ class AppSecConfigServiceImplSpecification extends DDSpecification {
474485
| CAPABILITY_ASM_TRUSTED_IPS
475486
| CAPABILITY_ASM_API_SECURITY_SAMPLE_RATE
476487
| CAPABILITY_ASM_RASP_SQLI
477-
| CAPABILITY_ASM_AUTO_USER_INSTRUM_MODE,)
488+
| CAPABILITY_ASM_AUTO_USER_INSTRUM_MODE
489+
| CAPABILITY_ENDPOINT_FINGERPRINT
490+
// | CAPABILITY_ASM_SESSION_FINGERPRINT
491+
| CAPABILITY_ASM_NETWORK_FINGERPRINT
492+
| CAPABILITY_ASM_HEADER_FINGERPRINT)
478493
4 * poller.removeListeners(_)
479494
1 * poller.removeConfigurationEndListener(_)
480495
1 * poller.stop()

dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/gateway/GatewayBridgeSpecification.groovy

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -924,4 +924,20 @@ class GatewayBridgeSpecification extends DDSpecification {
924924
'appsec.events.users.login.failure.track' | true
925925
'appsec.another.unrelated.tag' | false
926926
}
927+
928+
void 'fingerprints are set in the span after a request'() {
929+
given:
930+
final mockAppSecCtx = new AppSecRequestContext(derivatives: ['_dd.appsec.fp.http.endpoint': 'xyz'])
931+
final mockCtx = Stub(RequestContext) {
932+
getData(RequestContextSlot.APPSEC) >> mockAppSecCtx
933+
getTraceSegment() >> traceSegment
934+
}
935+
final spanInfo = Stub(AgentSpan)
936+
937+
when:
938+
requestEndedCB.apply(mockCtx, spanInfo)
939+
940+
then:
941+
1 * traceSegment.setTagTop('_dd.appsec.fp.http.endpoint', 'xyz')
942+
}
927943
}

dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/powerwaf/PowerWAFModuleSpecification.groovy

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1486,6 +1486,30 @@ class PowerWAFModuleSpecification extends DDSpecification {
14861486
})
14871487
}
14881488

1489+
void 'fingerprint support'() {
1490+
given:
1491+
final flow = Mock(ChangeableFlow)
1492+
setupWithStubConfigService 'fingerprint_config.json'
1493+
dataListener = pwafModule.dataSubscriptions.first()
1494+
ctx.closeAdditive()
1495+
final bundle = MapDataBundle.ofDelegate([
1496+
(KnownAddresses.WAF_CONTEXT_PROCESSOR): [fingerprint: true],
1497+
(KnownAddresses.REQUEST_METHOD): 'GET',
1498+
(KnownAddresses.REQUEST_URI_RAW): 'http://localhost:8080/test',
1499+
(KnownAddresses.REQUEST_BODY_OBJECT): [:],
1500+
(KnownAddresses.REQUEST_QUERY): [name: ['test']],
1501+
(KnownAddresses.HEADERS_NO_COOKIES): new CaseInsensitiveMap<List<String>>(['user-agent': ['Arachni/v1.5.1']])
1502+
])
1503+
1504+
when:
1505+
dataListener.onDataAvailable(flow, ctx, bundle, gwCtx)
1506+
ctx.closeAdditive()
1507+
1508+
then:
1509+
1 * flow.setAction({ it.blocking })
1510+
ctx.derivativeKeys.contains('_dd.appsec.fp.http.endpoint')
1511+
}
1512+
14891513
private Map<String, Object> getDefaultConfig() {
14901514
def service = new StubAppSecConfigService()
14911515
service.init()
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
{
2+
"version": "2.2",
3+
"metadata": {
4+
"rules_version": "1.8.0"
5+
},
6+
"rules": [
7+
{
8+
"id": "arachni_rule",
9+
"name": "Arachni",
10+
"tags": {
11+
"type": "security_scanner",
12+
"category": "attack_attempt"
13+
},
14+
"conditions": [
15+
{
16+
"parameters": {
17+
"inputs": [
18+
{
19+
"address": "server.request.headers.no_cookies",
20+
"key_path": [
21+
"user-agent"
22+
]
23+
}
24+
],
25+
"regex": "^Arachni\\/v"
26+
},
27+
"operator": "match_regex"
28+
}
29+
],
30+
"transformers": [],
31+
"on_match": ["block"]
32+
}
33+
],
34+
"processors": [
35+
{
36+
"id": "processor-001",
37+
"generator": "http_endpoint_fingerprint",
38+
"conditions": [
39+
{
40+
"operator": "equals",
41+
"parameters": {
42+
"inputs": [
43+
{
44+
"address": "waf.context.processor",
45+
"key_path": [
46+
"fingerprint"
47+
]
48+
}
49+
],
50+
"value": true,
51+
"type": "boolean"
52+
}
53+
}
54+
],
55+
"parameters": {
56+
"mappings": [
57+
{
58+
"method": [
59+
{
60+
"address": "server.request.method"
61+
}
62+
],
63+
"uri_raw": [
64+
{
65+
"address": "server.request.uri.raw"
66+
}
67+
],
68+
"body": [
69+
{
70+
"address": "server.request.body"
71+
}
72+
],
73+
"query": [
74+
{
75+
"address": "server.request.query"
76+
}
77+
],
78+
"output": "_dd.appsec.fp.http.endpoint"
79+
}
80+
]
81+
},
82+
"evaluate": true,
83+
"output": true
84+
}
85+
]
86+
}

remote-config/remote-config-api/src/main/java/datadog/remoteconfig/Capabilities.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,8 @@ public interface Capabilities {
3333
long CAPABILITY_APM_TRACING_SAMPLE_RULES = 1 << 29;
3434
long CAPABILITY_CSM_ACTIVATION = 1 << 30;
3535
long CAPABILITY_ASM_AUTO_USER_INSTRUM_MODE = 1L << 31;
36+
long CAPABILITY_ENDPOINT_FINGERPRINT = 1L << 32;
37+
long CAPABILITY_ASM_SESSION_FINGERPRINT = 1L << 33;
38+
long CAPABILITY_ASM_NETWORK_FINGERPRINT = 1L << 34;
39+
long CAPABILITY_ASM_HEADER_FINGERPRINT = 1L << 35;
3640
}

0 commit comments

Comments
 (0)