概要

サービスをインスツルメンテーションし、ユーザーのアクティビティを追跡することで、悪質なユーザーを検出・ブロックします。

認証されたユーザー情報をトレースに追加することで、認証された攻撃対象領域を狙う悪質なユーザーを特定し、ブロックすることができます。これを行うには、実行中の APM トレースにユーザー ID タグを設定し、ASM が認証済み攻撃者をブロックするために必要なインストルメンテーションを提供します。これにより、ASM は攻撃やビジネスロジックのイベントをユーザーに関連付けることができます。

ユーザーのログインとアクティビティを追跡し、すぐに使える検出ルールでアカウントの乗っ取りやビジネスロジックの乱用を検出し、最終的に攻撃者をブロックすることができます。

ユーザーアクティビティの自動検出: Datadog トレーシングライブラリは、ユーザーアクティビティイベントを自動的に検出してレポートしようとします。詳細については、ユーザーアクティビティイベントの自動追跡を無効にするを参照してください。

すぐに使える検出ルールとして、以下のようなカスタムユーザーアクティビティがあります。

内蔵のイベント名必要なメタデータ関連ルール
activity.sensitive{ "name": "coupon_use", "required_role": "user" }IP からのレート制限アクティビティ
不正なアクティビティの検出
users.login.successユーザー ID は必須で、オプションでメタデータを追加できますクレデンシャルスタッフィング攻撃
users.login.failureユーザー ID は必須で、オプションでメタデータを追加できますクレデンシャルスタッフィング攻撃
users.signup{ "usr.id": "12345" }IP からの過剰なアカウント作成
users.delete{ "usr.id": "12345" }IP からの過剰なアカウント削除
users.password_reset{ "usr.id": "12345", "exists": true }パスワードリセットのブルートフォース試行
payment.attempt{ "status": "failed" }IP からの過剰な支払い失敗

認証されたユーザー情報をトレースに追加し、ユーザーブロック機能を有効にする

ルートスパンにカスタムタグを追加する方法と、後述のインスツルメンテーション関数を利用する方法があります。

ルートスパンにカスタムタグを追加するための Java トレーサーの API を使用し、アプリケーションで認証されたリクエストを監視できるように、ユーザー情報を追加します。

ユーザーモニタリングタグは、ルートスパンに適用され、プレフィックス usr の後にフィールド名が続きます。例えば、usr.name は、ユーザーの名前を追跡するユーザーモニタリングタグです。

: アプリケーションに必要な依存関係が追加されていることを確認してください。

以下の例では、ルートスパンを取得し、関連するユーザー監視タグを追加し、ユーザーブロック機能を有効にする方法を示しています。

import io.opentracing.Span;
import io.opentracing.util.GlobalTracer;
import datadog.appsec.api.blocking.Blocking;
import datadog.trace.api.interceptor.MutableSpan;

// アクティブスパンの取得
final Span span = GlobalTracer.get().activeSpan();
if ((span instanceof MutableSpan)) {
   MutableSpan localRootSpan = ((MutableSpan) span).getLocalRootSpan();
   // 必須ユーザー ID タグの設定
   localRootSpan.setTag("usr.id", "d131dd02c56eec4");
   // オプションのユーザーモニタリングタグの設定
   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();

.NET トレーサーパッケージは SetUser() 関数を提供し、トレースにユーザー情報を追加することで認証されたリクエストを監視できるようにします。

以下の例では、関連するユーザー監視タグを追加し、ユーザーブロック機能を有効にする方法を示しています。


using Datadog.Trace;

// ...

    var userDetails = new UserDetails()
    {
        // ユーザーに対するシステム内部識別子
        Id = "d41452f2-483d-4082-8728-171a3570e930",
        // ユーザーのメールアドレス
        Email = "test@adventure-works.com",
        // システムによって表示されるユーザー名
        Name = "Jane Doh",
        // ユーザーのセッション ID
        SessionId = "d0632156-132b-4baa-95b2-a492c5f9cb16",
        // ユーザーがリクエストしたロール
        Role = "standard",
    };
    Tracer.Instance.ActiveScope?.Span.SetUser(userDetails);

情報およびオプションについては、.NET トレーサーのドキュメントをお読みください。

Go トレーサーパッケージは SetUser() 関数を提供し、トレースにユーザー情報を追加することで認証されたリクエストを監視できるようにします。他のオプションについては、Go トレーサーのドキュメントをご覧ください。

この例では、現在のトレーサースパンを取得し、それを使用してユーザー監視タグを設定し、ユーザーブロック機能を有効にする方法を説明します。

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 {
    // 早急にリクエストハンドラーを中止して、ユーザーをブロックする必要があります。
    // ブロック応答は、appsec ミドルウェアによって自動的に処理され、送信されます。
    return 
  }
}

