-
-
Notifications
You must be signed in to change notification settings - Fork 0
Description
SSRF Protection Manual Test Plan
Purpose: Manual testing plan for validating SSRF protection in production-like environment.
Test Date: _____________
Tester: _____________
Environment: _____________
Charon Version: _____________
Prerequisites
Before beginning tests, ensure:
- Charon deployed in test environment
- Admin access to Charon configuration interface
- Network access to test external webhooks
- Access to test webhook receiver (e.g., https://webhook.site)
-
curlor similar HTTP client available - Ability to view Charon server logs
Test Environment Setup
Required Tools
-
Webhook Testing Service:
- Webhook.site: https://webhook.site (get unique URL)
- RequestBin: https://requestbin.com
- Discord webhook: https://discord.com/developers/docs/resources/webhook
-
HTTP Client:
# Verify curl is available curl --version -
Log Access:
# View Charon logs docker logs charon --tail=50 --follow
Test Case Format
Each test case includes:
- Objective: What security control is being tested
- Steps: Detailed instructions
- Expected Result: What should happen (✅)
- Actual Result: Record what actually happened
- Pass/Fail: Mark after completion
- Notes: Any observations or issues
Test Suite 1: Valid External Webhooks
TC-001: Valid HTTPS Webhook
Objective: Verify legitimate HTTPS webhooks work correctly
Steps:
- Navigate to Security Settings → Notifications
- Configure webhook:
https://webhook.site/<your-unique-id> - Click Save
- Trigger security event (e.g., create test ACL rule)
- Check webhook.site for received event
Expected Result: ✅ Webhook successfully delivered, no errors in logs
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
TC-002: Valid HTTP Webhook (Non-Production)
Objective: Verify HTTP webhooks work when explicitly allowed
Steps:
- Navigate to Security Settings → Notifications
- Configure webhook:
http://webhook.site/<your-unique-id> - Click Save
- Trigger security event
- Check webhook receiver
Expected Result: ✅ Webhook accepted (if HTTP allowed), or ❌ Rejected with "HTTP is not allowed, use HTTPS"
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
TC-003: Slack Webhook Format
Objective: Verify production webhook services work
Steps:
- Create Slack incoming webhook at https://api.slack.com/messaging/webhooks
- Configure webhook in Charon:
https://hooks.slack.com/services/T00/B00/XXX - Save configuration
- Trigger security event
- Check Slack channel for notification
Expected Result: ✅ Notification appears in Slack
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
TC-004: Discord Webhook Format
Objective: Verify Discord integration works
Steps:
- Create Discord webhook in server settings
- Configure webhook in Charon:
https://discord.com/api/webhooks/123456/abcdef - Save configuration
- Trigger security event
- Check Discord channel
Expected Result: ✅ Notification appears in Discord
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
Test Suite 2: Private IP Rejection
TC-005: Class A Private Network (10.0.0.0/8)
Objective: Verify RFC 1918 Class A blocking
Steps:
- Attempt to configure webhook:
http://10.0.0.1/webhook - Click Save
- Observe error message
Expected Result: ❌ Error: "URL resolves to a private IP address (blocked for security)"
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
TC-006: Class B Private Network (172.16.0.0/12)
Objective: Verify RFC 1918 Class B blocking
Steps:
- Attempt to configure webhook:
http://172.16.0.1/admin - Click Save
- Observe error message
Expected Result: ❌ Error: "URL resolves to a private IP address (blocked for security)"
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
TC-007: Class C Private Network (192.168.0.0/16)
Objective: Verify RFC 1918 Class C blocking
Steps:
- Attempt to configure webhook:
http://192.168.1.1/ - Click Save
- Observe error message
Expected Result: ❌ Error: "URL resolves to a private IP address (blocked for security)"
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
TC-008: Private IP with Port
Objective: Verify port numbers don't bypass protection
Steps:
- Attempt to configure webhook:
http://192.168.1.100:8080/webhook - Click Save
- Observe error message
Expected Result: ❌ Error: "URL resolves to a private IP address (blocked for security)"
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
Test Suite 3: Cloud Metadata Endpoints
TC-009: AWS Metadata Endpoint
Objective: Verify AWS metadata service is blocked
Steps:
- Attempt to configure webhook:
http://169.254.169.254/latest/meta-data/ - Click Save
- Observe error message
- Check logs for HIGH severity SSRF attempt
Expected Result:
- ❌ Configuration rejected
- ✅ Log entry:
severity=HIGH event=ssrf_blocked
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
TC-010: GCP Metadata Endpoint
Objective: Verify GCP metadata service is blocked
Steps:
- Attempt to configure webhook:
http://metadata.google.internal/computeMetadata/v1/ - Click Save
- Observe error message
Expected Result: ❌ Error: "URL resolves to a private IP address" or "DNS lookup failed"
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
TC-011: Azure Metadata Endpoint
Objective: Verify Azure metadata service is blocked
Steps:
- Attempt to configure webhook:
http://169.254.169.254/metadata/instance?api-version=2021-02-01 - Click Save
- Observe error message
Expected Result: ❌ Error: "URL resolves to a private IP address (blocked for security)"
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
Test Suite 4: Loopback Addresses
TC-012: IPv4 Loopback (127.0.0.1)
Objective: Verify localhost blocking (unless explicitly allowed)
Steps:
- Attempt to configure webhook:
http://127.0.0.1:8080/internal - Click Save
- Observe error message
Expected Result: ❌ Error: "localhost URLs are not allowed (blocked for security)"
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
TC-013: Localhost Hostname
Objective: Verify localhost keyword blocking
Steps:
- Attempt to configure webhook:
http://localhost/admin - Click Save
- Observe error message
Expected Result: ❌ Error: "localhost URLs are not allowed (blocked for security)"
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
TC-014: IPv6 Loopback (::1)
Objective: Verify IPv6 loopback blocking
Steps:
- Attempt to configure webhook:
http://[::1]/webhook - Click Save
- Observe error message
Expected Result: ❌ Error: "URL resolves to a private IP address (blocked for security)"
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
Test Suite 5: Protocol Validation
TC-015: File Protocol
Objective: Verify file:// protocol is blocked
Steps:
- Attempt to configure webhook:
file:///etc/passwd - Click Save
- Observe error message
Expected Result: ❌ Error: "URL must use HTTP or HTTPS"
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
TC-016: FTP Protocol
Objective: Verify ftp:// protocol is blocked
Steps:
- Attempt to configure webhook:
ftp://internal-server.local/upload/ - Click Save
- Observe error message
Expected Result: ❌ Error: "URL must use HTTP or HTTPS"
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
TC-017: Gopher Protocol
Objective: Verify gopher:// protocol is blocked
Steps:
- Attempt to configure webhook:
gopher://internal:70/ - Click Save
- Observe error message
Expected Result: ❌ Error: "URL must use HTTP or HTTPS"
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
TC-018: Data URL
Objective: Verify data: scheme is blocked
Steps:
- Attempt to configure webhook:
data:text/html,<script>alert(1)</script> - Click Save
- Observe error message
Expected Result: ❌ Error: "URL must use HTTP or HTTPS"
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
Test Suite 6: DNS Rebinding Protection
Background: DNS rebinding attacks exploit the gap between URL validation and actual connection by changing DNS records after validation passes. Charon's network.NewSafeHTTPClient() prevents this by re-validating IPs at connection time.
TC-019: DNS Rebinding Simulation (Conceptual)
Objective: Verify connection-time IP validation prevents DNS rebinding
Steps:
- Configure a webhook with a domain you control
- Initially point the domain to a public IP (passes validation)
- After webhook is saved, update DNS to point to
192.168.1.100 - Trigger a security event to send webhook notification
- Observe the webhook delivery failure
Expected Result:
- ❌ Webhook delivery fails with "connection to private IP blocked"
- ✅ Log entry shows re-validation caught the attack
- ✅ No request reaches 192.168.1.100
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
This test requires DNS control. Alternative: use tools like rebinder.it
TC-020: Connection-Time IP Validation
Objective: Verify IPs are validated at TCP connection time (not just URL parsing)
Steps:
- Use a webhook receiver that logs incoming connections
- Configure webhook URL pointing to the receiver
- Check that the connection comes from Charon
- Verify in Charon logs that IP validation occurred during dial
Expected Result: ✅ Logs show safeDialer validated IP before connection
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
Check logs for: "Validating IP for connection"
Test Suite 7: Redirect Blocking
TC-021: Redirect to Private IP
Objective: Verify redirects to private IPs are blocked
Steps:
- Set up a redirect server that returns:
HTTP 302 Location: http://192.168.1.100/ - Configure webhook pointing to the redirect server
- Trigger webhook delivery
- Observe redirect handling
Expected Result:
- ❌ "redirect to private IP blocked"
- ✅ Original request fails, no connection to 192.168.1.100
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
Alternative: use httpbin.org/redirect-to?url=http://192.168.1.100
TC-022: Redirect to Cloud Metadata
Objective: Verify redirects to cloud metadata endpoints are blocked
Steps:
- Set up redirect:
HTTP 302 Location: http://169.254.169.254/latest/meta-data/ - Configure webhook pointing to redirect
- Trigger webhook delivery
- Verify metadata endpoint not accessed
Expected Result: ❌ "redirect to private IP blocked" (169.254.x.x is blocked)
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
TC-023: Redirect Count Limit
Objective: Verify excessive redirects are blocked
Steps:
- Set up chain of 5+ redirects (each to a valid public URL)
- Configure webhook pointing to first redirect
- Trigger webhook delivery
- Observe redirect chain handling
Expected Result: ❌ "too many redirects (max 2)" after 2 hops
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
Default max redirects is 0 (no redirects). If enabled, max is typically 2.
TC-024: Redirect to Localhost
Objective: Verify redirects to localhost are blocked
Steps:
- Set up redirect:
HTTP 302 Location: http://127.0.0.1:8080/admin - Configure webhook pointing to redirect
- Trigger webhook delivery
- Verify localhost not accessed
Expected Result: ❌ "redirect to localhost blocked"
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
Test Suite 8: URL Testing Endpoint
TC-025: Test Valid Public URL
Objective: Verify URL test endpoint works for legitimate URLs
Steps:
- Navigate to System Settings → URL Testing (or use API)
- Test URL:
https://api.github.com - Submit test
- Observe result
Expected Result: ✅ "URL is reachable" with latency in milliseconds
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
TC-026: Test Private IP via URL Testing
Objective: Verify URL test endpoint also has SSRF protection
Steps:
- Navigate to URL Testing
- Test URL:
http://192.168.1.1 - Submit test
- Observe error
Expected Result: ❌ Error: "URL resolves to a private IP address (blocked for security)"
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
TC-027: Test Non-Existent Domain
Objective: Verify DNS resolution failure handling
Steps:
- Test URL:
https://this-domain-does-not-exist-12345.com - Submit test
- Observe error
Expected Result: ❌ Error: "DNS lookup failed" or "connection timeout"
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
Test Suite 9: CrowdSec Hub Sync
TC-028: Official CrowdSec Hub Domain
Objective: Verify CrowdSec hub sync works with official domain
Steps:
- Navigate to Security → CrowdSec
- Enable CrowdSec (if not already enabled)
- Trigger hub sync (or wait for automatic sync)
- Check logs for hub update success
Expected Result: ✅ Hub sync completes successfully
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
TC-029: Invalid CrowdSec Hub Domain
Objective: Verify custom hub URLs are validated
Steps:
- Attempt to configure custom hub URL:
http://malicious-hub.evil.com - Trigger hub sync
- Observe error in logs
Expected Result: ❌ Hub sync fails with validation error
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
(This test may require configuration file modification)
Test Suite 10: Update Service
TC-030: GitHub Update Check
Objective: Verify update service uses validated GitHub URLs
Steps:
- Navigate to System → Updates (if available in UI)
- Click Check for Updates
- Observe success or error
- Check logs for GitHub API request
Expected Result: ✅ Update check completes (no SSRF vulnerability)
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
Test Suite 11: Error Message Validation
TC-031: Generic Error Messages
Objective: Verify error messages don't leak internal information
Steps:
- Attempt various blocked URLs from previous tests
- Record exact error messages shown to user
- Verify no internal IPs, hostnames, or network topology revealed
Expected Result: ✅ Generic errors like "URL resolves to a private IP address (blocked for security)"
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
TC-032: Log Detail vs User Error
Objective: Verify logs contain more detail than user-facing errors
Steps:
- Attempt blocked URL:
http://192.168.1.100/admin - Check user-facing error message
- Check server logs for detailed information
Expected Result:
- User sees: "URL resolves to a private IP address (blocked for security)"
- Logs show:
severity=HIGH url=http://192.168.1.100/admin resolved_ip=192.168.1.100
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
Test Suite 12: Integration Testing
TC-033: End-to-End Webhook Flow
Objective: Verify complete webhook notification flow with SSRF protection
Steps:
- Configure valid webhook:
https://webhook.site/<unique-id> - Trigger CrowdSec block event (simulate attack)
- Verify notification received at webhook.site
- Check logs for successful webhook delivery
Expected Result:
- ✅ Webhook configured without errors
- ✅ Security event triggered
- ✅ Notification delivered successfully
- ✅ Logs show
Webhook notification sent successfully
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
TC-034: Configuration Persistence
Objective: Verify webhook validation persists across restarts
Steps:
- Configure valid webhook:
https://webhook.site/<unique-id> - Restart Charon container:
docker restart charon - Trigger security event
- Verify notification still works
Expected Result: ✅ Webhook survives restart and continues to function
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
TC-035: Multiple Webhook Configurations
Objective: Verify SSRF protection applies to all webhook types
Steps:
- Configure security notification webhook (valid)
- Configure custom webhook notification (valid)
- Attempt to add webhook with private IP (blocked)
- Verify both valid webhooks work, blocked one rejected
Expected Result:
- ✅ Valid webhooks accepted
- ❌ Private IP webhook rejected
- ✅ Both valid webhooks receive notifications
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
TC-036: Admin-Only Access Control
Objective: Verify URL testing requires admin privileges
Steps:
- Log out of admin account
- Log in as non-admin user (if available)
- Attempt to access URL testing endpoint
- Observe access denied error
Expected Result: ❌ 403 Forbidden: "Admin access required"
Actual Result: _____________
Pass/Fail: [ ] Pass [ ] Fail
Notes:
Test Summary
Results Overview
| Test Suite | Total Tests | Passed | Failed | Skipped |
|---|---|---|---|---|
| Valid External Webhooks | 4 | ___ | ___ | ___ |
| Private IP Rejection | 4 | ___ | ___ | ___ |
| Cloud Metadata Endpoints | 3 | ___ | ___ | ___ |
| Loopback Addresses | 3 | ___ | ___ | ___ |
| Protocol Validation | 4 | ___ | ___ | ___ |
| DNS Rebinding Protection | 2 | ___ | ___ | ___ |
| Redirect Blocking | 4 | ___ | ___ | ___ |
| URL Testing Endpoint | 3 | ___ | ___ | ___ |
| CrowdSec Hub Sync | 2 | ___ | ___ | ___ |
| Update Service | 1 | ___ | ___ | ___ |
| Error Message Validation | 2 | ___ | ___ | ___ |
| Integration Testing | 4 | ___ | ___ | ___ |
| TOTAL | 36 | ___ | ___ | ___ |
Pass Criteria
Minimum Requirements:
- All 36 test cases passed OR
- All critical tests passed (TC-005 through TC-018, TC-021 through TC-024, TC-026) AND
- All failures have documented justification
Critical Tests (Must Pass):
- TC-005: Class A Private Network blocking
- TC-006: Class B Private Network blocking
- TC-007: Class C Private Network blocking
- TC-009: AWS Metadata blocking
- TC-012: IPv4 Loopback blocking
- TC-015: File protocol blocking
- TC-021: Redirect to Private IP blocking
- TC-022: Redirect to Cloud Metadata blocking
- TC-026: URL testing SSRF protection
Issues Found
Issue Template
Issue ID: _____________
Test Case: TC-___
Severity: [ ] Critical [ ] High [ ] Medium [ ] Low
Description:
Steps to Reproduce:
Expected vs Actual:
Workaround (if applicable):
Sign-Off
Tester Certification
I certify that:
- All test cases were executed as described
- Results are accurate and complete
- All issues are documented
- Test environment matches production configuration
- SSRF protection is functioning as designed
Tester Name: _____________
Signature: _____________
Date: _____________
QA Manager Approval
- Test plan executed completely
- All critical tests passed
- Issues documented and prioritized
- SSRF remediation approved for production
QA Manager Name: _____________
Signature: _____________
Date: _____________
Document Version: 1.1
Last Updated: December 24, 2025
Status: Ready for Execution
Auto-created from ssrf_manual_test_plan.md
Metadata
Metadata
Assignees
Labels
Projects
Status