Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ASM] Add support for attacker fingerprinting #4698

Merged
merged 16 commits into from
Oct 8, 2024
Merged
14 changes: 14 additions & 0 deletions .github/workflows/appsec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -250,3 +250,17 @@ jobs:
- run: yarn test:integration:appsec
- uses: ./.github/actions/node/latest
- run: yarn test:integration:appsec

passport:
simon-id marked this conversation as resolved.
Show resolved Hide resolved
runs-on: ubuntu-latest
env:
PLUGINS: passport-local|passport-http
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/node/setup
- uses: ./.github/actions/install
- uses: ./.github/actions/node/oldest
- run: yarn test:appsec:plugins:ci
- uses: ./.github/actions/node/latest
- run: yarn test:appsec:plugins:ci
- uses: codecov/codecov-action@v3
5 changes: 4 additions & 1 deletion packages/dd-trace/src/appsec/addresses.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,8 @@ module.exports = {
FS_OPERATION_PATH: 'server.io.fs.file',

DB_STATEMENT: 'server.db.statement',
DB_SYSTEM: 'server.db.system'
DB_SYSTEM: 'server.db.system',

LOGIN_SUCCESS: 'server.business_logic.users.login.success',
LOGIN_FAILURE: 'server.business_logic.users.login.failure'
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,8 @@ module.exports = {
ASM_RASP_SQLI: 1n << 21n,
ASM_RASP_SSRF: 1n << 23n,
ASM_RASP_LFI: 1n << 24n,
APM_TRACING_SAMPLE_RULES: 1n << 29n
APM_TRACING_SAMPLE_RULES: 1n << 29n,
ASM_ENDPOINT_FINGERPRINT: 1n << 32n,
ASM_NETWORK_FINGERPRINT: 1n << 34n,
ASM_HEADER_FINGERPRINT: 1n << 35n
}
6 changes: 6 additions & 0 deletions packages/dd-trace/src/appsec/remote_config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ function enableWafUpdate (appsecConfig) {
rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_RULES, true)
rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_BLOCKING_RESPONSE, true)
rc.updateCapabilities(RemoteConfigCapabilities.ASM_TRUSTED_IPS, true)
rc.updateCapabilities(RemoteConfigCapabilities.ASM_ENDPOINT_FINGERPRINT, true)
simon-id marked this conversation as resolved.
Show resolved Hide resolved
rc.updateCapabilities(RemoteConfigCapabilities.ASM_NETWORK_FINGERPRINT, true)
rc.updateCapabilities(RemoteConfigCapabilities.ASM_HEADER_FINGERPRINT, true)

if (appsecConfig.rasp?.enabled) {
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SQLI, true)
Expand Down Expand Up @@ -104,6 +107,9 @@ function disableWafUpdate () {
rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_RULES, false)
rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_BLOCKING_RESPONSE, false)
rc.updateCapabilities(RemoteConfigCapabilities.ASM_TRUSTED_IPS, false)
rc.updateCapabilities(RemoteConfigCapabilities.ASM_ENDPOINT_FINGERPRINT, false)
rc.updateCapabilities(RemoteConfigCapabilities.ASM_NETWORK_FINGERPRINT, false)
rc.updateCapabilities(RemoteConfigCapabilities.ASM_HEADER_FINGERPRINT, false)

rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SQLI, false)
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SSRF, false)
Expand Down
17 changes: 12 additions & 5 deletions packages/dd-trace/src/appsec/reporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,11 @@ function reportAttack (attackData) {
rootSpan.addTags(newTags)
}

