Files
timebank-cc-public/references/ADMIN_MANAGEMENT_SECURITY_ANALYSIS_2025-12-31.md
Ronald Huynen 2547717edb Initial commit
2026-03-23 21:37:59 +01:00

18 KiB
Raw Blame History

Admin Management Components Security Analysis

Date: 2025-12-31 Scope: Posts, Categories, Tags, Profiles, and Mailings Management Status: ⚠️ PARTIAL PROTECTION - CRITICAL VULNERABILITIES FOUND


Executive Summary

STATUS: ⚠️ SECURITY GAPS IDENTIFIED

Analysis of 5 admin management Livewire components reveals significant authorization vulnerabilities. While some components have basic authentication checks, NONE of them use ProfileAuthorizationHelper for IDOR protection, and most lack proper admin permission verification.

Critical Finding: Users can potentially access admin management interfaces by manipulating session variables, as authorization is checked inconsistently across components.


Components Analyzed

  1. Posts/Manage.php - Line 1364 (NO authorization checks)
  2. Categories/Manage.php - Line 1017 (NO authorization checks)
  3. Tags/Manage.php - Line 735 (NO authorization checks)
  4. Profiles/Manage.php - Line 1840 (NO ProfileAuthorizationHelper)
  5. Mailings/Manage.php - Line 1145 (Basic guard check only)

Security Analysis by Component

1. Posts/Manage.php CRITICAL VULNERABILITY

File: app/Http/Livewire/Posts/Manage.php Lines: 1-1364

Authorization Status: NO AUTHORIZATION CHECKS

Vulnerabilities:

  • No mount() authorization - Anyone can access the component
  • No ProfileAuthorizationHelper usage
  • No admin permission checks
  • No guard verification

Attack Scenarios:

// Attack: Regular user accesses admin post management
// 1. User authenticates as regular user on 'web' guard
// 2. User manipulates session to access admin area
// 3. User can view/edit/delete all posts system-wide
// 4. NO PROTECTION - Component allows full access

Exposed Methods (All Unprotected):

  • edit($translationId) - Line 301 - Can edit ANY post by ID
  • save() - Line 409 - Can modify ANY post
  • deleteSelected() - Line 742 - Can bulk delete posts
  • undeleteSelected() - Line 773 - Can restore posts
  • stopPublication() - Line 884 - Can unpublish any post
  • startPublication() - Line 912 - Can publish any post

Impact: CRITICAL - Complete unauthorized access to post management


2. Categories/Manage.php CRITICAL VULNERABILITY

File: app/Http/Livewire/Categories/Manage.php Lines: 1-1017

Authorization Status: NO AUTHORIZATION CHECKS

Vulnerabilities:

  • No mount() authorization - Anyone can access
  • No ProfileAuthorizationHelper usage
  • No admin permission checks
  • No guard verification

Attack Scenarios:

// Attack: Regular user manages categories
// 1. User accesses category management without admin rights
// 2. Can view all categories and their translations
// 3. Can edit/delete categories (affecting tags system-wide)
// 4. Can create new categories
// 5. NO PROTECTION - Full category system access

Exposed Methods (All Unprotected):

  • openBulkDeleteTranslationsModal() - Line 140
  • deleteSelected() - Line 232 - Deletes categories with tag reassignment
  • deleteCategory() - Line 418 - Deletes single category
  • updateCategory() - Line 547 - Modifies categories
  • storeCategory() - Line 682 - Creates new categories

Impact: CRITICAL - Unauthorized category management affects entire tag system


3. Tags/Manage.php CRITICAL VULNERABILITY

File: app/Http/Livewire/Tags/Manage.php Lines: 1-735

Authorization Status: NO AUTHORIZATION CHECKS

Vulnerabilities:

  • No mount() authorization - Anyone can access
  • No ProfileAuthorizationHelper usage
  • No admin permission checks
  • No guard verification

Attack Scenarios:

// Attack: Regular user manages tags
// 1. User accesses tag management interface
// 2. Can view all tags with user counts
// 3. Can edit tags (affecting all profiles using them)
// 4. Can merge tags (changing profile tags globally)
// 5. Can delete tags (removing skills from profiles)
// 6. NO PROTECTION - Complete tag system access

Exposed Methods (All Unprotected):

  • openDeleteTagModal($tagId) - Line 124
  • deleteTag() - Line 320 - Deletes tags
  • openBulkDeleteTagsModal() - Line 137
  • deleteSelected() - Line 353 - Bulk deletes tags
  • openEditTagModal($tagId) - Line 193
  • updateTag() - Line 453 - Modifies/merges tags (affects ALL profiles)

