Back to skill

Security audit

AWS Cognito Auth

Security checks for vulnerabilities and agentic risk

Overview

This is a documentation-only AWS Cognito skill, but some auth examples are unsafe enough that users should review them before allowing generated code into a real system.

Install only if you want Cognito-specific reference material and are comfortable reviewing all generated auth code. Do not copy the M2M token helper, passwordless OTP example, or pre-token admin-claim examples into production without hardcoding or allowlisting trusted domains, using cryptographic randomness and rate limits, and deriving roles or groups from a trusted authorization source.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
references/auth-flows.md:520
Finding

Caller-Controlled OAuth Endpoint Can Receive Confidential Client Credentials

Content
View full analysis

Vulnerability Details

File Location: references/auth-flows.md, lines 520-563
Vulnerability Type: Unrestricted transmission of client credentials to a caller-controlled endpoint
Risk Level: High

Vulnerable Code

typescript
async function getM2MToken(
  domain: string,
  clientId: string,
  clientSecret: string,
  scopes: string[]
) {
  const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');

  const response = await fetch(`https://${domain}/oauth2/token`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      Authorization: `Basic ${credentials}`,
    },
    body: new URLSearchParams({
      grant_type: 'client_credentials',
      scope: scopes.join(' '),
    }),
  });

  const data = await response.json();
  return data.access_token; // Only access token — no ID or refresh token
}
python
import requests
import base64

def get_m2m_token(domain: str, client_id: str, client_secret: str, scopes: list[str]):
    credentials = base64.b64encode(f"{client_id}:{client_secret}".encode()).decode()
    response = requests.post(
        f"https://{domain}/oauth2/token",
        headers={
            "Content-Type": "application/x-www-form-urlencoded",
            "Authorization": f"Basic {credentials}",
        },
        data={
            "grant_type": "client_credentials",
            "scope": " ".join(scopes),
        },
    )
    return response.json()["access_token"]

Technical Analysis

Base64 encoding the client ID and client secret is the standard encoding required by HTTP Basic authentication and is necessary for the Cognito client-credentials flow. It is not encryption and provides no confidentiality independently of TLS.

The security defect is that the destination domain is accepted without validation. If this value is obtained from configuration, reque ...[truncated 1590 chars]

Remediation
View remediation

Remediation Suggestions

  • Do not accept an arbitrary destination hostname at the point where credentials are transmitted.
  • Construct the endpoint from trusted deployment configuration rather than request or tenant input.
  • For Cognito-managed domains, validate the hostname against the expected AWS Cognito domain and region.
  • For custom Cognito domains, maintain an explicit allowlist of exact hostnames.
  • Parse the endpoint with a URL parser and reject user information, unexpected ports, fragments, query strings, and path confusion.
  • Do not use a suffix-only hostname check that could accept names such as amazoncognito.com.attacker.example.
  • Check response.ok in TypeScript and call response.raise_for_status() in Python before processing the body.
  • Validate that the returned JSON contains a correctly typed access_token.
  • Set connection and response timeouts.
  • Store client secrets in AWS Secrets Manager or another dedicated secret store and rotate them after suspected disclosure.
  • Limit each M2M client to the minimum required custom scopes.

T09 · Insecure Skill Coding Practices

Error
Location
references/auth-flows.md:475
Finding

Passwordless Authentication Uses a Non-Cryptographic OTP Generator

Content
View full analysis

Vulnerability Details

File Location: references/auth-flows.md, line 475
Vulnerability Type: Predictable authentication challenge generation
Risk Level: High

Vulnerable Code

typescript
import { SESClient, SendEmailCommand } from '@aws-sdk/client-ses';

const ses = new SESClient({});

export const handler = async (event: any) => {
  const otp = Math.floor(100000 + Math.random() * 900000).toString();

  // Send OTP via email
  await ses.send(new SendEmailCommand({
    Destination: { ToAddresses: [event.request.userAttributes.email] },
    Message: {
      Subject: { Data: 'Your verification code' },
      Body: { Text: { Data: `Your code is: ${otp}` } },
    },
    Source: 'noreply@yourdomain.com',
  }));

  event.response.publicChallengeParameters = {
    email: event.request.userAttributes.email,
  };
  event.response.privateChallengeParameters = { otp };
  event.response.challengeMetadata = 'EMAIL_OTP';

  return event;
};

Technical Analysis

Math.random() is not a cryptographically secure pseudorandom-number generator and must not be used to create authentication secrets. Its output may be predictable when an attacker can infer or observe enough generator state or related outputs.