以下の API のいずれかを使用して、トレースにユーザー情報を追加し、アプリケーションで認証されたリクエストを監視できるようにします。

ddtrace 1.1.0 からは、Datadog::Kit::Identity.set_user メソッドが使用できるようになりました。これは、トレースにユーザ情報を追加するための推奨 API です。

# アクティブトレースを取得する
trace = Datadog::Tracing.active_trace

# 必須ユーザー ID タグを設定する
Datadog::Kit::Identity.set_user(trace, id: 'd131dd02c56eeec4')

# または、オプションのユーザーモニタリングタグを設定する
Datadog::Kit::Identity.set_user(
  trace,

  # 必須 ID
  id: 'd131dd02c56eeec4',

  #セマティクスが分かっているオプションタグ
  name: 'Jean Example',
  email:, 'jean.example@example.com',
  session_id:, '987654321',
  role: 'admin',
  scope: 'read:message, write:files',

  # オプションの自由形式タグ
  another_tag: 'another_value',
)

Datadog::Kit::Identity.set_user がニーズに合わない場合は、代わりに set_tag を使用することができます。

ユーザーモニタリングタグは、トレースに適用され、プレフィックス usr. の後にフィールド名が続きます。例えば、usr.name は、ユーザーの名前を追跡するユーザーモニタリングタグです。

以下の例では、アクティブトレースを取得し、関連するユーザーモニタリングタグを追加する方法を示しています。

:

  • タグの値は文字列でなければなりません。
  • usr.id タグは必須です。
# アクティブトレースを取得する
trace = Datadog::Tracing.active_trace

# 必須ユーザー ID タグを設定する
trace.set_tag('usr.id', 'd131dd02c56eeec4')

# セマティクスが分かっているユーザーモニタリングタグをオプションで設定する
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')

# 自由形式のタグを設定する
trace.set_tag('usr.another_tag', 'another_value')

PHP トレーサーは \DDTrace\set_user() 関数を提供し、認証されたリクエストを監視したりブロックしたりすることができます。

\DDTrace\set_user() はトレースに関連するユーザータグとメタデータを追加し、ユーザーブロックを自動的に実行します。

以下の例では、ユーザー監視タグを設定し、ユーザーブロックを有効にする方法を示します。

<?php
// ブロッキングは、set_user コールにより内部で行われます。
\DDTrace\set_user(
    // ユーザーの一意な識別子が必要です。
    '123456789',

    // その他のフィールドはすべてオプションです。
    [
        'name' =>  'Jean Example',
        'email' => 'jean.example@example.com',
        'session_id' => '987654321',
        'role' => 'admin',
        'scope' => 'read:message, write:files',
    ]
);
?>

Node トレーサーパッケージは tracer.setUser(user) 関数を提供し、トレースにユーザー情報を追加することで認証されたリクエストを監視できるようにします。

以下の例では、関連するユーザー監視タグを追加し、ユーザーブロック機能を有効にする方法を示しています。

const tracer = require('dd-trace').init()

