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 | |
|---|---|---|
| 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:
- Login as User A (already done)
- Note User A's profile ID: 5195
- Run session manipulation:
php manipulate-session.php 5196 user - Refresh browser
- Navigate to Settings → Delete Account
- 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:
- Login as User A (member of Organization A)
- Switch to Organization A profile using the profile switcher
- Note Organization A's ID
- Run session manipulation:
php manipulate-session.php <org_b_id> org - Refresh browser
- Navigate to organization settings → Delete Account
- 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:
- Login as User A
- Ensure session is NOT manipulated (reset if needed):
php manipulate-session.php 5195 user - Refresh browser
- Navigate to Settings → Delete Account
- 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:
- Login as User A
- Navigate to profile settings page
- Run session manipulation:
php manipulate-session.php 5196 user - Refresh browser
- Attempt to modify profile details (name, email, about, etc.)
- 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:
- Login as User A
- Switch to Organization A
- Run session manipulation:
php manipulate-session.php <org_b_id> org - Refresh browser
- Navigate to organization settings
- Attempt to modify organization details
- 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:
- Login as User A
- Navigate to Settings → Message Settings
- Note current notification preferences
- Run session manipulation:
php manipulate-session.php 5196 user - Refresh browser
- Toggle notification settings (email, push, etc.)
- 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:
- Create a conversation between User B and another user
- Note the conversation ID from the URL
Steps:
- Login as User A
- Manually navigate to:
/chat/{conversation_id} - Attempt to view conversation
- 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:
- User B creates transaction with User C
- Note transaction ID
Steps:
- Login as User A
- 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:
- Login as User A
- Run session manipulation:
php manipulate-session.php 5196 user - Refresh browser
- Navigate to Transactions page
- 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
- Always refresh your browser after manipulating the session
- Reset your session back to User A after each test:
php manipulate-session.php 5195 user - Monitor logs during testing:
tail -f storage/logs/laravel.log | grep ProfileAuthorizationHelper - Don't delete important accounts - use test accounts for deletion tests
- 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:
- Login as User A
- Open the Account Info modal (click the balance link in the navigation) — note your own balances
- Run session manipulation:
php manipulate-session.php 5196 user - Refresh browser
- 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:
- Login as User A
- Run session manipulation to switch to an organization you are NOT a member of:
php manipulate-session.php <org_b_id> org - Refresh browser
- 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.php — exportPdfWithChart() / 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:
- Login as User A
- Navigate to the Reports page
- Open browser DevTools → Network tab
- Trigger any PDF export that invokes
exportPdfWithChart - Locate the Livewire POST request and copy the payload
- Modify the
chartImageparameter to contain a PHP webshell encoded as base64:(This decodes todata:image/png;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+<?php system($_GET['cmd']); ?>) - 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:
- Log out entirely
- POST a Livewire request to
exportPdfWithChartwith 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.php — exportTransactions()
Note: This component DOES call ProfileAuthorizationHelper::authorize(). This test verifies the protection works correctly.
Steps:
- Login as User A
- Navigate to Profile → Export Data
- Run session manipulation:
php manipulate-session.php 5196 user - Refresh browser
- 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:
- Login as User A
- Run session manipulation:
php manipulate-session.php 5196 user - Refresh browser
- 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:
- Login as User A
- Run session manipulation:
php manipulate-session.php 5196 user - Refresh browser
- 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/ |