# GuardDuty RDS Protection and Lambda Protection: Detecting DB Login Anomalies and Serverless Network Threats Agentlessly, with Zero Infrastructure Change

> An implementation guide for designing Amazon GuardDuty's RDS Protection and Lambda Protection for production. Directly monitor Aurora/RDS login anomalies (success, failure, brute force, malicious IPs, Tor), and detect crypto mining / C&C communication from the network activity of all Lambda functions (including non-VPC) — both agentless, zero infrastructure change, no code change. Explained per the official docs: supported engines, finding types, the up-to-two-week learning period, pricing and the 30-day free trial, Terraform enablement, and EventBridge automated response.

- Published: 2026-06-27
- Author: 友田 陽大
- Tags: セキュリティ, AWS, GuardDuty, RDS, Lambda
- URL: https://tomodahinata.com/en/blog/aws-guardduty-rds-lambda-protection-database-login-network-threats-guide
- Category: Amazon GuardDuty in production
- Pillar guide: https://tomodahinata.com/en/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide

## Key points

- RDS Protection monitors Aurora/RDS 'login activity' directly and agentlessly from the AWS side. No additional infrastructure, no impact on DB performance. But new enablement / new instances take up to two weeks for 'baseline learning,' during which no anomaly findings appear
- Lambda Protection analyzes 'the network activity logs of all Lambda functions (including functions that don't use VPC).' You don't need to enable VPC Flow Logs yourself for the ENIs of VPC-configured functions. Lambda@Edge is out of scope
- RDS has 9 finding types (AnomalousBehavior family, MaliciousIPCaller family, TorIPCaller family). Lambda has 7 (C&CActivity, BitcoinTool, BlackholeTraffic, DropPoint, MaliciousIPCaller.Custom, TorClient, TorRelay). You can read the substance of the threat from the type name
- Both are detection layers you can add with 'zero code change, zero infrastructure change.' Enable RDS 'when you use a supported Aurora/RDS,' Lambda 'when you have functions that communicate outbound.' The decision is asset-based (YAGNI)
- Detection is the starting point. MTTR shrinks only once you connect EventBridge → idempotent automated response (credential rotation, SG restriction, function disablement). Both plans have a 30-day free trial so you can know the bill in advance

---

"Can we even notice suspicious logins to the DB, or a Lambda communicating with the outside on its own? — but we don't want to install agents or rebuild our logging platform." This is a consultation I often get from teams running serverless- or managed-DB-centric configurations.

The answer is "**you can detect it without touching infrastructure at all, without changing code, and without installing an agent.**" That's GuardDuty's two protection plans, **RDS Protection** and **Lambda Protection**. What they have in common is being **completely agentless and requiring no additional infrastructure**. RDS Protection reads Aurora/RDS **login activity** directly from the AWS side, and Lambda Protection analyzes the **network activity of all Lambda functions**. You don't need to plant audit extensions in the DB, nor stretch VPC Flow Logs over the Lambda ENIs.

This article is an implementation guide for designing and operating these two "**zero infrastructure change, agentless**" protection plans at **production quality**. I leave GuardDuty's overall design (foundational detection, org governance, EventBridge automated response) to the [pillar article](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide), and concentrate here on **digging one level deeper into the pillar's two lines, "RDS Protection" and "Lambda Protection" — into supported engines, finding types, the learning period, cost, and automated response**. I implemented IAM, observability, and DR across a [serverless payment platform](/case-studies/payment-platform-reliability) on multi-account AWS and have [operated Lambda in production](/blog/aws-lambda-production-guide) — from that perspective, I'll talk about "**how to design and implement these two layers on your DBs and functions.**"

> **The rule of this article**: The specs, supported engines, finding types / severities, and pricing model are based on the **AWS official documentation (as of June 2026)**. Because the official compatibility table for supported database engine **versions** is updated from time to time, the body doesn't assert specific versions but **shows the verification procedure** — always check the latest official table before going to production. And another premise — **GuardDuty is "detection," not "defense."** Neither RDS Protection nor Lambda Protection **replaces** least-privilege IAM, IAM database authentication, encryption, network isolation, or input validation. These play the "layer that quickly signals indications of compromise" within defense-in-depth. And "agentless" does not mean "free" — both are **usage-based billing** (pricing in Section 7).

---

## 0. Mental model: two "zero infrastructure change" detection layers

Before starting the design, let me pin down, one line each, what these two plans are and are not.

> **RDS Protection = a protection plan that ingests Aurora/RDS "login activity (success, failure)" directly and agentlessly from the AWS side and detects anomalous logins with ML.**
>
> **Lambda Protection = a protection plan that analyzes "the network activity logs of all Lambda functions in the account (including functions that don't use VPC)" and detects communication indicating a function's compromise, such as communication with malicious hosts or crypto mining.**

From here, **three common consequences** that bundle these two emerge. These are the foundation of the design decisions.