function handle () {
  tracer.setUser({
    id: '123456789', // *必須* ユーザーの一意な識別子。

    // その他のフィールドはすべてオプションです。
    email: 'jane.doe@example.com', // ユーザーのメールアドレス。
    name: 'Jane Doe', // ユーザーのユーザーフレンドリーな名前。
    session_id: '987654321', // ユーザーのセッション ID。
    role: 'admin', // ユーザーがリクエストしたロール。
    scope: 'read:message, write:files', // ユーザーが現在持っているスコープまたは付与された権限。

    // ユーザーへのカスタムデータ (RBAC、Oauth など) をアタッチするために、任意のフィールドも受け付けます
    custom_tag: 'custom data'
  })

// 現在認証されているユーザーを設定し、ブロックされているかどうかを確認します
if (tracer.appsec.isUserBlocked(user)) {  // また、現在認証されているユーザーを設定します
  return tracer.appsec.blockRequest(req, res) // ブロック応答が送信されます
  }

}

情報およびオプションについては、Node.js トレーサーのドキュメントをお読みください。

Python トレーサーパッケージが提供する set_user 関数を用いて、トレースにユーザー情報を追加することで、認証済みリクエストを監視します。

この例では、ユーザー監視タグを設定し、ユーザーブロック機能を有効にする方法を示します。

from ddtrace.contrib.trace_utils import set_user
from ddtrace import tracer
# set_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)

ビジネスロジック情報 (ログイン成功、ログイン失敗、任意のビジネスロジック) のトレースへの追加

dd-trace-java v1.8.0 からは、Java トレーサーの API を使用してユーザーイベントを追跡することができます。

次の例は、ログインイベントやカスタムイベント (サインアップを例とする) を追跡する方法を示しています。

import datadog.trace.api.EventTracker;
import datadog.trace.api.GlobalTracer;

public class LoginController {

    private User doLogin(String userId, String password) {
        // ここで、userId/password の資格情報に基づいた User を取得します
        User user = checkLogin(userId, password);

        Map<String, String> metadata = new HashMap<>();
        metadata.put("email", user.getEmail());

        // ユーザー認証の成功イベントを追跡します
        GlobalTracer
            .getEventTracker()
            .trackLoginSuccessEvent(user.getId(), metadata);

    }
}
import datadog.trace.api.EventTracker;
import datadog.trace.api.GlobalTracer;

public class LoginController {

    private User doLogin(String userId, String password) {
        // ここで、userId/password の資格情報に基づいた User を取得します
        User user = checkLogin(userId, password);

        // 関数が null を返した場合 - ユーザーは存在しません
        boolean userExists = (user != null);
        Map<String, String> metadata = new HashMap<>();
        if (userExists != null) {
            metadata.put("email", user.getEmail());
        }

        // ユーザー認証のエラーイベントを追跡します
        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) {
        // ここで、ユーザーアカウントを作成します
        User user = createUser(userId, email);

        Map<String, String> metadata = new HashMap<>();
        metadata.put("email", user.getEmail());
        metadata.put("id", user.getId());

        // ユーザーサインアップイベントを追跡します
        GlobalTracer
            .getEventTracker()
            .trackCustomEvent("users.signup", metadata);
    }
}

dd-trace-dotnet v2.23.0 からは、.NET トレーサーの API を使用してユーザーイベントを追跡することができます。

次の例は、ログインイベントやカスタムイベント (サインアップを例とする) を追跡する方法を示しています。

using Datadog.Trace.AppSec;

void OnLogonSuccess(string userId, ...)
{
    // metadata はオプションです
    var metadata = new Dictionary<string, string>()
    {
        { "customKey", "customValue" }
    };
    EventTrackingSdk.TrackUserLoginSuccessEvent(userId, metadata);

    // ...
}
using Datadog.Trace.AppSec;

void OnLogonFailure(string userId, bool userExists, ...)
{
    // metadata はオプションです
    var metadata = new Dictionary<string, string>()
    {
        { "customKey", "customValue" }
    };
    EventTrackingSdk.TrackUserLoginFailureEvent(userId, userExists, metadata);

    // ...
}
void OnUserSignupComplete(string userId, ...)
{
    // metadata パラメーターはオプションですが、"usr.id" を追加します
    var metadata = new Dictionary<string, string>()
    {
        { "usr.id", userId }
    };
    // カスタムビジネスロジックの追跡を活用し、ユーザーのサインアップを追跡します
    EventTrackingSdk.TrackCustomEvent("users.signup", metadata);

    // ...
}

