Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -792,8 +792,13 @@ OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
# Set to false for custom auth flows where issuer varies or is not present
# JWT_ISSUER_VERIFICATION=true

# Expiry time for generated JWT tokens (in minutes; e.g. 7 days)
# TOKEN_EXPIRY=10080
# Session token expiration in minutes (X-Force Red Security Audit Fix)
# - Default changed from 10080 (7 days) to 15 minutes per X-Force Red penetration test findings
# - Recommended range: 5-20 minutes for session tokens
# - For automation/long-lived access: Use API tokens (POST /tokens with expires_in_days)
# - Session tokens are for interactive web UI login only
# - WARNING: Values >20 minutes trigger security warnings
TOKEN_EXPIRY=15

# SECURITY: Require expiration claim in all tokens (default: true)
# Set to false only for backward compatibility with legacy tokens
Expand Down
50 changes: 25 additions & 25 deletions .secrets.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -108,71 +108,71 @@
"hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3",
"is_secret": false,
"is_verified": false,
"line_number": 1011,
"line_number": 1016,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "7b4455a56fbf1d198e45e04c437488514645a82c",
"is_secret": false,
"is_verified": false,
"line_number": 1037,
"line_number": 1042,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "ac371b6dcce28a86c90d12bc57d946a800eebf17",
"is_secret": false,
"is_verified": false,
"line_number": 1117,
"line_number": 1122,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "0b6ec68df700dec4dcd64babd0eda1edccddace1",
"is_secret": false,
"is_verified": false,
"line_number": 1122,
"line_number": 1127,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "4ad6f0082ee224001beb3ca5c3e81c8ceea5ed86",
"is_secret": false,
"is_verified": false,
"line_number": 1127,
"line_number": 1132,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "cb32747fcfb55eaa194c8cd8e4ba7d49ada08a94",
"is_secret": false,
"is_verified": false,
"line_number": 1133,
"line_number": 1138,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "6c178d51b13520496dbc767ed3d9d7aa5803ac72",
"is_secret": false,
"is_verified": false,
"line_number": 1145,
"line_number": 1150,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "ca45060a53fd8a255d1a83ee8d2f025283ccc66e",
"is_secret": false,
"is_verified": false,
"line_number": 1163,
"line_number": 1168,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "910fbf00f58e9bcb095ea26a75cc1d9a3355e671",
"is_secret": false,
"is_verified": false,
"line_number": 1224,
"line_number": 1229,
"type": "Secret Keyword",
"verified_result": null
}
Expand Down Expand Up @@ -242,63 +242,63 @@
"hashed_secret": "b4673e578b9b30fe8bba1b555b7b59883444c697",
"is_secret": false,
"is_verified": false,
"line_number": 765,
"line_number": 901,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "4a0a2df96d4c9a13a282268cab33ac4b8cbb2c72",
"is_secret": false,
"is_verified": false,
"line_number": 853,
"line_number": 989,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8",
"is_secret": false,
"is_verified": false,
"line_number": 1203,
"line_number": 1339,
"type": "Basic Auth Credentials",
"verified_result": null
},
{
"hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3",
"is_secret": false,
"is_verified": false,
"line_number": 2569,
"line_number": 2705,
"type": "Basic Auth Credentials",
"verified_result": null
},
{
"hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3",
"is_secret": false,
"is_verified": false,
"line_number": 2660,
"line_number": 2796,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "ac371b6dcce28a86c90d12bc57d946a800eebf17",
"is_secret": false,
"is_verified": false,
"line_number": 2703,
"line_number": 2839,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "0b6ec68df700dec4dcd64babd0eda1edccddace1",
"is_secret": false,
"is_verified": false,
"line_number": 2708,
"line_number": 2844,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "4ad6f0082ee224001beb3ca5c3e81c8ceea5ed86",
"is_secret": false,
"is_verified": false,
"line_number": 2713,
"line_number": 2849,
"type": "Secret Keyword",
"verified_result": null
}
Expand Down Expand Up @@ -4170,23 +4170,23 @@
"hashed_secret": "559b05f1b2863e725b76e216ac3dadecbf92e244",
"is_secret": false,
"is_verified": false,
"line_number": 4788,
"line_number": 4842,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "a8af4759392d4f7496d613174f33afe2074a4b8d",
"is_secret": false,
"is_verified": false,
"line_number": 4790,
"line_number": 4844,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "85b60d811d16ff56b3654587d4487f713bfa33b7",
"is_secret": false,
"is_verified": false,
"line_number": 15116,
"line_number": 15170,
"type": "Secret Keyword",
"verified_result": null
}
Expand Down Expand Up @@ -4880,7 +4880,7 @@
"hashed_secret": "ff37a98a9963d347e9749a5c1b3936a4a245a6ff",
"is_secret": false,
"is_verified": false,
"line_number": 2297,
"line_number": 2305,
"type": "Secret Keyword",
"verified_result": null
}
Expand Down Expand Up @@ -4982,7 +4982,7 @@
"hashed_secret": "aa1a82fe15c74459f1261961b07ae924e2b94ab2",
"is_secret": false,
"is_verified": false,
"line_number": 150,
"line_number": 154,
"type": "Secret Keyword",
"verified_result": null
}
Expand Down Expand Up @@ -8898,7 +8898,7 @@
"hashed_secret": "c00dbbc9dadfbe1e232e93a729dd4752fade0abf",
"is_secret": false,
"is_verified": false,
"line_number": 1106,
"line_number": 1124,
"type": "Secret Keyword",
"verified_result": null
}
Expand Down Expand Up @@ -9152,15 +9152,15 @@
"hashed_secret": "516b9783fca517eecbd1d064da2d165310b19759",
"is_secret": false,
"is_verified": false,
"line_number": 1016,
"line_number": 1017,
"type": "Basic Auth Credentials",
"verified_result": null
},
{
"hashed_secret": "ef4eb24299c517306652ffee61e05934f2224914",
"is_secret": false,
"is_verified": false,
"line_number": 1268,
"line_number": 1269,
"type": "Secret Keyword",
"verified_result": null
}
Expand Down
136 changes: 136 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,142 @@

