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

14 KiB

Security Testing Guide

This guide provides step-by-step instructions for performing the manual security tests from references/MANUAL_SECURITY_TESTING_CHECKLIST.md.

Prerequisites

  • You are logged in as User A (ID: 5195)
  • User B (ID: 5196) exists as the victim account
  • Organization A and Organization B exist
  • Bank A exists (optional)

Test Accounts

Profile ID Email
User A (you) 5195 user-a@test.nl
User B (victim) 5196 user-b@test.nl
Organization A TBD TBD
Organization B TBD TBD

How to Manipulate Session

Since Laravel stores sessions server-side (not in browser Session Storage), use this command:

cd /home/r/Websites/timebank_cc_2
php manipulate-session.php <profile_id> <type>

Examples:

# Change to User B
php manipulate-session.php 5196 user

# Change to Organization A (ID 1)
php manipulate-session.php 1 org

# Change to Bank A (ID 1)
php manipulate-session.php 1 bank

# Change back to User A
php manipulate-session.php 5195 user

After running the command, refresh your browser to apply the changes.


Test Category 1: Profile Deletion Authorization

Test 1.1: Unauthorized User Profile Deletion

Risk Level: CRITICAL

Steps:

  1. Login as User A (already done)
  2. Note User A's profile ID: 5195
  3. Run session manipulation:
    php manipulate-session.php 5196 user
    
  4. Refresh browser
  5. Navigate to Settings → Delete Account
  6. Attempt to delete the profile

Expected Result: HTTP 403 Forbidden error Security Failure: Profile deletion succeeds

Log Verification:

tail -f storage/logs/laravel.log | grep "ProfileAuthorizationHelper"

Should show: "Unauthorized User access attempt"

Reset Session After Test:

php manipulate-session.php 5195 user  # Change back to User A

Test 1.2: Unauthorized Organization Profile Deletion

Risk Level: CRITICAL

Prerequisites: Find Organization A and B IDs first:

mysql -u timebank_cc_dev -p'zea2A8sd{QA,9^pS*2^@Xcltuk.vgV' timebank_cc_2 -e \
"SELECT id, name, email FROM organizations WHERE name LIKE '%Organization%' LIMIT 5;"

Steps:

  1. Login as User A (member of Organization A)
  2. Switch to Organization A profile using the profile switcher
  3. Note Organization A's ID
  4. Run session manipulation:
    php manipulate-session.php <org_b_id> org
    
  5. Refresh browser
  6. Navigate to organization settings → Delete Account
  7. Attempt deletion

Expected Result: HTTP 403 Forbidden Security Failure: Organization B deleted

Reset:

php manipulate-session.php 5195 user

Test 1.3: Legitimate Profile Deletion (Control Test)

Purpose: Verify legitimate operations still work

Steps:

  1. Login as User A
  2. Ensure session is NOT manipulated (reset if needed):
    php manipulate-session.php 5195 user
    
  3. Refresh browser
  4. Navigate to Settings → Delete Account
  5. Complete deletion process (⚠️ Use a test account, not your main account!)

Expected Result: Profile deletion succeeds Security Failure: Legitimate deletion blocked


Test Category 2: Profile Modification Authorization

Test 2.1: Unauthorized Profile Settings Modification

Risk Level: CRITICAL

Steps:

  1. Login as User A
  2. Navigate to profile settings page
  3. Run session manipulation:
    php manipulate-session.php 5196 user
    
  4. Refresh browser
  5. Attempt to modify profile details (name, email, about, etc.)
  6. Click Save

Expected Result: HTTP 403 Forbidden Security Failure: User B's profile modified

Reset:

php manipulate-session.php 5195 user

Test 2.2: Unauthorized Organization Settings Modification

Risk Level: CRITICAL

Steps:

  1. Login as User A
  2. Switch to Organization A
  3. Run session manipulation:
    php manipulate-session.php <org_b_id> org
    
  4. Refresh browser
  5. Navigate to organization settings
  6. Attempt to modify organization details
  7. Click Save

Expected Result: HTTP 403 Forbidden Security Failure: Organization B modified

Reset:

php manipulate-session.php 5195 user

Test Category 3: Message Settings Authorization

Test 3.1: Unauthorized Message Settings Access

Risk Level: CRITICAL

Steps:

  1. Login as User A
  2. Navigate to Settings → Message Settings
  3. Note current notification preferences
  4. Run session manipulation:
    php manipulate-session.php 5196 user
    
  5. Refresh browser
  6. Toggle notification settings (email, push, etc.)
  7. Click Save