function reportSchemas (derivatives) {
function isFingerprintDerivative (derivative) {
return derivative.startsWith('_dd.appsec.fp')
}

function reportDerivatives (derivatives) {
if (!derivatives) return

const req = storage.getStore()?.req
Expand All @@ -162,9 +166,12 @@ function reportSchemas (derivatives) {
if (!rootSpan) return

const tags = {}
for (const [address, value] of Object.entries(derivatives)) {
const gzippedValue = zlib.gzipSync(JSON.stringify(value))
tags[address] = gzippedValue.toString('base64')
for (let [tag, value] of Object.entries(derivatives)) {
if (!isFingerprintDerivative(tag)) {
const gzippedValue = zlib.gzipSync(JSON.stringify(value))
value = gzippedValue.toString('base64')
}
tags[tag] = value
}

rootSpan.addTags(tags)
Expand Down Expand Up @@ -248,7 +255,7 @@ module.exports = {
reportMetrics,
reportAttack,
reportWafUpdate: incrementWafUpdatesMetric,
reportSchemas,
reportDerivatives,
finishRequest,
setRateLimit,
mapHeaderAndTags
Expand Down
5 changes: 5 additions & 0 deletions packages/dd-trace/src/appsec/sdk/track_event.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const { getRootSpan } = require('./utils')
const { MANUAL_KEEP } = require('../../../../../ext/tags')
const { setUserTags } = require('./set_user')
const standalone = require('../standalone')
const waf = require('../waf')

function trackUserLoginSuccessEvent (tracer, user, metadata) {
// TODO: better user check here and in _setUser() ?
Expand Down Expand Up @@ -76,6 +77,10 @@ function trackEvent (eventName, fields, sdkMethodName, rootSpan, mode) {
rootSpan.addTags(tags)

standalone.sample(rootSpan)

if (['users.login.success', 'users.login.failure'].includes(eventName)) {
waf.run({ persistent: { [`server.business_logic.${eventName}`]: null } })
}
}

module.exports = {
Expand Down
2 changes: 1 addition & 1 deletion packages/dd-trace/src/appsec/waf/waf_context_wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class WAFContextWrapper {
Reporter.reportAttack(JSON.stringify(result.events))
}

Reporter.reportSchemas(result.derivatives)
Reporter.reportDerivatives(result.derivatives)

if (wafRunFinished.hasSubscribers) {
wafRunFinished.publish({ payload })
Expand Down
204 changes: 204 additions & 0 deletions packages/dd-trace/test/appsec/attacker-fingerprinting-rules.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
{
"version": "2.2",
"metadata": {
"rules_version": "1.5.0"
},
"rules": [
{
"id": "tst-000-001-",
"name": "rule to test fingerprint",
"tags": {
"type": "attack_tool",
"category": "attack_attempt",
"confidence": "1"
},
"conditions": [
{
"parameters": {
"inputs": [
{
"address": "server.request.query"
}
],
"list": [
"testattack"
]
},
"operator": "phrase_match"
}
],
"transformers": []
}
],
"processors": [
{
"id": "http-endpoint-fingerprint",
"generator": "http_endpoint_fingerprint",
"conditions": [
{
"operator": "exists",
"parameters": {
"inputs": [
{
"address": "waf.context.event"
},
{
"address": "server.business_logic.users.login.failure"
},
{
"address": "server.business_logic.users.login.success"
}
]
}
}
],
"parameters": {
"mappings": [
{
"method": [
{
"address": "server.request.method"
}
],
"uri_raw": [
{
"address": "server.request.uri.raw"
}
],
"body": [
{
"address": "server.request.body"
}
],
"query": [
{
"address": "server.request.query"
}
],
"output": "_dd.appsec.fp.http.endpoint"
}
]
},
"evaluate": false,
"output": true
},
{
"id": "http-header-fingerprint",
"generator": "http_header_fingerprint",
"conditions": [
{
"operator": "exists",
"parameters": {
"inputs": [
{
"address": "waf.context.event"
},
{
"address": "server.business_logic.users.login.failure"
},
{
"address": "server.business_logic.users.login.success"
}
]
}
}
],
"parameters": {
"mappings": [
{
"headers": [
{
"address": "server.request.headers.no_cookies"
}
],
"output": "_dd.appsec.fp.http.header"
}
]
},
"evaluate": false,
"output": true
},
{
"id": "http-network-fingerprint",
"generator": "http_network_fingerprint",
"conditions": [
{
"operator": "exists",
"parameters": {
"inputs": [
{
"address": "waf.context.event"
},
{
"address": "server.business_logic.users.login.failure"
},
{
"address": "server.business_logic.users.login.success"
}
]
}
}
],
"parameters": {
"mappings": [
{
"headers": [
{
"address": "server.request.headers.no_cookies"
}
],
"output": "_dd.appsec.fp.http.network"
}
]
},
"evaluate": false,
"output": true
},
{
"id": "session-fingerprint",
"generator": "session_fingerprint",
"conditions": [
{
"operator": "exists",
"parameters": {
"inputs": [
{
"address": "waf.context.event"
},
{
"address": "server.business_logic.users.login.failure"
},
{
"address": "server.business_logic.users.login.success"
}
]
}
}
],
"parameters": {
"mappings": [
{
"cookies": [
{
"address": "server.request.cookies"
}
],
"session_id": [
{
"address": "usr.session_id"
}
],
"user_id": [
{
"address": "usr.id"
}
],
"output": "_dd.appsec.fp.session"
}
]
},
"evaluate": false,
"output": true
}
]
}
Loading
Loading