dd-trace-go v1.47.0 からは、Go トレーサーの API を使用してユーザーイベントを追跡することができます。

次の例は、ログインイベントやカスタムイベント (サインアップを例とする) を追跡する方法を示しています。

import "gopkg.in/DataDog/dd-trace-go.v1/appsec"

func handler(w http.ResponseWriter, r *http.Request) {
  metadata := /* オプションの追加イベントメタデータ */
  userdata := /* オプションの追加ユーザーデータ */

  // ログイン成功を追跡します
  if appsec.TrackUserLoginSuccessEvent(r.Context(), "my-uid", metadata, userdata) != nil {
    // 指定されたユーザー ID はブロックされているため、速やかにハンドラーを中止する必要があります。
    // ブロック応答は、appsec ミドルウェアから送信されます。
    return
  }
}
import "gopkg.in/DataDog/dd-trace-go.v1/appsec"

func handler(w http.ResponseWriter, r *http.Request) {
  exists := /* 指定されたユーザー ID が存在するかどうか */
  metadata := /* オプションの追加イベントメタデータ */ 
  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"}

  // カスタムビジネスロジックの追跡を活用し、ユーザーのサインアップを追跡します
  appsec.TrackCustomEvent(r.Context(), "users.signup", metadata)
}

dd-trace-rb v1.9.0 からは、Ruby トレーサーの API を使用してユーザーイベントを追跡することができます。

次の例は、ログインイベントやカスタムイベント (サインアップを例とする) を追跡する方法を示しています。

ログインの成功/失敗イベントを含むトレースは、以下のクエリ @appsec.security_activity:business_logic.users.login.success または @appsec.security_activity:business_logic.users.login.failure を使用してクエリすることができます。

require 'datadog/kit/appsec/events'

trace = Datadog::Tracing.active_trace
Datadog::Kit::AppSec::Events.track_login_success(trace, user: { id: 'my_user_id' })
require 'datadog/kit/appsec/events'
trace = Datadog::Tracing.active_trace

# ユーザー ID が存在する場合
Datadog::Kit::AppSec::Events.track_login_failure(trace, user_id: 'my_user_id', user_exists: true)

# ユーザー ID が存在しない場合
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

# カスタムビジネスロジックの追跡を活用し、ユーザーのサインアップを追跡します
Datadog::Kit::AppSec::Events.track('users.signup', trace)

dd-trace-php v0.84.0 からは、PHP トレーサーの API を使用してユーザーイベントを追跡することができます。

次の例は、ログインイベントやカスタムイベント (サインアップを例とする) を追跡する方法を示しています。

<?php
\datadog\appsec\track_user_login_success_event($id, ['email' => $email])
?>
<?php
\datadog\appsec\track_user_login_failure_event($id, $exists, ['email' => $email])
?>
<?php
\datadog\appsec\track_custom_event('users.signup', ['id' => $id, 'email' => $email]);
?>

dd-trace-js v3.13.1 からは、NodeJS トレーサーの API を使用してユーザーイベントを追跡することができます。

次の例は、ログインイベントやカスタムイベント (サインアップを例とする) を追跡する方法を示しています。

const tracer = require('dd-trace')

// コントローラーで
const user = {
  id: 'user-id', // id は必須です
  email: 'user@email.com' // その他のフィールドはオプションです
}
const metadata = { custom: 'value' } // 任意フィールドを持つオプションのメタデータ

// ユーザー認証に成功したイベントのログ
tracer.appsec.trackUserLoginSuccessEvent(user, metadata) // metadata はオプションです
const tracer = require('dd-trace')

// コントローラーで
const userId = 'user-id'
const userExists = true // ユーザーログインがデータベースに存在する場合、例えば
const metadata = { custom: 'value' } // 任意フィールドを持つオプションのメタデータ