> All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project **adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)**.

## [Unreleased]

### ⚠️ Breaking Changes

#### **🔐 Session Token Security Fixes (X-Force Red Audit)** ([#4324](https://github.com/IBM/mcp-context-forge/issues/4324), ICACF-22)

**SECURITY**: X-Force Red penetration testing identified critical session token vulnerabilities. This release addresses both findings with breaking changes to token lifetime.

**Findings:**
1. ❌ Session tokens had excessive lifetime (~30 days vs recommended 5-20 minutes)
2. ❌ Tokens remained valid after logout, enabling replay attacks

**Fixes:**
1. ✅ **Session token lifetime reduced**: 10,080 minutes (7 days) → **15 minutes** (configurable)
2. ✅ **Server-side token revocation**: New `POST /auth/logout` endpoint + admin UI logout now revokes tokens
3. ✅ **Revocation blocklist**: Tokens immediately rejected after logout (cached in Redis)

**Action Required:**

**For Interactive Users (Web UI):**
- ✅ No action needed
- You'll be prompted to re-authenticate every 15 minutes (standard for secure applications)
- This is normal security practice (banking, enterprise SaaS)

**For API Automation:**
- ⚠️ **Scripts using session tokens will break after 15 minutes**
- **Migration**: Use API tokens instead of session tokens for automation

```bash
# ❌ OLD WAY (breaks after 15 minutes):
SESSION_TOKEN=$(curl -X POST /auth/login -d '{"email":"...","password":"..."}' | jq -r .access_token) # pragma: allowlist secret
curl -H "Authorization: Bearer $SESSION_TOKEN" /tools # Fails after 15 min

# ✅ NEW WAY (long-lived API token):
# 1. Get session token (short-lived)
SESSION_TOKEN=$(curl -X POST /auth/login -d '{"email":"...","password":"..."}' | jq -r .access_token) # pragma: allowlist secret

# 2. Create long-lived API token
API_TOKEN=$(curl -X POST /tokens \
-H "Authorization: Bearer $SESSION_TOKEN" \
-d '{"name":"ci-token","expires_in_days":90}' | jq -r .token)

# 3. Use API_TOKEN for automation (valid for 90 days)
curl -H "Authorization: Bearer $API_TOKEN" /tools
```

