In progress
This commit is contained in:
162
colaflow-api/SECURITY-FIX-CROSS-TENANT-ACCESS.md
Normal file
162
colaflow-api/SECURITY-FIX-CROSS-TENANT-ACCESS.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# SECURITY FIX: Cross-Tenant Access Validation
|
||||
|
||||
## Executive Summary
|
||||
|
||||
**Status**: IMPLEMENTED ✓
|
||||
**Priority**: CRITICAL
|
||||
**Date**: 2025-11-03
|
||||
**Modified Files**: 1
|
||||
**Build Status**: SUCCESS (0 warnings, 0 errors)
|
||||
|
||||
## Security Vulnerability
|
||||
|
||||
### Issue Identified
|
||||
During Day 6 integration testing, a critical security gap was discovered in the Role Management API (`TenantUsersController`):
|
||||
|
||||
**Vulnerability**: Users from Tenant A could access and potentially manage Tenant B's users and roles by manipulating the `tenantId` route parameter.
|
||||
|
||||
**Impact**:
|
||||
- Unauthorized access to other tenants' user lists
|
||||
- Potential unauthorized role assignments across tenants
|
||||
- Breach of multi-tenant data isolation principles
|
||||
|
||||
**Severity**: HIGH - Violates fundamental security principle of tenant isolation
|
||||
|
||||
## Implementation
|
||||
|
||||
### Modified File
|
||||
`src/ColaFlow.API/Controllers/TenantUsersController.cs`
|
||||
|
||||
### Endpoints Fixed
|
||||
|
||||
| Endpoint | Method | Authorization Policy | Validation Added |
|
||||
|----------|--------|---------------------|------------------|
|
||||
| `/api/tenants/{tenantId}/users` | GET | RequireTenantAdmin | ✓ Yes |
|
||||
| `/api/tenants/{tenantId}/users/{userId}/role` | POST | RequireTenantOwner | ✓ Yes |
|
||||
| `/api/tenants/{tenantId}/users/{userId}` | DELETE | RequireTenantOwner | ✓ Yes |
|
||||
| `/api/tenants/../roles` | GET | RequireTenantAdmin | N/A (Static data) |
|
||||
|
||||
### Validation Logic
|
||||
|
||||
Each protected endpoint now includes:
|
||||
|
||||
```csharp
|
||||
// SECURITY: Validate user belongs to target tenant
|
||||
var userTenantIdClaim = User.FindFirst("tenant_id")?.Value;
|
||||
if (userTenantIdClaim == null)
|
||||
return Unauthorized(new { error = "Tenant information not found in token" });
|
||||
|
||||
var userTenantId = Guid.Parse(userTenantIdClaim);
|
||||
if (userTenantId != tenantId)
|
||||
return StatusCode(403, new { error = "Access denied: You can only manage users in your own tenant" });
|
||||
```
|
||||
|
||||
### Security Flow
|
||||
|
||||
1. **Extract JWT Claim**: Read `tenant_id` from authenticated user's JWT token
|
||||
2. **Claim Validation**: Return 401 Unauthorized if `tenant_id` claim is missing
|
||||
3. **Tenant Matching**: Compare user's tenant ID with route parameter `tenantId`
|
||||
4. **Access Control**: Return 403 Forbidden if tenant IDs don't match
|
||||
5. **Proceed**: Continue to business logic only if validation passes
|
||||
|
||||
## Expected Behavior After Fix
|
||||
|
||||
### Scenario 1: Same Tenant Access (Authorized)
|
||||
```
|
||||
User: Tenant A Admin (tenant_id = "aaaa-1111")
|
||||
Request: GET /api/tenants/aaaa-1111/users
|
||||
Result: 200 OK + User list
|
||||
```
|
||||
|
||||
### Scenario 2: Cross-Tenant Access (Blocked)
|
||||
```
|
||||
User: Tenant A Admin (tenant_id = "aaaa-1111")
|
||||
Request: GET /api/tenants/bbbb-2222/users
|
||||
Result: 403 Forbidden + Error message
|
||||
```
|
||||
|
||||
### Scenario 3: Missing Tenant Claim (Invalid Token)
|
||||
```
|
||||
User: Token without tenant_id claim
|
||||
Request: GET /api/tenants/aaaa-1111/users
|
||||
Result: 401 Unauthorized + Error message
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
### Build Status
|
||||
```
|
||||
Build succeeded.
|
||||
0 Warning(s)
|
||||
0 Error(s)
|
||||
|
||||
Time Elapsed 00:00:02.24
|
||||
```
|
||||
|
||||
### Code Quality
|
||||
- ✓ Consistent validation pattern across all endpoints
|
||||
- ✓ Clear security comments explaining purpose
|
||||
- ✓ Proper HTTP status codes (401 vs 403)
|
||||
- ✓ Descriptive error messages
|
||||
- ✓ No code duplication (same pattern repeated)
|
||||
|
||||
## Technical Details
|
||||
|
||||
### JWT Claim Structure
|
||||
The `tenant_id` claim is added by `JwtService.GenerateToken()`:
|
||||
```csharp
|
||||
new("tenant_id", tenant.Id.ToString())
|
||||
```
|
||||
|
||||
Location: `src/Modules/Identity/ColaFlow.Modules.Identity.Infrastructure/Services/JwtService.cs` (Line 34)
|
||||
|
||||
### Authorization Policies (Unchanged)
|
||||
Existing policies remain in place:
|
||||
- `RequireTenantOwner` - Checks for TenantOwner role
|
||||
- `RequireTenantAdmin` - Checks for TenantAdmin or TenantOwner role
|
||||
|
||||
**Important**: These policies validate **roles**, while the new validation checks **tenant membership**.
|
||||
|
||||
### Why GetAvailableRoles Doesn't Need Validation
|
||||
The `/api/tenants/../roles` endpoint returns static role definitions (TenantOwner, TenantAdmin, etc.) and doesn't access tenant-specific data. The existing `RequireTenantAdmin` policy is sufficient.
|
||||
|
||||
## Security Best Practices Followed
|
||||
|
||||
1. **Defense in Depth**: Validation at API layer before business logic
|
||||
2. **Fail Securely**: Return 403 Forbidden on mismatch (don't reveal if tenant exists)
|
||||
3. **Clear Error Messages**: Help legitimate users understand authorization failures
|
||||
4. **Consistent Implementation**: Same pattern across all endpoints
|
||||
5. **Minimal Changes**: API-layer validation only, no changes to command handlers or repositories
|
||||
|
||||
## Remaining Work
|
||||
|
||||
### Testing Recommendations
|
||||
1. **Unit Tests**: Add tests for tenant validation logic
|
||||
2. **Integration Tests**: Update `RoleManagementTests.cs` to verify cross-tenant blocking
|
||||
3. **Security Testing**: Penetration test with cross-tenant attack scenarios
|
||||
|
||||
### Future Enhancements
|
||||
1. **Centralized Validation**: Consider extracting to an action filter or middleware
|
||||
2. **Audit Logging**: Log all 403 responses for security monitoring
|
||||
3. **Rate Limiting**: Add rate limiting on 403 responses to prevent tenant enumeration
|
||||
|
||||
## References
|
||||
|
||||
- **Original Report**: `DAY6-TEST-REPORT.md` - Section "Cross-Tenant Access Validation"
|
||||
- **JWT Service**: `src/Modules/Identity/ColaFlow.Modules.Identity.Infrastructure/Services/JwtService.cs`
|
||||
- **Modified Controller**: `src/ColaFlow.API/Controllers/TenantUsersController.cs`
|
||||
- **Authorization Policies**: `src/ColaFlow.API/Program.cs` (Lines configuring authorization)
|
||||
|
||||
## Sign-Off
|
||||
|
||||
**Implemented By**: Backend Agent (Claude Code)
|
||||
**Reviewed By**: Pending code review
|
||||
**Status**: Ready for integration testing
|
||||
**Next Steps**:
|
||||
1. User to commit the staged changes (1Password SSH signing required)
|
||||
2. Add integration tests to verify cross-tenant blocking
|
||||
3. Deploy to staging environment for security testing
|
||||
|
||||
---
|
||||
|
||||
**Note**: The implementation is complete and builds successfully. The file is staged for commit but cannot be committed automatically due to 1Password SSH signing configuration requiring user interaction.
|
||||
Reference in New Issue
Block a user