// metadata はオプションです
tracer.appsec.trackUserLoginFailureEvent(userId, userExists, metadata)
const tracer = require('dd-trace')

// コントローラーで
const eventName = 'users.signup'
const metadata = { 'usr.id': 'user-id' }

tracer.appsec.trackCustomEvent(eventName, metadata)

dd-trace-py v1.9.0 からは、Python トレーサーの API を使用してユーザーイベントを追跡することができます。

次の例は、ログインイベントやカスタムイベント (サインアップを例とする) を追跡する方法を示しています。

from ddtrace.appsec.trace_utils import track_user_login_success_event
from ddtrace import tracer
metadata = {"custom": "customvalue"}
# name、email、scope、role、session_id、propagate はオプションの引数で、
# デフォルトは None ですが propagate はデフォルトが True になります。
# これらは set_user() 関数に渡されます
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 は、ログインに失敗したユーザーがシステムに存在するかどうかを示します
exists = False
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": "12345"}
event_name = "users.signup"
track_custom_event(tracer, event_name, metadata)

コードを変更せずにビジネスロジック情報を追跡する

サービスで ASM が有効になっており、リモート構成が有効になっている場合、カスタムのビジネスロジックタグと一致するリクエストにフラグを立てるカスタム WAF ルールを作成することができます。この場合、アプリケーションを変更する必要はなく、すべて Datadog から行うことができます。

まず、Custom WAF Rule ページに移動し、“Create New Rule” をクリックします。

ASM ホームページから Protection、In-App WAF、Custom Rules の順にクリックして、Custom WAF Rule メニューにアクセス

カスタム WAF ルールを定義するためのメニューが開きます。“Business Logic” カテゴリーを選択すると、イベントタイプ (例: users.password_reset) を構成できるようになります。次に、追跡したいサービスと特定のエンドポイントを選択します。また、ルール条件を使用して特定のパラメーターをターゲットにし、_インスツルメント_したいコードフローを特定することもできます。条件が一致すると、ライブラリがトレースにタグを付け、それを ASM に転送するフラグを立てます。条件が不要な場合は、すべてに一致する大まかな条件を設定することもできます。

Create New Rule ボタンをクリックした際に表示されるフォームのスクリーンショット

ルールが保存されると、リモート構成が有効になっているサービスのインスタンスにデプロイされます。

ユーザーアクティビティイベントの自動追跡

ASM を有効にすると、最近の Datadog トレーシングライブラリは、ユーザーアクティビティイベントの自動検出を試みます。

自動検出できるイベントは以下の通りです。

  • users.login.success
  • users.login.failure
  • users.signup

ユーザーアクティビティイベント自動追跡モード

ユーザーアクティビティの自動追跡には、safe モードと extended モードの 2 種類があります

safe モードでは、トレースライブラリはイベントのメタデータに PII 情報を含めません。トレーサーライブラリはユーザー ID の収集を試みますが、ユーザー ID が有効な GUID である場合のみです

extended モードでは、トレースライブラリはユーザー ID とユーザーのメールアドレスを収集しようとします。このモードでは、ユーザー ID のタイプが GUID であるかどうかをチェックしません。トレースライブラリは、イベントから抽出できる値であれば何でもレポートします。

ユーザーイベント自動追跡モードを構成するには、環境変数 DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKINGsafe または extended に設定します。デフォルトでは、トレーサーライブラリは safe モードを使用します。

: トレースライブラリがユーザーイベントから情報を抽出できない場合があります。イベントは空のメタデータでレポートされます。そのような場合は、SDK を使用して、ユーザーイベントを手動でインスツルメンテーションすることをお勧めします。

ユーザーアクティビティイベントの自動追跡を無効にする

これらのイベントの検出を無効にするには、環境変数 DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKINGdisabled に設定します。これは、Datadog Agent ではなく、Datadog トレーシングライブラリをホストするアプリケーションで設定する必要があります。

その他の参考資料