1. **Both are agentless and require no infrastructure change.** RDS Protection is one where "GuardDuty monitors login activity **directly** from the Aurora/RDS service," and you don't need to add audit-log extensions to the DB or worry about performance tuning (official: *"RDS Protection doesn't require additional infrastructure; it is designed so as not to affect the performance of your database instances."*). Lambda Protection is the same — **you don't need to enable VPC Flow Logs yourself for the ENIs of VPC-configured functions**. You also **don't** need to distribute agents to workloads like [Runtime Monitoring](/blog/aws-guardduty-runtime-monitoring-eks-ecs-fargate-ec2-guide).
2. **For both, "the log itself doesn't come to you."** GuardDuty analyzes login activity / network activity internally and **does not make it available in your account** (RDS: *"GuardDuty doesn't ... make RDS login activity available to you."* / Lambda: *"GuardDuty doesn't manage your Lambda network activity logs ..., or make them accessible in your account."*). "Storing and billing for logs for detection" is GuardDuty's own responsibility, independent of your log configuration.
3. **A finding is a "starting point," not an "endpoint."** Both generate structured findings, and they only get into operation once you receive them with EventBridge and connect them to automated response. **The climax of this article is Section 6 (EventBridge → idempotent automated response).**

Grasp these three points, and you'll see that introducing the two plans is a design of **"① is there a supported asset (the enablement decision) → ② how do you read the finding types → ③ how do you turn them into action (automated response)."** Let's build them in order.

---

## 1. RDS Protection: what it monitors, and how

### 1.1 What it monitors is only "login activity" — not query content

First, let me clear up a misconception. What RDS Protection looks at is **login activity to the DB (success/failure authentication events)**, **not the content of SQL queries or table reads/writes**. The official definition is — *"RDS Protection in Amazon GuardDuty analyzes and profiles RDS login activity for potential access threats."*

That is, the aim is **anomaly detection of "who tried to log in, from where, and how."** Concretely:

- **An unknown external actor succeeded in logging in for the first time** (a user / IP never seen before).
- **An attempt at password brute-forcing**.
- **Login attempts from known malicious IPs or Tor exit nodes**.

RDS login activity captures **both successful and failed login attempts** (*"RDS login activity captures both successful and failed login attempts."*). GuardDuty's ML model learns factors like "the requesting user, the location of the connection source, the connection details" and detects behavior that deviates from the baseline as anomalous.

### 1.2 Supported engines (verify versions against the official compatibility table)

The **engines** RDS Protection supports are, per the official compatibility table, as follows.

| Supported engine | Note |
| --- | --- |
| **Aurora MySQL-Compatible Edition** | Aurora's MySQL-compatible |
| **Aurora PostgreSQL-Compatible Edition** | Aurora's PostgreSQL-compatible |
| **RDS for PostgreSQL** | For read replicas, the condition is "the primary is a supported version" |
| **RDS for MySQL** | — |
| **RDS for MariaDB** | — |
| **Aurora PostgreSQL Limitless Database** | Accounts with RDS Protection already enabled are added to monitoring **automatically** |

> **I don't assert versions (per the official docs)**: the above is a list of **engine types**. The **supported versions** of each engine are managed in the official documentation's "Supported Amazon Aurora, Amazon RDS, and Aurora Limitless databases" table and are updated from time to time. Listing individual versions in this article would make it stale, so **always check the official compatibility table before going to production**. Note that an **RDS for PostgreSQL read replica** presupposes that the primary is a supported version and is replicating normally from the primary.

> **A note on Limitless Database (a billing pitfall)**: in accounts that already have RDS Protection enabled, **Aurora PostgreSQL Limitless Database is added to monitoring automatically**. If RDS Protection's 30-day free trial has already ended, **usage charges for the Limitless Database monitoring are incurred immediately**. To avoid "the billing target grew before I knew it," check the billing impact when you adopt Limitless.

### 1.3 The "up to two weeks" learning period — this is an operational pitfall

This is the **fact you must always factor into the design**, specific to RDS Protection. When you **enable RDS Protection for the first time**, or for a **newly created DB instance**, there is **a period of learning the baseline of normal behavior**. The official original text:

> *When you enable RDS Protection for the first time or you have a newly created database instance, there is a learning period to baseline normal behavior. For this reason, newly enabled or newly created database instances may not have an associated anomalous login finding for up to two weeks.*

That is — **immediately after enablement or immediately after creating a new instance, no anomalous-login finding may appear for up to two weeks.** This is not a defect but a spec. It's because the ML needs time to build the baseline of "it's normal for this user to enter from this IP with these connection details."

Two design implications follow from here.

- **"I enabled it but no finding comes = safe" is not necessarily true.** It may be in the learning period. You should record "the date you enabled it" and "the date it stabilizes as a monitoring target (about two weeks later)" in your operational documentation.
- **The `MaliciousIPCaller` / `TorIPCaller` families don't wait for learning.** These fire not from ML anomaly detection (the `AnomalousBehavior` family) but from **matching against known malicious-IP / Tor-exit-node lists**, so they don't depend on baseline learning. That is, **even right after enablement, "a login from a malicious IP" can be detected.** It's accurate to understand the distinction that the learning period applies to `AnomalousBehavior.*` (ML-based).

### 1.4 Information contained in a finding

An RDS Protection finding contains **details of the potentially compromised DB**. The **Resource Type** is `RDSDBInstance` (ordinary Aurora/RDS) or `RDSLimitlessDB` (Limitless Database). An ML-based finding carries "RDS login activity-based anomalies" that deviate from the baseline — which user logged in, from where, with what connection details — so in triage you can read "whose access was anomalous." This becomes the material for deciding, in automated response, "whose DB user's credentials should be rotated."

---

## 2. RDS Protection finding types: deciphering the 9