Expected Result: HTTP 403 Forbidden Security Failure: User B's message settings changed

Reset:

php manipulate-session.php 5195 user

Test Category 4: Chat/Conversation Authorization

Test 4.1: Unauthorized Conversation Access

Risk Level: HIGH

Prerequisites:

  1. Create a conversation between User B and another user
  2. Note the conversation ID from the URL

Steps:

  1. Login as User A
  2. Manually navigate to: /chat/{conversation_id}
  3. Attempt to view conversation
  4. Attempt to send messages

Expected Result: HTTP 403 Forbidden or redirect Security Failure: User A can read/send messages


Test Category 5: Transaction/Payment Authorization

Test 5.1: Unauthorized Transaction Viewing

Risk Level: HIGH

Prerequisites:

  1. User B creates transaction with User C
  2. Note transaction ID

Steps:

  1. Login as User A
  2. Navigate to /transaction/{transaction_id}

Expected Result: HTTP 403 Forbidden Security Failure: User A can view transaction


Test 5.2: Session Manipulation for Transaction Access

Risk Level: CRITICAL

Steps:

  1. Login as User A
  2. Run session manipulation:
    php manipulate-session.php 5196 user
    
  3. Refresh browser
  4. Navigate to Transactions page
  5. Check which transactions are visible

Expected Result: Only User A's transactions visible (or 403 error) Security Failure: User B's transactions visible

Reset:

php manipulate-session.php 5195 user

Useful Commands

View Current Sessions

mysql -u timebank_cc_dev -p'zea2A8sd{QA,9^pS*2^@Xcltuk.vgV' timebank_cc_2 -e \
"SELECT id, user_id, ip_address, FROM_UNIXTIME(last_activity) as last_active \
FROM sessions WHERE last_activity > UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 2 HOUR)) \
ORDER BY last_activity DESC LIMIT 5;"

Find Profile IDs

# Users
mysql -u timebank_cc_dev -p'zea2A8sd{QA,9^pS*2^@Xcltuk.vgV' timebank_cc_2 -e \
"SELECT id, name, email FROM users WHERE name LIKE '%User%' ORDER BY id LIMIT 10;"

# Organizations
mysql -u timebank_cc_dev -p'zea2A8sd{QA,9^pS*2^@Xcltuk.vgV' timebank_cc_2 -e \
"SELECT id, name, email FROM organizations WHERE name LIKE '%Organization%' LIMIT 10;"

# Banks
mysql -u timebank_cc_dev -p'zea2A8sd{QA,9^pS*2^@Xcltuk.vgV' timebank_cc_2 -e \
"SELECT id, name, email FROM banks LIMIT 10;"

Monitor Authorization Logs

# Watch for authorization attempts
tail -f storage/logs/laravel.log | grep "ProfileAuthorizationHelper"

# Watch for unauthorized access attempts
tail -f storage/logs/laravel.log | grep -i "Unauthorized.*access attempt"

Reset Session to User A

php manipulate-session.php 5195 user

Troubleshooting

Problem: Session manipulation doesn't work Solution: Make sure you refresh the browser after running the command

Problem: Can't find profile IDs Solution: Use the MySQL commands above to query the database

Problem: Script permission denied Solution: Run chmod +x manipulate-session.php

Problem: Need to restore your original session Solution: Run php manipulate-session.php 5195 user and refresh browser


Important Notes

  1. Always refresh your browser after manipulating the session
  2. Reset your session back to User A after each test: php manipulate-session.php 5195 user
  3. Monitor logs during testing: tail -f storage/logs/laravel.log | grep ProfileAuthorizationHelper
  4. Don't delete important accounts - use test accounts for deletion tests
  5. These tests are for security testing only - never use in production

Test Category 6: AccountInfoModal — IDOR (Balance Leakage)

Test 6.1: Unauthorized Balance Viewing via Session Manipulation

Risk Level: HIGH — Fixed 2026-03-23 Component: app/Http/Livewire/AccountInfoModal.php Fix: ProfileAuthorizationHelper::authorize($profile) added to loadAccounts() after profile resolution. Manipulated session now returns HTTP 403.

Steps:

  1. Login as User A
  2. Open the Account Info modal (click the balance link in the navigation) — note your own balances
  3. Run session manipulation:
    php manipulate-session.php 5196 user
    
  4. Refresh browser
  5. Open the Account Info modal again