The generated value has only six decimal digits. A six-digit OTP can be acceptable when combined with strict expiration, one-time-use enforcement, request throttling, and a low attempt limit, but those safeguards are not demonstrated by the custom authentication example. The associated challenge-definition example fails only after a single incorrect Cognito challenge response, but the documentation does not establish account-level issuance limits, replay protection, or explicit expiration.

Attack Path

  1. An attacker initiates custom authentication for a target account.
  2. The Lambda generates the OTP with Math.random().
  3. The attacker predicts the value using information ab ...[truncated 712 chars]
Remediation
View remediation

Remediation Suggestions

  • Generate the OTP with Node.js cryptographic randomness:

    typescript
    import { randomInt } from 'node:crypto';
    
    const otp = randomInt(100000, 1000000).toString();
    
  • Associate every challenge with a short, explicit expiration time.

  • Invalidate a challenge immediately after successful use.

  • Invalidate older challenges when a replacement OTP is issued.

  • Limit verification attempts per challenge and per account.

  • Rate-limit OTP issuance by account, IP address, device, and destination address.

  • Apply AWS WAF and application-level abuse controls to the authentication flow.

  • Avoid logging OTP values or complete Cognito trigger events.

  • Monitor abnormal OTP issuance and verification failures.

  • Use a constant-time comparison for authentication secrets where the surrounding implementation permits it.

T05 · Unauthorized Access and Privilege Escalation

Error
Location
references/lambda-triggers.md:157
Finding

Pre-Token Trigger Examples Unconditionally Grant Administrative Claims

Content
View full analysis

Vulnerability Details

File Location: references/lambda-triggers.md, lines 157-194
Vulnerability Type: Unconditional authorization claim and group elevation
Risk Level: High

Vulnerable Code

typescript
export const handler = async (event: any) => {
  // Add custom claims to the ID token
  event.response.claimsAndScopeOverrideDetails = {
    idTokenGeneration: {
      claimsToAddOrOverride: {
        'custom:tenant': 'acme-corp',
        'custom:permissions': JSON.stringify(['read', 'write']),
      },
      claimsToSuppress: ['email_verified'], // Remove claims you don't want exposed
    },
    accessTokenGeneration: {
      claimsToAddOrOverride: {
        'custom:role': 'admin',
      },
      scopesToAdd: ['custom-scope'],
      scopesToSuppress: [],
    },
  };

  return event;
};
typescript
export const handler = async (event: any) => {
  // V1 can only modify ID token claims and group overrides
  event.response.claimsOverrideDetails = {
    claimsToAddOrOverride: {
      'custom:tenant': 'acme-corp',
    },
    groupsToOverride: ['admin', 'users'], // Override cognito:groups claim
  };

  return event;
};

Technical Analysis

A pre-token generation trigger executes for users during token issuance and can alter claims that downstream services use for authorization. These examples assign an admin role, add a custom scope, and replace Cognito group membership with admin and users for every invocation.

No authorization decision is made from a trusted server-side source. The values are not tied to the authenticated user's immutable identifier, verified tenant membership, an administrative assignment, or an approved group record.

If copied into an application where APIs trust custom:role, custom scopes, or cognito:groups, the trigger converts every successfully authenticated user into an administrator. The behavior exceeds the m ...[truncated 1111 chars]

Remediation
View remediation

Remediation Suggestions

  • Remove privileged literal values from general-purpose examples.
  • Use clearly non-privileged placeholders when demonstrating claim customization.
  • Resolve roles, groups, permissions, scopes, and tenant membership from a trusted server-side datastore.
  • Key authorization lookups by the immutable Cognito sub, not by mutable email addresses or user-editable custom attributes.
  • Default to no additional privileges when a lookup fails, times out, or returns inconsistent data.
  • Validate that a user belongs to the tenant inserted into the token.
  • Maintain an explicit allowlist of claims and scopes that the trigger may add.
  • Do not allow client-controlled attributes to determine administrative roles.
  • Add tests proving that ordinary users cannot receive administrative claims or groups.
  • Audit downstream services to ensure sensitive authorization decisions use access tokens and validated scopes or trusted claims.
  • Revoke active sessions and correct role assignments if the unsafe example has already been deployed.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (32)

Vague Triggers

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The manifest uses extremely broad trigger language, including generic authentication and authorization scenarios that may only tangentially relate to Cognito. This can cause the skill to be invoked in contexts the user did not explicitly request, increasing the chance of inappropriate routing, overreach into sensitive auth design decisions, and accidental exposure of Cognito-specific guidance where a different solution would be safer or more appropriate.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 36)May include surrounding context.

md
These are the two main Cognito components and they serve different purposes:

- **User Pool**: A user directory and OIDC identity provider. Handles sign-up, sign-in, MFA, token issuance (ID token, access token, refresh token), and federation with external IdPs. Think of it as "who is this user?"
- **Identity Pool** (Federated Identities): Exchanges tokens (from a user pool, social provider, SAML, or OIDC) for temporary AWS credentials (STS). Think of it as "what AWS resources can this user access?"

A common architecture uses both: User Pool authenticates the user and issues tokens → Identity Pool exchanges those tokens for AWS credentials → User accesses S3, DynamoDB, etc.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 107)May include surrounding context.

md
These are the two main Cognito components and they serve different purposes:

- **User Pool**: A user directory and OIDC identity provider. Handles sign-up, sign-in, MFA, token issuance (ID token, access token, refresh token), and federation with external IdPs. Think of it as "who is this user?"
- **Identity Pool** (Federated Identities): Exchanges tokens (from a user pool, social provider, SAML, or OIDC) for temporary AWS credentials (STS). Think of it as "what AWS resources can this user access?"

A common architecture uses both: User Pool authenticates the user and issues tokens → Identity Pool exchanges those tokens for AWS credentials → User accesses S3, DynamoDB, etc.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 54)May include surrounding context.

md
### Token Types

- **ID Token**: Contains user identity claims (email, name, groups, custom attributes). Use for identity verification on your backend.
- **Access Token**: Contains scopes and authorized actions. Use for API authorization (e.g., API Gateway Cognito Authorizer).
- **Refresh Token**: Long-lived token to obtain new ID/access tokens without re-authentication. Default validity is 30 days.

## Workflow: Building a Cognito Solution

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 55)May include surrounding context.

md
- **ID Token**: Contains user identity claims (email, name, groups, custom attributes). Use for identity verification on your backend.
- **Access Token**: Contains scopes and authorized actions. Use for API authorization (e.g., API Gateway Cognito Authorizer).
- **Refresh Token**: Long-lived token to obtain new ID/access tokens without re-authentication. Default validity is 30 days.

## Workflow: Building a Cognito Solution

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/auth-flows.md (reported line 415)May include surrounding context.

md
- `aud` — audience (client ID)
- `exp` — expiration timestamp

**Access Token claims**:
- `sub` — same as ID token
- `scope` — OAuth scopes
- `cognito:groups` — group names

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/security.md (reported line 137)May include surrounding context.

md
### Storage

| Platform | ID/Access Tokens | Refresh Token |
|----------|-----------------|---------------|
| Web (SPA) | In-memory (JS variable) | HttpOnly Secure cookie |
| Web (SSR) | HttpOnly Secure cookie | HttpOnly Secure cookie |

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/setup-guide.md (reported line 99)May include surrounding context.

md
Recommended defaults:
- ID token: 1 hour (range: 5 min to 1 day)
- Access token: 1 hour (range: 5 min to 1 day)
- Refresh token: 30 days (range: 1 hour to 10 years)

For high-security apps, shorten ID/access to 15-30 minutes.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/lambda-triggers.md (reported line 154)May include surrounding context.

md
### `Token is expired`

ID and access tokens have a default 1-hour validity.

**Fix**: Implement token refresh logic. With Amplify, `fetchAuthSession()` auto-refreshes. With SDK, call `InitiateAuth` with `REFRESH_TOKEN_AUTH` flow.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/security.md (reported line 166)May include surrounding context.

md
### `Token is expired`

ID and access tokens have a default 1-hour validity.

**Fix**: Implement token refresh logic. With Amplify, `fetchAuthSession()` auto-refreshes. With SDK, call `InitiateAuth` with `REFRESH_TOKEN_AUTH` flow.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/troubleshooting.md (reported line 65)May include surrounding context.

md
### `Token is expired`

ID and access tokens have a default 1-hour validity.

**Fix**: Implement token refresh logic. With Amplify, `fetchAuthSession()` auto-refreshes. With SDK, call `InitiateAuth` with `REFRESH_TOKEN_AUTH` flow.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/troubleshooting.md (reported line 83)May include surrounding context.

md
### `Token is expired`

ID and access tokens have a default 1-hour validity.

**Fix**: Implement token refresh logic. With Amplify, `fetchAuthSession()` auto-refreshes. With SDK, call `InitiateAuth` with `REFRESH_TOKEN_AUTH` flow.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/auth-flows.md (reported line 541)May include surrounding context.

md
### `Token use doesn't match`

You're validating an ID token where an access token is expected, or vice versa.

**Fix**: Check the `token_use` claim. ID tokens have `"token_use": "id"`, access tokens have `"token_use": "access"`.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/security.md (reported line 156)May include surrounding context.

md
### `Token use doesn't match`

You're validating an ID token where an access token is expected, or vice versa.