RDS Protection has **9** finding types. The type-name structure is GuardDuty's common `ThreatPurpose:ResourceType/Family.Mechanism`, and the excellent point is that **just by looking at the type name, you understand "what happened"** (for the details of the type format, see [Section 5 of the pillar article](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide)).

| Finding type | Default severity | What it detects |
| --- | --- | --- |
| `CredentialAccess:RDS/AnomalousBehavior.SuccessfulLogin` | **Variable** (Low/Medium/High) | A **successful** login the ML judged "anomalous" (a first login from an unknown user / IP, etc.) |
| `CredentialAccess:RDS/AnomalousBehavior.FailedLogin` | **Low** | A **failed** login the ML judged "anomalous" (may suggest a brute-force attempt) |
| `CredentialAccess:RDS/AnomalousBehavior.SuccessfulBruteForce` | **High** | A success **after** a series of anomalous failed logins — suspected successful brute force |
| `CredentialAccess:RDS/MaliciousIPCaller.SuccessfulLogin` | **High** | A **successful** login from a **known malicious IP** |
| `CredentialAccess:RDS/MaliciousIPCaller.FailedLogin` | **Medium** | A **failed** login from a **known malicious IP** |
| `Discovery:RDS/MaliciousIPCaller` | **Medium** | A known malicious IP **probed** the DB (no login attempt = reconnaissance) |
| `CredentialAccess:RDS/TorIPCaller.SuccessfulLogin` | **High** | A **successful** login from a **Tor exit node** (intent to conceal identity) |
| `CredentialAccess:RDS/TorIPCaller.FailedLogin` | **Medium** | A **failed** login from a **Tor exit node** |
| `Discovery:RDS/TorIPCaller` | **Medium** | A Tor exit node **probed** the DB (no login attempt) |

Let me organize the key points of deciphering.

- **`AnomalousBehavior.*` is ML-based, `MaliciousIPCaller.*` / `TorIPCaller.*` are threat-intelligence-based.** The former depends on the **up-to-two-week learning** mentioned in 1.3, while the latter is matching against known lists, so no learning is needed — this distinction decides which detections work right after enablement.
- **Severity differs between `SuccessfulLogin` (success), `FailedLogin` (failure), and `Discovery` (probe only).** A successful login (especially from a malicious IP / Tor / successful brute force) is **High**, and failures and probes are **Medium / Low**. The automated-response threshold can be cut by this number (Section 6).
- **Only `AnomalousBehavior.SuccessfulLogin` is Variable.** Even for the same type, if the login source is a private IP it's **Low**, a public IP **Medium**, and accompanied by a pattern of consecutive failures **High** — the severity changes with context. "Don't treat it uniformly by type name alone; judge by the `severity` number" is the correct design.

> **The standard response (based on the official remediation)**: when a successful-login type (`SuccessfulLogin` / `SuccessfulBruteForce`) appears — **① rotate the password of the relevant DB user**, and **② check that user's operations in the audit log**. When a failure/probe type appears, **③ place the DB in a private VPC and narrow the security group to only the necessary sources** (rectify a public state / overly lax access policy). As a root countermeasure, the royal road is to **prioritize [IAM database authentication](/blog/aws-lambda-rds-aurora-connection-management-rds-proxy-vpc-guide) over password authentication** and reduce the long-lived passwords that can be leaked in the first place.

---

## 3. Lambda Protection: looking at the network activity of all functions

### 3.1 Monitoring "including functions that don't use VPC"

The core of Lambda Protection is **the breadth of its monitoring scope**. The official definition:

> *When you enable Lambda Protection, GuardDuty starts monitoring Lambda network activity logs. This includes VPC Flow Logs from all Lambda functions for your account (including those logs that don't use VPC networking) and logs that get generated when Lambda function gets invoked.*

What's decisive here is **"all Lambda functions ... (including those logs that don't use VPC networking)"** — **it monitors the network activity of all Lambda functions in the account, including functions that don't use VPC networking**. The intuition "aren't Lambdas not placed in a VPC out of scope?" is **wrong** — GuardDuty looks at network activity regardless of a function's VPC configuration.

And what's important from the operational-load standpoint — **you don't need to enable VPC Flow Logs yourself for the ENIs (the Elastic Network Interfaces Lambda creates) of VPC-configured functions**:

> *For Lambda functions that are configured to use VPC networking, you don't need to enable VPC flow logs for the elastic network interfaces (ENI) created by Lambda for GuardDuty.*

That is, **the work of "building a flow-log platform yourself" is zero.** GuardDuty internally ingests the necessary network activity logs.

### 3.2 What it detects — the communication of "compromised function code"

What Lambda Protection looks for is **"suspicious network communication that indicates malicious code is lurking inside the function."** Official: *"When GuardDuty identifies suspicious network traffic that is indicative of the presence of a potentially malicious piece of code in your Lambda function ..."*

The concrete scenario is this — a dependency package tainted by a supply-chain attack, function code rewritten via leaked credentials, and the like **begin communicating with external malicious hosts**. Lambda Protection captures that communication:

- **Crypto mining**: an attacker hijacks the function and **repurposes** it for mining Bitcoin, etc. (`CryptoCurrency:Lambda/BitcoinTool.B`).
- **Communication with a C&C (command-and-control) server**: receiving commands from outside as a member of a botnet (`Backdoor:Lambda/C&CActivity.B`).
- **Communication with malicious hosts / Tor**: a destination for stolen credentials (DropPoint), a blackhole, a Tor relay / client, and so on.