Special Concern - Tag Merging:

// Line 461-479: Tag merging without authorization
// This affects ALL profiles that have the merged tag
DB::table('taggable_taggables')
    ->where('tag_id', $this->selectedTagId)
    ->update(['tag_id' => $mergeTagId]);

Impact: CRITICAL - Tag modifications affect all user profiles system-wide


4. Profiles/Manage.php ⚠️ INSUFFICIENT PROTECTION

File: app/Http/Livewire/Profiles/Manage.php Lines: 1-1840

Authorization Status: ⚠️ BASIC CHECK ONLY - NO IDOR PROTECTION

Current Protection (Lines 96-102):

public function mount()
{
    // Check authorization
    if (!Auth::guard('admin')->check() && !Auth::guard('bank')->check()) {
        abort(403, __('Unauthorized to access mailings management.'));
    }

    // ... initialization code
}

Vulnerabilities:

  • Has basic guard check in mount()
  • No ProfileAuthorizationHelper usage
  • No verification that admin accessing their own admin profile
  • Allows Bank level=1 to access (not restricted to level=0 central bank)

Attack Scenarios:

// Attack 1: Cross-guard attack (similar to ExportProfileData issue)
// 1. User authenticated on 'web' guard
// 2. User IS a manager of Bank ID 1
// 3. User manipulates session to active Bank profile
// 4. mount() check PASSES (Bank guard exists)
// 5. But user is on WRONG GUARD (web instead of bank)
// 6. Can access profile management as if they were Bank
// Result: Unauthorized access to profile management

// Attack 2: Bank level 1 accessing admin functions
// 1. Bank level 1 authenticates on 'bank' guard
// 2. mount() check PASSES (Bank guard exists)
// 3. Bank level 1 shouldn't have admin access
// Result: Non-central banks can manage all profiles

Exposed Methods (Insufficient Protection):

  • openEditAccountsModal($profileId, $modelName) - Line 216
  • openEditProfileModal($profileId, $modelName) - Line 258
  • updateProfile() - Line 631 - Can edit ANY profile
  • deleteProfile() - Line 1386 - Can delete profiles
  • restoreProfile() - Line 1493 - Can restore deleted profiles

Missing Validation:

  • No check that admin is acting on behalf of their own Admin profile
  • No verification of cross-guard attacks
  • No restriction for Bank level (should be level=0 only)

Impact: HIGH - Partial protection but vulnerable to guard manipulation


5. Mailings/Manage.php ⚠️ INSUFFICIENT PROTECTION

File: app/Http/Livewire/Mailings/Manage.php Lines: 1-1145

Authorization Status: ⚠️ BASIC CHECK ONLY - NO IDOR PROTECTION

Current Protection (Lines 96-102):

public function mount()
{
    // Check authorization
    if (!Auth::guard('admin')->check() && !Auth::guard('bank')->check()) {
        abort(403, __('Unauthorized to access mailings management.'));
    }

    $this->estimatedRecipientCount = 0;
}

Vulnerabilities:

  • Has basic guard check in mount()
  • No ProfileAuthorizationHelper usage
  • No verification of cross-guard attacks
  • Allows Bank access without level verification

Attack Scenarios:

// Attack: Cross-guard mailing access
// 1. User authenticated on 'web' guard
// 2. User is manager of Bank ID 1
// 3. User manipulates session: activeProfileType = Bank, activeProfileId = 1
// 4. mount() check PASSES because Bank guard exists
// 5. User on WRONG GUARD accesses mailing management
// Result: Unauthorized mailing access

Exposed Methods (Insufficient Protection):

  • openCreateModal() - Line 163
  • openEditModal($mailingId) - Line 170
  • saveMailing() - Line 418 - Creates/updates mailings
  • deleteMailing($mailingId) - Line 485
  • sendMailing() - Line 527 - Sends emails to users
  • sendTestMail($mailingId) - Line 611

Missing Validation:

  • No cross-guard attack prevention
  • No admin profile ownership verification
  • Bank access not restricted to central bank only

Impact: HIGH - Can create/send mass mailings without proper authorization


Common Vulnerabilities Across All Components

1. Missing ProfileAuthorizationHelper Integration

Issue: None of the 5 components use ProfileAuthorizationHelper Impact: No IDOR protection, no cross-guard validation

2. Inconsistent Authorization Checks

Issue: Only 2/5 components have ANY authorization (Profiles and Mailings) Impact: 3 components (Posts, Categories, Tags) are completely unprotected

3. No Active Profile Verification