**Fix**: Check the `token_use` claim. ID tokens have `"token_use": "id"`, access tokens have `"token_use": "access"`.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/troubleshooting.md (reported line 81)May include surrounding context.

md
### `Token use doesn't match`

You're validating an ID token where an access token is expected, or vice versa.

**Fix**: Check the `token_use` claim. ID tokens have `"token_use": "id"`, access tokens have `"token_use": "access"`.

Behavior Manipulation

Medium
Category
Prompt Injection
Confidence
70% confidence
Finding

Subtle instructions detected that may alter agent decision-making or introduce hidden biases.

Content

Scanner excerpt · references/auth-flows.md (reported line 29)May include surrounding context.

md
| Migration from legacy auth | USER_PASSWORD_AUTH + migration trigger | Temporary |

**General rules**:
- Always prefer SRP over plaintext password flows
- Always use PKCE for public clients
- Never use Implicit flow (legacy, insecure)
- Always implement token refresh

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · references/auth-flows.md (reported line 453)May include surrounding context.

md
// First attempt — issue custom challenge
    event.response.challengeName = 'CUSTOM_CHALLENGE';
    event.response.issueTokens = false;
    event.response.failAuthentication = false;
  } else if (
    session.length === 1 &&
    session[0].challengeResult === true

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · references/auth-flows.md (reported line 460)May include surrounding context.

md
// First attempt — issue custom challenge
    event.response.challengeName = 'CUSTOM_CHALLENGE';
    event.response.issueTokens = false;
    event.response.failAuthentication = false;
  } else if (
    session.length === 1 &&
    session[0].challengeResult === true

Dynamic Request Target

Medium
Category
Server-Side Request Forgery
Confidence
84% confidence
Finding

The Node.js M2M example interpolates a dynamic domain directly into the token endpoint URL and sends Basic-auth client credentials to that host. In a real integration where domain can be influenced by users or external config, this creates SSRF risk and may disclose OAuth client secrets to attacker-controlled infrastructure.

Content

Scanner excerpt · references/auth-flows.md (reported line 528)May include surrounding context.

md
) {
  const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');

  const response = await fetch(`https://${domain}/oauth2/token`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',

Dynamic Request Target

Medium
Category
Server-Side Request Forgery
Confidence
84% confidence
Finding

The Python M2M example constructs the request URL from a caller-controlled domain without validation or allowlisting. If reused in an application with untrusted input, this can enable outbound requests to attacker-chosen hosts, potentially causing SSRF, credential leakage via Authorization headers, or access to internal services.

Content

Scanner excerpt · references/auth-flows.md (reported line 553)May include surrounding context.

md
def get_m2m_token(domain: str, client_id: str, client_secret: str, scopes: list[str]):
    credentials = base64.b64encode(f"{client_id}:{client_secret}".encode()).decode()
    response = requests.post(
        f"https://{domain}/oauth2/token",
        headers={
            "Content-Type": "application/x-www-form-urlencoded",

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/iac-patterns.md (reported line 175)May include surrounding context.

md
cognito.OAuthScope.OPENID,
      cognito.OAuthScope.EMAIL,
    ],
    callbackUrls: ['https://api.yourdomain.com/auth/callback'],
    logoutUrls: ['https://api.yourdomain.com/auth/logout'],
  },
  preventUserExistenceErrors: true,

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/iac-patterns.md (reported line 176)May include surrounding context.

md
cognito.OAuthScope.OPENID,
      cognito.OAuthScope.EMAIL,
    ],
    callbackUrls: ['https://api.yourdomain.com/auth/callback'],
    logoutUrls: ['https://api.yourdomain.com/auth/logout'],
  },
  preventUserExistenceErrors: true,

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · references/lambda-triggers.md (reported line 23)May include surrounding context.

md
| Trigger | When It Fires | Common Use Cases |
|---------|--------------|------------------|
| Pre Sign-Up | Before a new user is registered | Auto-confirm users, validate email domains, block disposable emails |
| Post Confirmation | After user confirms their account | Create user record in DynamoDB, send welcome email, add to default group |
| Pre Authentication | Before credentials are validated | Custom validation, rate limiting, block certain users |
| Post Authentication | After successful authentication | Log sign-in events, update last-login timestamp |

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · references/lambda-triggers.md (reported line 43)May include surrounding context.

Pre Sign-Up

Fires before Cognito creates the user. You can auto-confirm, auto-verify, or reject the sign-up.

typescript
export const handler = async (event: any) => {

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · references/lambda-triggers.md (reported line 85)May include surrounding context.

Pre Sign-Up

Fires before Cognito creates the user. You can auto-confirm, auto-verify, or reject the sign-up.

typescript
export const handler = async (event: any) => {

Static analysis

No suspicious patterns detected.