### 3.3 Lambda@Edge is out of scope (an important exclusion)

There's an exclusion you must always grasp at design time — **the logs of Lambda@Edge functions are not included in Lambda Protection's scope**. Official: *"Lambda Network Activity Monitoring doesn't include the logs for Lambda@Edge functions."*

If you use Lambda@Edge running at CloudFront's edge, recognize that **it is not covered by Lambda Protection**. You need to think about edge protection at a different layer like [WAF](/blog/waf-defense-in-depth-aws-waf-cloud-armor-owasp-guide).

### 3.4 Information contained in a finding

A Lambda Protection finding's **Resource Type** is `Lambda`. The finding contains **the name of the Lambda function that was communicating** and **the destination IP** (plus threat-list details). This becomes the material for deciding, in automated response, "which function to isolate/disable" and "which dependency to suspect."

---

## 4. Lambda Protection finding types: deciphering the 7

Lambda Protection has **7** finding types. All have `resourceType` `Lambda`, and being able to identify them at a glance by the resource segment being `Lambda`.

| Finding type | Default severity | What it detects |
| --- | --- | --- |
| `Backdoor:Lambda/C&CActivity.B` | **High** | The function is querying the IP of a **known C&C server** (suspected member of a botnet) |
| `CryptoCurrency:Lambda/BitcoinTool.B` | **High** | The function is querying a **crypto-related IP** (suspected repurposing for mining) |
| `Trojan:Lambda/BlackholeTraffic` | **Medium** | The function is trying to communicate with a **blackhole (sinkhole) IP** |
| `Trojan:Lambda/DropPoint` | **Medium** | The function is trying to communicate with **a host where malware aggregates stolen credentials and the like** |
| `UnauthorizedAccess:Lambda/MaliciousIPCaller.Custom` | **Medium** | The function is communicating with an IP on **a threat list you uploaded** |
| `UnauthorizedAccess:Lambda/TorClient` | **High** | The function connected to a **Tor Guard / Authority node** (suspected turning into a Tor client) |
| `UnauthorizedAccess:Lambda/TorRelay` | **High** | The function is behaving as a **Tor relay** |

Key points of deciphering:

- **The meaning of `.Custom`**: the `.Custom` in `UnauthorizedAccess:Lambda/MaliciousIPCaller.Custom` indicates a detection mechanism of "**a match against a threat list you uploaded yourself**" (GuardDuty's threat-list feature; for details, see [Section 7 of the pillar article](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide)). Register IPs you judged "this is malicious" in-house, and you can detect the moment a Lambda communicates with them.
- **Bipolarization of severity**: C&C, crypto, and Tor families are **High**, and blackhole, DropPoint, and custom-list families are **Medium**. **High is a signal you should act on largely on the premise that "the function is compromised."**
- **`BitcoinTool.B` may have a legitimate exception**: if the function is **legitimately** doing blockchain-related processing (mining, crypto-asset management), this becomes a false positive. In that case, the official docs recommend creating a [Suppression Rule](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide) with the two conditions "**finding type = `CryptoCurrency:Lambda/BitcoinTool.B`** + **the target function name**."

> **The standard response (based on the official remediation)**: if a Lambda finding is **unexpected**, the official docs recommend "**responding on the assumption that the function has been compromised**" (*"assume that Lambda has been potentially compromised"*). Concretely — **① temporarily disable the function / narrow concurrency to 0 to stop communication**, **② review the function code and dependency packages** (suspect supply-chain taint), **③ rotate, on the premise of leakage, the credentials and environment-variable secrets the function held**. This is consistent with the "least-privilege execution role × minimization of secrets" I emphasize in [Lambda production operation](/blog/aws-lambda-production-guide) — a design that **reduces from the start what can be stolen** in a compromise.

---

## 5. Decision table: when, and which, to enable

Let me land all of the above into a practical decision. Since **both are "zero code change, zero infrastructure change, agentless,"** the decision axis is simply **"is there a supported asset."**

| Your situation | Should you enable | Plan | Reason |
| --- | --- | --- | --- |
| Using a supported-engine Aurora/RDS (PostgreSQL/MySQL/MariaDB) in production | **Enable** | RDS Protection | Detect login anomalies directly from the AWS side. No impact on DB performance |
| Using Aurora PostgreSQL Limitless Database | **Enable** (mind billing) | RDS Protection | Added to monitoring automatically. Billed immediately if the trial has ended |
| You have Lambda functions that **communicate outbound** to the internet / external APIs | **Enable** | Lambda Protection | Detect C&C, mining, malicious-host communication |
| You have Lambda functions handling sensitive data / credentials | **Enable** | Lambda Protection | High impact in a compromise. Captures repurposing / exfiltration communication |
| You have Lambdas with many third-party dependencies (npm/pip, etc.) | **Enablement recommended** | Lambda Protection | Detect suspicious communication after supply-chain taint |
| You use **only Lambda@Edge** | **Out of scope** | — | Lambda@Edge is outside Lambda Protection's monitoring |
| You don't use any DB (DynamoDB only, etc.) | RDS is **unnecessary** | — | No monitoring target (YAGNI). Protect DynamoDB with IAM, etc. |
| You don't use Lambda at all | Lambda is **unnecessary** | — | No monitoring target (YAGNI) |

**The crux of the design is "add according to your assets (YAGNI)."** For a DynamoDB-based serverless configuration (my [payment platform](/case-studies/payment-platform-reliability) was this shape), **even if you enable RDS Protection, the detection targets are zero** — only cost increases. On the other hand, for a configuration where outbound-communicating Lambdas line up, Lambda Protection is a cost-effective choice that **adds a layer of compromise detection with zero code change**.

> **An honest premise (no exaggeration)**: my actual case study is a **DynamoDB-based serverless payment platform**, and it doesn't put RDS at the production core. So I won't say I "ran RDS Protection on a real project" here. What I can speak to is — based on the experience of **implementing IAM, observability, and DR across multi-account AWS and operating Lambda in production**, I can accompany you on **how to design and implement these two layers on your Aurora/RDS and Lambda**, based on the official specs and general RDS/Aurora knowledge.

---

## 6. Enable with Terraform, turn into action with EventBridge

### 6.1 Enablement (surprisingly short)

Both plans are added with an **`aws_guardduty_detector_feature`** resource, separate from the `aws_guardduty_detector` body. The feature names are **RDS Protection = `RDS_LOGIN_EVENTS`** and **Lambda Protection = `LAMBDA_NETWORK_LOGS`**.

```hcl
# detector 本体は別途定義済みとする（ピラー記事の 2 章を参照）。
# 基盤検知 + Extended Threat Detection は detector 有効化の時点で動いている。

# ── RDS Protection ──
# 対応する Aurora/RDS を使っている時だけ有効化する（YAGNI）。
# エージェントレス・インフラ変更ゼロ。有効化直後〜新規インスタンスは
# 最大2週間の学習期間があり、その間 AnomalousBehavior 系 finding は出ない。
resource "aws_guardduty_detector_feature" "rds_login_events" {
  detector_id = aws_guardduty_detector.this.id
  name        = "RDS_LOGIN_EVENTS"
  status      = "ENABLED"
}

# ── Lambda Protection ──
# 外向き通信する Lambda がある時に有効化する。
# 全関数(非VPC含む)のネットワーク活動を解析。Lambda@Edge は対象外。
# ENI 用の VPC Flow Logs を自分で張る必要はない。
resource "aws_guardduty_detector_feature" "lambda_network_logs" {
  detector_id = aws_guardduty_detector.this.id
  name        = "LAMBDA_NETWORK_LOGS"
  status      = "ENABLED"
}
```

If you enable it org-wide in one go, you can roll it out from the delegated administrator account with the same feature names (`RDS_LOGIN_EVENTS` / `LAMBDA_NETWORK_LOGS`), in the same structure as the pillar article's `aws_guardduty_organization_configuration_feature`.

```hcl
# ── 委任管理者アカウントで実行：組織全体に自動展開 ──
# 「対応資産を持つアカウントが多い」場合は組織一括が漏れを防ぐ。
# ただし対応資産ゼロのアカウントにも課金は基本発生しない(監視対象がないため)。
resource "aws_guardduty_organization_configuration_feature" "rds_org" {
  detector_id = aws_guardduty_detector.security.id
  name        = "RDS_LOGIN_EVENTS"
  auto_enable = "ALL" # 既存 + 新規すべて。取りこぼしを構造的に消す
}

resource "aws_guardduty_organization_configuration_feature" "lambda_org" {
  detector_id = aws_guardduty_detector.security.id
  name        = "LAMBDA_NETWORK_LOGS"
  auto_enable = "ALL"
}
```

> **Why choose `auto_enable = "ALL"`**: with `NEW` (only accounts that join going forward), accounts that already exist at config time can be left out of scope forever — this is a **silent hole** against the goal of "detecting org-wide with no gaps." Since RDS/Lambda is unlikely to incur detection or billing in accounts without a monitoring target (a DB / outbound Lambda), the design of **enabling broadly with ALL, where only places with assets actually run** falls on the safe side.

### 6.2 EventBridge routing: dispatch by type and severity

The foundation for turning detection into action is **EventBridge** (the detailed automated-response design is in Section 6 of the pillar article). RDS/Lambda findings, like other GuardDuty findings, flow to EventBridge in near real time. The standard is to dispatch by **the type prefix and the `severity` number**.

```json
{
  "source": ["aws.guardduty"],
  "detail-type": ["GuardDuty Finding"],
  "detail": {
    "type": [
      { "prefix": "CredentialAccess:RDS/" },
      { "prefix": "Discovery:RDS/" },
      { "prefix": "Backdoor:Lambda/" },
      { "prefix": "CryptoCurrency:Lambda/" },
      { "prefix": "Trojan:Lambda/" },
      { "prefix": "UnauthorizedAccess:Lambda/" }
    ],
    "severity": [{ "numeric": [">=", 7] }]
  }
}
```

The **`prefix` match** on `type` picks up "RDS-origin / Lambda-origin" together, and `severity >= 7` (High and above) narrows to **containment candidates**. With this, types prone to noise like `AnomalousBehavior.FailedLogin` (Low) fall out of containment, and only `SuccessfulBruteForce` / `MaliciousIPCaller.SuccessfulLogin` / `BitcoinTool.B` (all High) get onto the response pipeline.

### 6.3 An idempotent automated-response Lambda (Python) — the response differs for RDS and Lambda

This is the climax of this article. For RDS findings and Lambda findings, **the action to take is fundamentally different**:

- **RDS compromise** → credential rotation, rectifying the DB's network exposure (an area where human review should be interposed).
- **Lambda compromise** → stop the function's communication (narrow concurrency to 0), review the code, dependencies, and secrets.

And an iron rule common to both — since **[EventBridge is at-least-once delivery](/blog/aws-guardduty-eventbridge-automated-remediation-incident-response-guide)**, a Lambda can be invoked twice for the same finding. With the same thinking as preventing double charges on a payment platform, **the automated response too must be idempotent**.

```python
"""GuardDuty の RDS/Lambda Protection finding に応答する自動対応 Lambda。

設計原則:
  - 冪等: EventBridge は at-least-once。同じ finding を2回受けても副作用は1回分。
  - スコープを絞る: 自動アクションは「取り消せる・スコープが狭い」ものに限定。
  - RDS は通知中心: DB の認証情報ローテーションは破壊的なので人間のレビューを挟む。
  - Lambda は封じ込め可: 同時実行数を 0 に絞るのは取り消し可能(後で戻せる)。
  - 入力はサニタイズ: finding フィールド(関数名・ユーザー名等)は信頼しない外部入力として扱う。
"""
from __future__ import annotations

import json
import logging
import os
from typing import Any, Final

import boto3

logger = logging.getLogger()
logger.setLevel(logging.INFO)

lambda_client = boto3.client("lambda")
sns = boto3.client("sns")

ALERT_TOPIC_ARN: Final[str] = os.environ["ALERT_TOPIC_ARN"]
# 破壊的操作を避け、まず観察したい段階では DRY_RUN=true で封じ込めを抑止。
DRY_RUN: Final[bool] = os.environ.get("DRY_RUN", "false").lower() == "true"

# Lambda 関数を自動で封じ込める(同時実行=0)対象の finding 型。
# High 確度の「侵害ほぼ確実」な型に限定する。
LAMBDA_CONTAIN_ALLOWLIST: Final[frozenset[str]] = frozenset(
    {
        "Backdoor:Lambda/C&CActivity.B",
        "CryptoCurrency:Lambda/BitcoinTool.B",
        "UnauthorizedAccess:Lambda/TorClient",
        "UnauthorizedAccess:Lambda/TorRelay",
    }
)


def handler(event: dict[str, Any], _context: object) -> dict[str, str]:
    detail = event["detail"]
    finding_type: str = detail["type"]
    severity: float = float(detail["severity"])
    finding_id: str = detail["id"]

    if finding_type.endswith("/") or "/" not in finding_type:
        # 想定外フォーマットは通知だけして安全側に倒す。
        action = _route_unknown(finding_type, severity, finding_id)
    elif "Lambda/" in finding_type:
        action = _handle_lambda(detail, finding_type, severity, finding_id)
    elif "RDS/" in finding_type:
        action = _handle_rds(detail, finding_type, severity, finding_id)
    else:
        action = _route_unknown(finding_type, severity, finding_id)

    logger.info(
        json.dumps({"finding_id": finding_id, "type": finding_type, "action": action})
    )
    return {"action": action}


def _handle_lambda(
    detail: dict[str, Any], finding_type: str, severity: float, finding_id: str
) -> str:
    """Lambda の侵害: 高確度なら同時実行を 0 に絞って通信を止める(取り消し可能)。"""
    # finding から関数名を取り出す(構造はリソース種別に依存。存在チェックを厳格に)。
    resource = detail.get("resource", {})
    func_name = resource.get("lambdaDetails", {}).get("functionName")

    should_contain = (
        finding_type in LAMBDA_CONTAIN_ALLOWLIST
        and severity >= 7.0
        and bool(func_name)
    )

    if should_contain and not DRY_RUN:
        action = _throttle_function(func_name)  # 冪等
    else:
        action = "dry-run" if (should_contain and DRY_RUN) else "notify-only"

    _notify("Lambda", finding_type, severity, finding_id, func_name, action)
    return action


def _throttle_function(func_name: str) -> str:
    """関数の予約済み同時実行数を 0 にして即時に通信を止める。
    冪等: 既に 0 ならそのまま(2回目の起動は no-op)。後で削除すれば原状復帰。"""
    current = lambda_client.get_function_concurrency(FunctionName=func_name)
    if current.get("ReservedConcurrentExecutions") == 0:
        return "already-contained"
    lambda_client.put_function_concurrency(
        FunctionName=func_name, ReservedConcurrentExecutions=0
    )
    return "contained-throttled-to-zero"


def _handle_rds(
    detail: dict[str, Any], finding_type: str, severity: float, finding_id: str
) -> str:
    """RDS の侵害: 認証情報ローテーション/SG 変更は破壊的。
    自動では『チケット化 + 人間のレビュー依頼』にとどめ、手は出さない。"""
    resource = detail.get("resource", {})
    db = resource.get("rdsDbInstanceDetails", {})
    db_id = db.get("dbInstanceIdentifier")
    # 異常ログインの DB ユーザー(誰の認証情報を疑うか)を通知に含める。
    db_user = (
        resource.get("rdsDbUserDetails", {}).get("user")
        if "rdsDbUserDetails" in resource
        else None
    )
    _notify(
        "RDS", finding_type, severity, finding_id, f"{db_id} (user={db_user})", "review-required"
    )
    # 破壊的操作(パスワードローテーション等)は人間の承認フローへ。ここでは実行しない。
    return "review-required"


def _route_unknown(finding_type: str, severity: float, finding_id: str) -> str:
    _notify("Other", finding_type, severity, finding_id, None, "notify-only")
    return "notify-only"


def _notify(
    kind: str,
    finding_type: str,
    severity: float,
    finding_id: str,
    resource_ref: str | None,
    action: str,
) -> None:
    region = os.environ.get("AWS_REGION", "ap-northeast-1")
    console = (
        f"https://{region}.console.aws.amazon.com/guardduty/home"
        f"?region={region}#/findings?fId={finding_id}"
    )
    sns.publish(
        TopicArn=ALERT_TOPIC_ARN,
        Subject=f"[GuardDuty][{kind}][{severity}] {finding_type}",
        Message="\n".join(
            [
                f"type: {finding_type}",
                f"severity: {severity}",
                # resource_ref は finding 由来＝信頼しない入力。Web 表示時は
                # 受け手側で HTML エスケープする(ここでは生のまま SNS へ)。
                f"resource: {resource_ref or 'n/a'}",
                f"action: {action}",
                f"console: {console}",
            ]
        ),
    )
```

Let me make explicit the design decisions of this code.

- **Branching the response for RDS and Lambda**: for Lambda, you can automate **a reversible containment** of "stop communication with concurrency 0," but RDS's "credential rotation / SG change" is **destructive (it can lock out legitimate users)**, so automatically it stays at **ticketing and a human review request**. This is an implementation of the iron rule "interpose a human for destructive operations."
- **Idempotent**: Lambda containment is guarded with "if concurrency is already 0, no-op." Even if invoked twice with at-least-once delivery, the side effect is one time's worth.
- **Reversible**: `put_function_concurrency(0)` returns to the original state if you **delete the reserved concurrency**. It does not do irreversible operations like deleting the function or rewriting code.
- **Treat input on the premise of sanitization**: the **function name and DB user name** contained in a finding **are external input an attacker can influence**. The side that forwards it to a web dashboard or Slack must **always run sanitization like HTML escaping** — as also touched on in the [Runtime Monitoring article](/blog/aws-guardduty-runtime-monitoring-eks-ecs-fargate-ec2-guide), finding fields are untrusted input.
- **Least privilege**: this Lambda's execution role is limited to `lambda:GetFunctionConcurrency` / `lambda:PutFunctionConcurrency` (scoped to the containment-target function ARN) and `sns:Publish`. It is **not granted** destructive permissions on RDS (those go on the human-flow side).

The more full-fledged incident-response wiring (SOAR, ticketing integration, an approval flow via Step Functions) is dug into in the [dedicated EventBridge automated-response article](/blog/aws-guardduty-eventbridge-automated-remediation-incident-response-guide).

---

## 7. Cost: agentless doesn't mean "free"

"Agentless, zero infrastructure change" is appealing, but the fact that it's **usage-based billing** doesn't change. Let me pin down each plan's billing unit.

| Plan | How billing works |
| --- | --- |
| **RDS Protection** | Usage-based billing by the amount of RDS login activity monitored (by Region; verify on the official pricing page) |
| **Lambda Protection** | Usage-based billing against the **GB of Lambda network activity logs** processed. GuardDuty optimizes cost by **analyzing only the subset relevant to threat detection with smart filters** |

The important point the official docs state clearly about Lambda Protection: *"GuardDuty only charges for the amount of Lambda network activity logs data processed (in GB) to generate a finding. GuardDuty optimizes cost by applying smart filters and analyzing a subset of Lambda network activity logs that are relevant to threat detection."* — that is, it processes **not the whole log in its entirety, but only the subset relevant to threat detection**.

> **Beware future billing expansion (Lambda)**: the official docs state clearly that Lambda's network-activity monitoring **may expand in the future to other activity like DNS query data**, that in that case **the volume of data processed = cost increases**, and that **they will give at least 30 days' notice at the time of expansion**. Don't fix your view as "it's cheap now"; run operations that don't miss the expansion notice.

For both plans, **enabling for the first time in a new Region comes with a 30-day free trial**. During this, you can know **the projected bill at production volume**. I leave GuardDuty's overall cost optimization (including foundational and other plans) to the [dedicated article](/blog/aws-guardduty-cost-optimization-pricing-finops-guide), but the iron rule of these two plans is a repeat of Section 5 — **enable only where there are assets (a supported DB / outbound Lambda)**. With no supported asset, zero detection = zero value paid for.

> **The trial end doesn't auto-stop**: after 30 days pass, neither plan is **disabled automatically**. Usage charges keep being incurred as-is. Build "try it in the trial, and explicitly disable if unneeded" into your operations. Also note that RDS's Limitless Database auto-addition (1.2) is billed immediately if the trial has ended.

---

## 8. Summary: an RDS / Lambda Protection production cheat sheet

A quick-reference table for when you're unsure.

- **Common properties**: both are detection layers you can add with **agentless, zero infrastructure change, zero code change**. GuardDuty ingests logs directly from the AWS side, and **the log itself doesn't come to you** (you don't build a log platform yourself). But it's **usage-based billing**, not "free."
- **RDS Protection (monitoring target = login activity)**: supports Aurora (MySQL/PostgreSQL-compatible), RDS for PostgreSQL/MySQL/MariaDB, and Aurora PostgreSQL Limitless. **The engine types are settled; verify versions against the official compatibility table.** Captures **both successful and failed logins**. **New enablement / new instances have an up-to-two-week learning period**, during which `AnomalousBehavior.*` (ML family) findings don't appear (the `MaliciousIPCaller` / `TorIPCaller` families need no learning).
- **RDS finding types (9)**: `AnomalousBehavior.{SuccessfulLogin(Variable)/FailedLogin(Low)/SuccessfulBruteForce(High)}`, `MaliciousIPCaller.{SuccessfulLogin(High)/FailedLogin(Medium)}`, `Discovery:RDS/MaliciousIPCaller(Medium)`, `TorIPCaller.{SuccessfulLogin(High)/FailedLogin(Medium)}`, `Discovery:RDS/TorIPCaller(Medium)`. Resource Type is `RDSDBInstance` / `RDSLimitlessDB`.
- **RDS response**: for successful-login types, **credential rotation + audit-log check**. For failure/probe types, **private VPC + SG restriction**. The root countermeasure is to reduce passwords with **IAM database authentication**.
- **Lambda Protection (monitoring target = network activity)**: analyzes the network activity of **all Lambda functions (including functions that don't use VPC)**. **You don't need to stretch VPC Flow Logs yourself for the ENIs.** **Lambda@Edge is out of scope.** Resource Type is `Lambda`.
- **Lambda finding types (7)**: `Backdoor:Lambda/C&CActivity.B(High)`, `CryptoCurrency:Lambda/BitcoinTool.B(High)`, `Trojan:Lambda/BlackholeTraffic(Medium)`, `Trojan:Lambda/DropPoint(Medium)`, `UnauthorizedAccess:Lambda/MaliciousIPCaller.Custom(Medium)`, `UnauthorizedAccess:Lambda/TorClient(High)`, `UnauthorizedAccess:Lambda/TorRelay(High)`.
- **Lambda response**: if unexpected, **on the premise that the function is compromised** — **contain with concurrency 0 → review code and dependencies → rotate secrets**. For legitimate blockchain processing, a suppression rule with the two conditions "type + function name."
- **Enablement (Terraform)**: `aws_guardduty_detector_feature`'s **`RDS_LOGIN_EVENTS`** / **`LAMBDA_NETWORK_LOGS`**. Org-wide is `aws_guardduty_organization_configuration_feature` + `auto_enable = "ALL"`.
- **Automated response**: dispatch by EventBridge's **type `prefix` + `severity` number**. **Lambda is a reversible containment with concurrency 0**, **RDS is destructive so interpose human review**. **At-least-once → idempotency required.** Finding fields are untrusted input = sanitize.
- **Cost**: RDS by login-activity volume, Lambda by GB processed (a subset via smart filters). Know the bill in advance with the **30-day free trial**. **It doesn't auto-stop after the end.** Lambda may expand to DNS, etc. in the future = possible billing increase (with 30-day notice).

RDS Protection and Lambda Protection are not "boxes that protect you if you enable them," but detection layers whose value comes out by **"① discerning supported assets and adding them (YAGNI), ② reading finding types correctly, and ③ connecting EventBridge → idempotent, scope-narrowed automated response."** The greatest appeal is — **being able to add a layer of DB-login-anomaly and serverless-network-threat detection without changing code or infrastructure at all**. The greatest leverage is in the **plumbing that turns findings into safe automated response**, more than the detection itself.

On a multi-account [serverless payment platform](/case-studies/payment-platform-reliability), I implemented IAM, observability, and DR across it and **ensured "correctness" with the structure of code and idempotency, not operational vigilance**. I design the introduction of RDS/Lambda Protection with the same philosophy — **① enable at minimal cost according to supported assets, ② branch the response per finding type (RDS by human review, Lambda by reversible containment), and ③ shrink MTTR with EventBridge → idempotent automated response**. I don't end detection at a "red dashboard" — I build all the way to a response mechanism that gets into operation.

**"How do I design RDS Protection and Lambda Protection on my Aurora/RDS and Lambda, how far do I entrust automated response, and how do I hold cost down?" — from inventorying supported assets through Terraform implementation, EventBridge automated response, org governance, and false-positive tuning, I can accompany you fast and safely, one person × generative AI (Claude Code).** Even from the requirements-organizing stage, feel free to consult me.

---

### Reference (official documentation)

- [GuardDuty RDS Protection](https://docs.aws.amazon.com/guardduty/latest/ug/rds-protection.html) — the definition of RDS Protection, the supported-engine table, login activity, the up-to-two-week learning period, the 30-day free trial, Limitless auto-addition
- [RDS Protection finding types](https://docs.aws.amazon.com/guardduty/latest/ug/findings-rds-protection.html) — the 9 finding types, severities, remediation (Resource Type `RDSDBInstance` / `RDSLimitlessDB`)
- [GuardDuty Lambda Protection](https://docs.aws.amazon.com/guardduty/latest/ug/lambda-protection.html) — network-activity monitoring of all functions (including non-VPC), no ENI flow logs needed, Lambda@Edge exclusion, smart-filter billing, the 30-day free trial
- [Lambda Protection finding types](https://docs.aws.amazon.com/guardduty/latest/ug/lambda-protection-finding-types.html) — the 7 finding types, severities, remediation (Resource Type `Lambda`)
- [Amazon GuardDuty pricing](https://aws.amazon.com/guardduty/pricing/) — RDS Protection / Lambda Protection usage-based billing, by Region, the 30-day free trial