Issue: Components don't verify that active profile matches authenticated profile Impact: Cross-guard attacks possible (similar to ExportProfileData vulnerability)

4. Bank Level Not Validated

Issue: Bank level 1 can access admin functions Impact: Non-central banks have admin privileges


Priority 1: CRITICAL (Posts, Categories, Tags)

Add ProfileAuthorizationHelper to mount():

public function mount()
{
    // Get active profile from session
    $activeProfileType = session('activeProfileType');
    $activeProfileId = session('activeProfileId');

    if (!$activeProfileType || !$activeProfileId) {
        abort(403, __('No active profile selected'));
    }

    $profile = $activeProfileType::find($activeProfileId);

    if (!$profile) {
        abort(403, __('Active profile not found'));
    }

    // Validate profile ownership using ProfileAuthorizationHelper
    \App\Helpers\ProfileAuthorizationHelper::authorize($profile);

    // Verify admin permissions
    if (!($profile instanceof \App\Models\Admin)) {
        abort(403, __('Admin access required'));
    }

    // Additional initialization...
}

Priority 2: HIGH (Profiles, Mailings)

Enhance existing mount() with ProfileAuthorizationHelper:

public function mount()
{
    // Get active profile
    $activeProfileType = session('activeProfileType');
    $activeProfileId = session('activeProfileId');

    if (!$activeProfileType || !$activeProfileId) {
        abort(403, __('No active profile selected'));
    }

    $profile = $activeProfileType::find($activeProfileId);

    if (!$profile) {
        abort(403, __('Active profile not found'));
    }

    // Use ProfileAuthorizationHelper for cross-guard protection
    \App\Helpers\ProfileAuthorizationHelper::authorize($profile);

    // Verify admin or central bank access
    if ($profile instanceof \App\Models\Admin) {
        // Admin access OK
    } elseif ($profile instanceof \App\Models\Bank) {
        // Only central bank (level 0)
        if ($profile->level !== 0) {
            abort(403, __('Central bank access required'));
        }
    } else {
        abort(403, __('Admin or central bank access required'));
    }

    // Continue with initialization...
}

Additional Protection: Route Middleware

Create Admin Authorization Middleware:

// app/Http/Middleware/RequireAdminProfile.php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use App\Helpers\ProfileAuthorizationHelper;

class RequireAdminProfile
{
    public function handle(Request $request, Closure $next)
    {
        $activeProfileType = session('activeProfileType');
        $activeProfileId = session('activeProfileId');

        if (!$activeProfileType || !$activeProfileId) {
            abort(403, __('No active profile selected'));
        }

        $profile = $activeProfileType::find($activeProfileId);

        if (!$profile) {
            abort(403, __('Active profile not found'));
        }

        // Use ProfileAuthorizationHelper
        ProfileAuthorizationHelper::authorize($profile);

        // Verify admin or central bank
        if ($profile instanceof \App\Models\Admin) {
            return $next($request);
        }

        if ($profile instanceof \App\Models\Bank && $profile->level === 0) {
            return $next($request);
        }

        abort(403, __('Admin or central bank access required'));
    }
}

Apply to routes:

// routes/web.php
Route::middleware(['auth', 'admin-profile'])->group(function () {
    Route::get('/posts/manage', \App\Http\Livewire\Posts\Manage::class);
    Route::get('/categories/manage', \App\Http\Livewire\Categories\Manage::class);
    Route::get('/tags/manage', \App\Http\Livewire\Tags\Manage::class);
    Route::get('/profiles/manage', \App\Http\Livewire\Profiles\Manage::class);
    Route::get('/mailings/manage', \App\Http\Livewire\Mailings\Manage::class);
});

Test Coverage Recommendations

Posts Management Authorization Tests

// tests/Feature/Security/Authorization/PostsManageAuthorizationTest.php

/** @test */
public function admin_can_access_posts_management()
{
    $admin = Admin::factory()->create();

    $this->actingAs($admin, 'admin');
    session(['activeProfileType' => Admin::class, 'activeProfileId' => $admin->id]);

    $response = Livewire::test(\App\Http\Livewire\Posts\Manage::class);

    $response->assertStatus(200);
}

/** @test */
public function user_cannot_access_posts_management()
{
    $user = User::factory()->create();

    $this->actingAs($user, 'web');
    session(['activeProfileType' => User::class, 'activeProfileId' => $user->id]);

    $response = Livewire::test(\App\Http\Livewire\Posts\Manage::class);

    $response->assertStatus(403);
}