**Configuration:**

```bash
# .env - Session token expiry (X-Force Red recommends 5-20 minutes)
TOKEN_EXPIRY=15 # minutes (default changed from 10080)

# WARNING: Values >20 minutes trigger security warnings
# Values >24 hours trigger CRITICAL warnings
```

**Temporary Workaround (NOT RECOMMENDED for production):**

```bash
# Only for testing/migration - increases security risk
TOKEN_EXPIRY=1440 # 24 hours - will trigger security warning
```

**Why This Change?**

X-Force Red Finding: *"Token expiry was 2,592,000 seconds (~30 days). It was noted that when the user was logged out, the token was still useable."*

- Stolen/compromised session tokens were valid for 30 days
- Logout did not invalidate tokens (replay attack vulnerability)
- Industry standard: 5-20 minutes for session tokens

**Token Type Distinction:**

| Type | Created By | Lifetime | Purpose | Revocation |
|------|-----------|----------|---------|------------|
| **Session Token** | `POST /auth/login` | 15 min | Interactive UI | `POST /auth/logout` |
| **API Token** | `POST /tokens` | Days/months | Automation | `DELETE /tokens/{id}` |

**Security Benefits:**
- Stolen session tokens have 15-minute window (vs 30 days)
- Logout immediately invalidates tokens (prevents replay attacks)
- API tokens available for legitimate long-lived access

**Migration Resources:**
- Token types: Session tokens vs API tokens (see updated auth docs)
- API token creation: `POST /tokens` endpoint
- Security advisory: X-Force Red audit findings

**Testing:**

```bash
# Verify token lifetime
curl -X POST /auth/login -d '{"email":"test@example.com","password":"..."}' | jq '.expires_in' # pragma: allowlist secret
# Should return 900 (15 minutes)

# Verify logout revocation
TOKEN="..."
curl -X POST /auth/logout -H "Authorization: Bearer $TOKEN"
curl -H "Authorization: Bearer $TOKEN" /gateways
# Should return 401 Unauthorized (not 200 OK)
```

### Security

- **[SECURITY]** Fixed session token replay vulnerability after logout (X-Force Red finding) - tokens now revoked server-side via `TokenRevocation` blocklist
- **[SECURITY]** Reduced session token lifetime from 7 days (10,080 min) to 15 minutes per X-Force Red maximum recommendation (5-20 min)
- **[SECURITY]** Added validation warnings for excessive `TOKEN_EXPIRY` values (>20 min triggers warning, >24 hours triggers critical alert)
- **[SECURITY]** Added `POST /auth/logout` endpoint for programmatic token revocation with immediate Redis cache invalidation
- **[SECURITY]** Updated admin UI logout to revoke session tokens server-side before clearing cookies (prevents client-side-only logout)

### Added

- New endpoint: `POST /auth/logout` - Revoke session token with server-side blocklist (X-Force Red fix)
- Security tests: `tests/unit/mcpgateway/test_auth.py` - X-Force Red vulnerability regression tests
- Configuration validation: Startup warnings for excessive session token lifetime

### Changed

- **[BREAKING]** `TOKEN_EXPIRY` default: 10,080 minutes (7 days) → 15 minutes (X-Force Red security fix)
- `.env.example`: Updated `TOKEN_EXPIRY` documentation with session vs API token distinction
- Admin UI logout: Now revokes tokens server-side (not just cookie clearing)
- Auth flow: Existing revocation checks now enforced for session token logout (already present in codebase)

### Documentation

- Updated `.env.example` with X-Force Red security context and migration guidance
- Added inline documentation distinguishing session tokens from API tokens
- Security event logging: Logout operations tagged with `xforce_red_fix: true` for audit queries

**References:**
- X-Force Red Report: Internal security audit
- JIRA: ICACF-22
- GitHub Issue: [#4324](https://github.com/IBM/mcp-context-forge/issues/4324)

---

## [1.0.0-RC3] - 2026-04-14 - Auth Hardening, Plugin Multi-Tenancy, Rust Runtime & Multi-Arch

### Overview
Expand Down
Loading
Loading