Expected Result: HTTP 403 Forbidden OR modal shows zero/no accounts Security Failure: User B's account balances are visible

Log Verification:

tail -f storage/logs/laravel.log | grep "ProfileAuthorizationHelper"

Reset:

php manipulate-session.php 5195 user

Test 6.2: Cross-Profile-Type Balance Leakage (Organization)

Risk Level: HIGH

Steps:

  1. Login as User A
  2. Run session manipulation to switch to an organization you are NOT a member of:
    php manipulate-session.php <org_b_id> org
    
  3. Refresh browser
  4. Open the Account Info modal

Expected Result: HTTP 403 Forbidden OR modal shows zero accounts Security Failure: Organization B's account balances are visible

Reset:

php manipulate-session.php 5195 user

Test Category 7: Reports — Arbitrary File Write

Test 7.1: Malformed Base64 Chart Image Upload

Risk Level: MEDIUM — Fixed 2026-03-23 Component: app/Http/Livewire/Reports.phpexportPdfWithChart() / exportPdfWithCharts() Fix: decodeChartImage() helper validates PNG/JPEG magic bytes before writing. base64_decode(..., strict: true) used to reject malformed input. Non-image payloads abort with HTTP 422 and are logged.

Steps:

  1. Login as User A
  2. Navigate to the Reports page
  3. Open browser DevTools → Network tab
  4. Trigger any PDF export that invokes exportPdfWithChart
  5. Locate the Livewire POST request and copy the payload
  6. Modify the chartImage parameter to contain a PHP webshell encoded as base64:
    data:image/png;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+
    
    (This decodes to <?php system($_GET['cmd']); ?>)
  7. Re-send the modified request (use DevTools "Copy as fetch" then paste in Console)

Expected Result: Request rejected (invalid MIME type), or file written but not web-executable Security Failure: PHP file written to an accessible path and the application executes it

Verify storage is not web-accessible:

curl -I http://localhost/storage/temp/
# Should return 404 or 403, not 200

Test 7.2: Unauthenticated Livewire Chart Export Action

Risk Level: MEDIUM — Fixed 2026-03-23 (abort_unless(Auth::check(), 403) added)

Steps:

  1. Log out entirely
  2. POST a Livewire request to exportPdfWithChart with a valid-looking base64 payload

Expected Result: Redirected to login, or 401/403 response Security Failure: File written to temp storage without authentication


Test Category 8: ExportProfileData — Authorization Verification

Test 8.1: Unauthorized Transaction Export via Session Manipulation

Risk Level: HIGH Component: app/Http/Livewire/Profile/ExportProfileData.phpexportTransactions() Note: This component DOES call ProfileAuthorizationHelper::authorize(). This test verifies the protection works correctly.

Steps:

  1. Login as User A
  2. Navigate to Profile → Export Data
  3. Run session manipulation:
    php manipulate-session.php 5196 user
    
  4. Refresh browser
  5. Attempt to export transactions (any format)

Expected Result: HTTP 403 Forbidden Security Failure: User B's transactions are exported

Log Verification:

tail -f storage/logs/laravel.log | grep "ProfileAuthorizationHelper"

Should show: "Unauthorized User profile access attempt"

Reset:

php manipulate-session.php 5195 user

Test 8.2: Unauthorized Messages Export

Risk Level: HIGH

Steps:

  1. Login as User A
  2. Run session manipulation:
    php manipulate-session.php 5196 user
    
  3. Refresh browser
  4. Attempt to export messages

Expected Result: HTTP 403 Forbidden Security Failure: User B's private messages exported

Reset:

php manipulate-session.php 5195 user

Test 8.3: Unauthorized Contacts Export

Risk Level: MEDIUM

Steps:

  1. Login as User A
  2. Run session manipulation:
    php manipulate-session.php 5196 user
    
  3. Refresh browser
  4. Attempt to export contacts

Expected Result: HTTP 403 Forbidden Security Failure: User B's contact list exported

Reset:

php manipulate-session.php 5195 user

Quick Reference

Action Command
Change to User B php manipulate-session.php 5196 user
Change to Org B (ID 1) php manipulate-session.php 1 org
Change to Bank A (ID 1) php manipulate-session.php 1 bank
Reset to User A php manipulate-session.php 5195 user
Monitor logs tail -f storage/logs/laravel.log | grep ProfileAuthorizationHelper
Check temp dir not web-accessible curl -I http://localhost/storage/temp/