/** @test */
public function web_user_cannot_access_admin_posts_via_cross_guard_attack()
{
    $user = User::factory()->create();
    $admin = Admin::factory()->create();
    $admin->users()->attach($user->id); // User is linked to admin

    // User authenticated on 'web' guard
    $this->actingAs($user, 'web');

    // Malicious: manipulate session to target admin profile
    session(['activeProfileType' => Admin::class, 'activeProfileId' => $admin->id]);

    $response = Livewire::test(\App\Http\Livewire\Posts\Manage::class);

    $response->assertStatus(403); // Should be blocked by ProfileAuthorizationHelper
}

/** @test */
public function admin_cannot_edit_post_without_proper_authorization()
{
    $admin1 = Admin::factory()->create();
    $admin2 = Admin::factory()->create();

    $post = Post::factory()->create();
    $translation = $post->translations()->first();

    $this->actingAs($admin1, 'admin');
    session(['activeProfileType' => Admin::class, 'activeProfileId' => $admin1->id]);

    // Attempt to manipulate session to access as different admin
    session(['activeProfileId' => $admin2->id]);

    $response = Livewire::test(\App\Http\Livewire\Posts\Manage::class)
        ->call('edit', $translation->id);

    $response->assertStatus(403);
}

Similar tests needed for:

  • Categories Management (30+ tests)
  • Tags Management (30+ tests)
  • Profiles Management (40+ tests)
  • Mailings Management (25+ tests)

Total Recommended Tests: ~150 authorization tests across 5 components


Security Logging Recommendations

Add logging to all admin operations:

// In mount() after authorization
Log::info('Admin management access', [
    'component' => get_class($this),
    'admin_id' => $profile->id,
    'admin_type' => get_class($profile),
    'authenticated_guard' => Auth::getDefaultDriver(),
    'ip_address' => request()->ip(),
]);

// In sensitive operations (delete, update, etc.)
Log::warning('Admin operation performed', [
    'operation' => 'delete_post',
    'admin_id' => $profile->id,
    'target_id' => $postId,
    'ip_address' => request()->ip(),
]);

Attack Surface Summary

Component Current Status Attack Vectors Impact Level
Posts/Manage No protection Session manipulation, Direct access CRITICAL
Categories/Manage No protection Session manipulation, Direct access CRITICAL
Tags/Manage No protection Session manipulation, Direct access CRITICAL
Profiles/Manage ⚠️ Basic check Cross-guard attack, Bank level bypass HIGH
Mailings/Manage ⚠️ Basic check Cross-guard attack, Bank level bypass HIGH

Compliance Impact

OWASP Top 10 2021

A01:2021 Broken Access Control

  • Admin interfaces lack proper authorization
  • Cross-guard attacks possible
  • No IDOR protection on management endpoints

CWE Coverage

CWE-639: Authorization Bypass Through User-Controlled Key

  • Session manipulation allows unauthorized access

CWE-284: Improper Access Control

  • Missing admin permission verification
  • No cross-guard validation

GDPR Compliance

⚠️ Data Protection (Article 32)

  • Admin access to all user data not properly secured
  • No audit trail for admin actions

Production Deployment Blockers

DO NOT DEPLOY TO PRODUCTION until:

  • Posts/Manage.php has ProfileAuthorizationHelper integration
  • Categories/Manage.php has ProfileAuthorizationHelper integration
  • Tags/Manage.php has ProfileAuthorizationHelper integration
  • Profiles/Manage.php enhanced with ProfileAuthorizationHelper
  • Mailings/Manage.php enhanced with ProfileAuthorizationHelper
  • Admin authorization middleware created and applied
  • ~150 authorization tests written and passing
  • Security audit conducted on all admin endpoints
  • Monitoring configured for admin access attempts

Conclusion

CRITICAL SECURITY GAPS IDENTIFIED:

5 admin management components analyzed:

  • 3 components (Posts, Categories, Tags) have ZERO authorization protection
  • ⚠️ 2 components (Profiles, Mailings) have insufficient protection
  • 0 components use ProfileAuthorizationHelper
  • No cross-guard attack prevention
  • No comprehensive authorization testing

The admin management system is NOT PRODUCTION READY from a security perspective.

Immediate Actions Required:

  1. Integrate ProfileAuthorizationHelper into all 5 components
  2. Add admin permission verification
  3. Implement cross-guard attack prevention
  4. Create comprehensive test suite (~150 tests)
  5. Add security logging for all admin operations
  6. Security team review before production deployment

Document Version: 1.0 Last Updated: 2025-12-31 Prepared By: Claude Code Security Audit Status: CRITICAL VULNERABILITIES FOUND - NOT PRODUCTION READY