Files

520 lines
22 KiB
Bash
Raw Permalink Normal View History

#!/bin/bash
#
# Security Test Suite for Contact Form
# Tests all security features against a live server
#
# Usage:
# ./security_tests.sh [SERVER_URL]
#
# Example:
# ./security_tests.sh http://localhost:8080
# ./security_tests.sh https://cv.example.com
#
set -e # Exit on error
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Server URL (default to localhost)
SERVER_URL="${1:-http://localhost:8080}"
API_URL="${SERVER_URL}/api/contact"
# Test counters
TOTAL_TESTS=0
PASSED_TESTS=0
FAILED_TESTS=0
# Helper functions
print_header() {
echo ""
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
}
print_test() {
echo -e "${YELLOW}$1${NC}"
}
print_pass() {
echo -e "${GREEN}$1${NC}"
((PASSED_TESTS++))
}
print_fail() {
echo -e "${RED}$1${NC}"
((FAILED_TESTS++))
}
test_result() {
local test_name="$1"
local expected_code="$2"
local actual_code="$3"
local response="$4"
((TOTAL_TESTS++))
if [ "$actual_code" -eq "$expected_code" ]; then
print_pass "$test_name (expected $expected_code, got $actual_code)"
return 0
else
print_fail "$test_name (expected $expected_code, got $actual_code)"
echo " Response: ${response:0:200}"
return 1
fi
}
# Calculate submit time (5 seconds ago)
get_submit_time() {
echo $(( $(date +%s) * 1000 - 5000 ))
}
# Get submit time that's too fast (500ms ago)
get_fast_submit_time() {
echo $(( $(date +%s) * 1000 - 500 ))
}
# Check if server is running
check_server() {
print_header "Checking Server Availability"
if curl -s -o /dev/null -w "%{http_code}" "$SERVER_URL" > /dev/null 2>&1; then
print_pass "Server is running at $SERVER_URL"
return 0
else
print_fail "Server is not accessible at $SERVER_URL"
echo "Please start the server first: go run main.go"
exit 1
fi
}
# Test 1: Browser-Only Middleware - Block curl
test_block_curl() {
print_header "Test 1: Browser-Only Middleware - Block HTTP Clients"
# Test curl (default user agent)
print_test "Testing curl blocking"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "email=test@example.com&name=Test&subject=Test&message=Test message&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Block curl" 403 "$http_code" "$body"
# Test wget user agent
print_test "Testing wget blocking"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Wget/1.20.3" \
-d "email=test@example.com&name=Test&subject=Test&message=Test message&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Block wget" 403 "$http_code" "$body"
# Test postman
print_test "Testing Postman blocking"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: PostmanRuntime/7.26.8" \
-d "email=test@example.com&name=Test&subject=Test&message=Test message&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Block Postman" 403 "$http_code" "$body"
# Test python requests
print_test "Testing Python requests blocking"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: python-requests/2.25.1" \
-d "email=test@example.com&name=Test&subject=Test&message=Test message&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Block Python requests" 403 "$http_code" "$body"
# Test empty user agent
print_test "Testing empty User-Agent blocking"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent:" \
-d "email=test@example.com&name=Test&subject=Test&message=Test message&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Block empty User-Agent" 403 "$http_code" "$body"
}
# Test 2: Browser-Only Middleware - Require Referer/Origin
test_require_referer_origin() {
print_header "Test 2: Browser-Only Middleware - Referer/Origin Headers"
# Test without Referer or Origin
print_test "Testing request without Referer/Origin (should block)"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" \
-H "HX-Request: true" \
-d "email=test@example.com&name=Test&subject=Test&message=Test message&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Block without Referer/Origin" 403 "$http_code" "$body"
# Test with Referer
print_test "Testing request with Referer (should allow)"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" \
-H "Referer: ${SERVER_URL}/" \
-H "HX-Request: true" \
-d "email=test@example.com&name=Test&subject=Test&message=Test message here&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Allow with Referer" 200 "$http_code" "$body"
# Test with Origin
print_test "Testing request with Origin (should allow)"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" \
-H "Origin: ${SERVER_URL}" \
-H "HX-Request: true" \
-d "email=test2@example.com&name=Test2&subject=Test&message=Another test message&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Allow with Origin" 200 "$http_code" "$body"
}
# Test 3: Browser-Only Middleware - Require Browser Headers
test_require_browser_headers() {
print_header "Test 3: Browser-Only Middleware - Browser Headers"
# Test without browser headers
print_test "Testing request without browser headers (should block)"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" \
-H "Referer: ${SERVER_URL}/" \
-d "email=test@example.com&name=Test&subject=Test&message=Test message&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Block without browser headers" 403 "$http_code" "$body"
# Test with HX-Request header
print_test "Testing request with HX-Request header (should allow)"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" \
-H "Referer: ${SERVER_URL}/" \
-H "HX-Request: true" \
-d "email=test3@example.com&name=Test3&subject=Test&message=Message with HX header&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Allow with HX-Request" 200 "$http_code" "$body"
# Test with X-Requested-With header
print_test "Testing request with X-Requested-With header (should allow)"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" \
-H "Referer: ${SERVER_URL}/" \
-H "X-Requested-With: XMLHttpRequest" \
-d "email=test4@example.com&name=Test4&subject=Test&message=Message with XMLHttpRequest header&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Allow with X-Requested-With" 200 "$http_code" "$body"
# Test with X-Browser-Request header
print_test "Testing request with X-Browser-Request header (should allow)"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" \
-H "Referer: ${SERVER_URL}/" \
-H "X-Browser-Request: true" \
-d "email=test5@example.com&name=Test5&subject=Test&message=Message with browser request header&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Allow with X-Browser-Request" 200 "$http_code" "$body"
}
# Test 4: Input Validation - Email Format
test_email_validation() {
print_header "Test 4: Input Validation - Email Format"
# Valid email
print_test "Testing valid email"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Mozilla/5.0" \
-H "Referer: ${SERVER_URL}/" \
-H "HX-Request: true" \
-d "email=valid@example.com&name=Test&subject=Test&message=Valid email test message&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Valid email accepted" 200 "$http_code" "$body"
# Invalid email - no @
print_test "Testing invalid email (no @)"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Mozilla/5.0" \
-H "Referer: ${SERVER_URL}/" \
-H "HX-Request: true" \
-d "email=notanemail&name=Test&subject=Test&message=Invalid email test&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Invalid email rejected (no @)" 400 "$http_code" "$body"
# Invalid email - no domain
print_test "Testing invalid email (no domain)"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Mozilla/5.0" \
-H "Referer: ${SERVER_URL}/" \
-H "HX-Request: true" \
-d "email=test@&name=Test&subject=Test&message=Invalid email test&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Invalid email rejected (no domain)" 400 "$http_code" "$body"
# Empty email
print_test "Testing empty email"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Mozilla/5.0" \
-H "Referer: ${SERVER_URL}/" \
-H "HX-Request: true" \
-d "email=&name=Test&subject=Test&message=Empty email test&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Empty email rejected" 400 "$http_code" "$body"
}
# Test 5: Input Validation - Message Length
test_message_validation() {
print_header "Test 5: Input Validation - Message Length"
# Valid message
print_test "Testing valid message length"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Mozilla/5.0" \
-H "Referer: ${SERVER_URL}/" \
-H "HX-Request: true" \
-d "email=test@example.com&name=Test&subject=Test&message=This is a valid message with sufficient length.&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Valid message accepted" 200 "$http_code" "$body"
# Empty message
print_test "Testing empty message"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Mozilla/5.0" \
-H "Referer: ${SERVER_URL}/" \
-H "HX-Request: true" \
-d "email=test@example.com&name=Test&subject=Test&message=&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Empty message rejected" 400 "$http_code" "$body"
# Too long message (> 5000 chars)
print_test "Testing message too long (>5000 chars)"
long_message=$(printf 'a%.0s' {1..5001})
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Mozilla/5.0" \
-H "Referer: ${SERVER_URL}/" \
-H "HX-Request: true" \
-d "email=test@example.com&name=Test&subject=Test&message=${long_message}&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Too long message rejected" 400 "$http_code" "$body"
}
# Test 6: Bot Protection - Honeypot
test_honeypot() {
print_header "Test 6: Bot Protection - Honeypot Field"
# Honeypot empty (human)
print_test "Testing honeypot empty (human behavior)"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Mozilla/5.0" \
-H "Referer: ${SERVER_URL}/" \
-H "HX-Request: true" \
-d "email=human@example.com&name=Human&subject=Test&message=I am a real human user.&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Honeypot empty - accepted" 200 "$http_code" "$body"
# Honeypot filled (bot)
print_test "Testing honeypot filled (bot behavior)"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Mozilla/5.0" \
-H "Referer: ${SERVER_URL}/" \
-H "HX-Request: true" \
-d "email=bot@example.com&name=Bot&subject=Spam&message=This is spam!&website=http://spam-site.com&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
# Honeypot returns 200 to fool bots, but doesn't actually send email
test_result "Honeypot filled - fake success (bot fooled)" 200 "$http_code" "$body"
}
# Test 7: Bot Protection - Timing
test_timing_validation() {
print_header "Test 7: Bot Protection - Form Submission Timing"
# Valid timing (5 seconds)
print_test "Testing normal submission timing (5 seconds)"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Mozilla/5.0" \
-H "Referer: ${SERVER_URL}/" \
-H "HX-Request: true" \
-d "email=slow@example.com&name=Slow&subject=Test&message=I took my time filling this out.&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Normal timing accepted" 200 "$http_code" "$body"
# Too fast (< 2 seconds)
print_test "Testing fast submission (< 2 seconds)"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Mozilla/5.0" \
-H "Referer: ${SERVER_URL}/" \
-H "HX-Request: true" \
-d "email=fast@example.com&name=Fast&subject=Test&message=I submitted instantly!&website=&submit_time=$(get_fast_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Too fast submission rejected" 400 "$http_code" "$body"
}
# Test 8: Rate Limiting
test_rate_limiting() {
print_header "Test 8: Rate Limiting (5 requests per hour)"
echo "Sending 6 requests rapidly..."
for i in {1..6}; do
print_test "Request #$i"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Mozilla/5.0" \
-H "Referer: ${SERVER_URL}/" \
-H "HX-Request: true" \
-d "email=rate${i}@example.com&name=RateTest${i}&subject=Test&message=Rate limit test message number ${i}.&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
if [ "$i" -le 5 ]; then
# First 5 should succeed
test_result "Request $i allowed" 200 "$http_code" "$body"
else
# 6th should be rate limited
test_result "Request $i rate limited" 429 "$http_code" "$body"
fi
# Small delay between requests
sleep 0.1
done
echo ""
echo -e "${YELLOW}Note: Rate limit resets after 1 hour${NC}"
}
# Test 9: Real-World Attack Scenarios
test_attack_scenarios() {
print_header "Test 9: Real-World Attack Scenarios"
# Scenario 1: Sophisticated bot with browser-like headers but honeypot filled
print_test "Scenario 1: Sophisticated bot (browser headers + honeypot)"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
-H "Referer: ${SERVER_URL}/" \
-H "HX-Request: true" \
-d "email=smartbot@example.com&name=SmartBot&subject=Offer&message=Check out our amazing product!&website=http://smart-bot.com&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Smart bot caught by honeypot" 200 "$http_code" "$body"
# Scenario 2: Script kiddie with curl
print_test "Scenario 2: Script kiddie using curl"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "email=hacker@example.com&name=Hacker&subject=Pwned&message=You got hacked!&website=&submit_time=$(get_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Script kiddie blocked by BrowserOnly" 403 "$http_code" "$body"
# Scenario 3: Automated form filler (fast submission)
print_test "Scenario 3: Automated form filler (too fast)"
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "User-Agent: Mozilla/5.0" \
-H "Referer: ${SERVER_URL}/" \
-H "HX-Request: true" \
-d "email=autobot@example.com&name=AutoBot&subject=Auto&message=Automatically filled form!&website=&submit_time=$(get_fast_submit_time)")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
test_result "Auto-filler caught by timing check" 400 "$http_code" "$body"
}
# Summary
print_summary() {
print_header "Test Summary"
echo ""
echo -e "Total Tests: ${BLUE}${TOTAL_TESTS}${NC}"
echo -e "Passed: ${GREEN}${PASSED_TESTS}${NC}"
echo -e "Failed: ${RED}${FAILED_TESTS}${NC}"
echo ""
if [ "$FAILED_TESTS" -eq 0 ]; then
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${GREEN}✓ ALL SECURITY TESTS PASSED!${NC}"
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
exit 0
else
echo -e "${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${RED}✗ SOME TESTS FAILED${NC}"
echo -e "${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
exit 1
fi
}
# Main execution
main() {
clear
echo -e "${BLUE}"
echo "╔══════════════════════════════════════════════════════════════════════╗"
echo "║ ║"
echo "║ CONTACT FORM SECURITY TEST SUITE ║"
echo "║ ║"
echo "╚══════════════════════════════════════════════════════════════════════╝"
echo -e "${NC}"
echo ""
echo "Server: $SERVER_URL"
echo "Endpoint: $API_URL"
echo ""
check_server
test_block_curl
test_require_referer_origin
test_require_browser_headers
test_email_validation
test_message_validation
test_honeypot
test_timing_validation
test_rate_limiting
test_attack_scenarios
print_summary
}
# Run main
main