| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
|
| 6 |
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 7 |
source "$SCRIPT_DIR/../config.sh" |
| 8 |
|
| 9 |
OUT_FILE="$OUT/24_data_privacy.txt" |
| 10 |
echo "=== 24. Data Privacy & PII Exposure ===" | tee "$OUT_FILE" |
| 11 |
echo "Target: $TARGET API: $API_TARGET" | tee -a "$OUT_FILE" |
| 12 |
echo "" | tee -a "$OUT_FILE" |
| 13 |
|
| 14 |
W() { printf '%s' "$1" | python3 -c "import sys,urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip()))"; } |
| 15 |
|
| 16 |
do_test() { |
| 17 |
local label="$1"; shift |
| 18 |
local code |
| 19 |
code=$(curl -sk -o /dev/null -w '%{http_code}' "$@") |
| 20 |
printf '[%s] %s\n' "$code" "$label" | tee -a "$OUT_FILE" |
| 21 |
} |
| 22 |
|
| 23 |
do_body_check() { |
| 24 |
local label="$1"; local pattern="$2"; shift 2 |
| 25 |
local body |
| 26 |
body=$(curl -sk "$@") |
| 27 |
local code=$(curl -sk -o /dev/null -w '%{http_code}' "$@") |
| 28 |
printf '[%s] %s\n' "$code" "$label" | tee -a "$OUT_FILE" |
| 29 |
if echo "$body" | grep -qiE "$pattern"; then |
| 30 |
echo " [WARN] Sensitive pattern found: $(echo "$body" | grep -ioE "$pattern" | head -3 | tr '\n' ' ')" | tee -a "$OUT_FILE" |
| 31 |
fi |
| 32 |
} |
| 33 |
|
| 34 |
do_header_check() { |
| 35 |
local label="$1"; shift |
| 36 |
local headers |
| 37 |
headers=$(curl -sk -D - -o /dev/null "$@") |
| 38 |
local code=$(echo "$headers" | head -1 | grep -oP '\d{3}' | head -1) |
| 39 |
printf '[%s] %s\n' "$code" "$label" | tee -a "$OUT_FILE" |
| 40 |
| 41 |
local cc=$(echo "$headers" | grep -i '^cache-control:' | head -1) |
| 42 |
if [ -n "$cc" ]; then |
| 43 |
echo " Cache-Control: $(echo "$cc" | cut -d: -f2- | tr -d '\r')" | tee -a "$OUT_FILE" |
| 44 |
else |
| 45 |
echo " [MISS] No Cache-Control header" | tee -a "$OUT_FILE" |
| 46 |
fi |
| 47 |
} |
| 48 |
|
| 49 |
| 50 |
echo "--- PII in URL Parameters ---" | tee -a "$OUT_FILE" |
| 51 |
do_body_check "Email in URL ?email=test@example.com" \ |
| 52 |
'test@example\.com' \ |
| 53 |
-A "$BROWSER_UA" "$TARGET/?email=test@example.com" |
| 54 |
do_body_check "Password in URL ?password=Secret123" \ |
| 55 |
'secret123' \ |
| 56 |
-A "$BROWSER_UA" "$TARGET/?password=Secret123" |
| 57 |
do_body_check "API key in URL ?api_key=abc123" \ |
| 58 |
'abc123' \ |
| 59 |
-A "$BROWSER_UA" "$API_TARGET/status?api_key=abc123" |
| 60 |
do_body_check "Token in URL ?token=eyJhbGci" \ |
| 61 |
'eyJhbGci' \ |
| 62 |
-A "$BROWSER_UA" "$TARGET/?token=eyJhbGciOiJIUzI1NiJ9.test.sig" |
| 63 |
do_body_check "SSN-like value in URL ?ssn=123-45-6789" \ |
| 64 |
'[0-9]{3}-[0-9]{2}-[0-9]{4}' \ |
| 65 |
-A "$BROWSER_UA" "$TARGET/?ssn=123-45-6789" |
| 66 |
echo "" | tee -a "$OUT_FILE" |
| 67 |
|
| 68 |
| 69 |
echo "--- Stack Trace / Verbose Error Leakage ---" | tee -a "$OUT_FILE" |
| 70 |
STACK_PATTERNS='(stack trace|exception|traceback|at [A-Za-z]+\.[A-Za-z]+\(|line [0-9]+|\.php:[0-9]+|\.rs:[0-9]+|/var/www|/home/|/root/|fatal error|undefined (variable|index)|parse error)' |
| 71 |
|
| 72 |
do_body_check "404 error verbosity" "$STACK_PATTERNS" \ |
| 73 |
-A "$BROWSER_UA" "$TARGET/this-page-definitely-does-not-exist-12345" |
| 74 |
do_body_check "API invalid JSON error verbosity" "$STACK_PATTERNS" \ |
| 75 |
-A "$BROWSER_UA" -X POST -H 'Content-Type: application/json' \ |
| 76 |
-d '{invalid json}' "$API_TARGET/encrypt" |
| 77 |
do_body_check "API missing required field error" "$STACK_PATTERNS" \ |
| 78 |
-A "$BROWSER_UA" -X POST -H 'Content-Type: application/json' \ |
| 79 |
-d '{}' "$API_TARGET/encrypt" |
| 80 |
do_body_check "API wrong type error verbosity" "$STACK_PATTERNS" \ |
| 81 |
-A "$BROWSER_UA" -X POST -H 'Content-Type: application/json' \ |
| 82 |
-d '{"data":12345,"algorithm":true}' "$API_TARGET/encrypt" |
| 83 |
do_body_check "PHP error probe via bad extension" "$STACK_PATTERNS" \ |
| 84 |
-A "$BROWSER_UA" "$TARGET/index.php?XDEBUG_SESSION=1" |
| 85 |
do_body_check "Server path disclosure in 500" "$STACK_PATTERNS" \ |
| 86 |
-A "$BROWSER_UA" -X POST -H 'Content-Type: application/json' \ |
| 87 |
-d '{"data":"'"$(python3 -c "print('A'*100000)")"'"}' "$API_TARGET/encrypt" |
| 88 |
echo "" | tee -a "$OUT_FILE" |
| 89 |
|
| 90 |
| 91 |
echo "--- Sensitive Data in Response Headers ---" | tee -a "$OUT_FILE" |
| 92 |
check_sensitive_headers() { |
| 93 |
local label="$1"; shift |
| 94 |
local headers |
| 95 |
headers=$(curl -sk -D - -o /dev/null "$@") |
| 96 |
local code=$(echo "$headers" | head -1 | grep -oP '\d{3}' | head -1) |
| 97 |
printf '[%s] %s\n' "$code" "$label" | tee -a "$OUT_FILE" |
| 98 |
| 99 |
for BAD in Server X-Powered-By X-AspNet-Version X-AspNetMvc-Version X-Generator X-Drupal X-WordPress X-Runtime X-Request-Id; do |
| 100 |
VAL=$(echo "$headers" | grep -i "^$BAD:" | head -1) |
| 101 |
if [ -n "$VAL" ]; then |
| 102 |
echo " [WARN] $VAL" | tee -a "$OUT_FILE" |
| 103 |
fi |
| 104 |
done |
| 105 |
| 106 |
if echo "$headers" | grep -qiE '(127\.[0-9]+\.[0-9]+\.[0-9]+|10\.[0-9]+\.[0-9]+\.[0-9]+|192\.168\.[0-9]+\.[0-9]+|172\.(1[6-9]|2[0-9]|3[01])\.[0-9]+\.[0-9]+)'; then |
| 107 |
echo " [WARN] Internal IP found in headers" | tee -a "$OUT_FILE" |
| 108 |
fi |
| 109 |
} |
| 110 |
check_sensitive_headers "Main page response headers" -A "$BROWSER_UA" "$TARGET/" |
| 111 |
check_sensitive_headers "API response headers" -A "$BROWSER_UA" "$API_TARGET/status" |
| 112 |
check_sensitive_headers "404 response headers" -A "$BROWSER_UA" "$TARGET/nonexistent-12345" |
| 113 |
check_sensitive_headers "API 404 headers" -A "$BROWSER_UA" "$API_TARGET/nonexistent-endpoint" |
| 114 |
echo "" | tee -a "$OUT_FILE" |
| 115 |
|
| 116 |
| 117 |
echo "--- Cache-Control on Sensitive Endpoints ---" | tee -a "$OUT_FILE" |
| 118 |
SENSITIVE_PATHS=( |
| 119 |
"/crypt.php" |
| 120 |
"/admin/" |
| 121 |
"/admin/index.php" |
| 122 |
"/key-vault/" |
| 123 |
"/security-systems/" |
| 124 |
) |
| 125 |
for SPATH in "${SENSITIVE_PATHS[@]}"; do |
| 126 |
do_header_check "Cache headers: $SPATH" -A "$BROWSER_UA" "$TARGET$SPATH" |
| 127 |
done |
| 128 |
do_header_check "Cache headers: API /encrypt" \ |
| 129 |
-A "$BROWSER_UA" -X POST -H 'Content-Type: application/json' \ |
| 130 |
-d '{"data":"test","algorithm":"classical"}' "$API_TARGET/encrypt" |
| 131 |
echo "" | tee -a "$OUT_FILE" |
| 132 |
|
| 133 |
| 134 |
echo "--- Sensitive Data File Exposure ---" | tee -a "$OUT_FILE" |
| 135 |
do_body_check "robots.txt PII/path disclosure" \ |
| 136 |
'(/var/www|/home/|/root/|password|secret|token|key)' \ |
| 137 |
-A "$BROWSER_UA" "$TARGET/robots.txt" |
| 138 |
do_body_check "sitemap.xml sensitive paths" \ |
| 139 |
'(admin|login|password|token|key|secret|user|account)' \ |
| 140 |
-A "$BROWSER_UA" "$TARGET/sitemap.xml" |
| 141 |
do_body_check "API spec / OpenAPI sensitive schema" \ |
| 142 |
'(password|secret|ssn|credit_card|cvv|pin)' \ |
| 143 |
-A "$BROWSER_UA" "$API_TARGET/openapi.json" |
| 144 |
|
| 145 |
| 146 |
PAGE_BODY=$(curl -sk -A "$BROWSER_UA" "$TARGET/") |
| 147 |
echo "" | tee -a "$OUT_FILE" |
| 148 |
echo "--- Version/Build Info in Page Source ---" | tee -a "$OUT_FILE" |
| 149 |
for PATTERN in 'version["\s:=]+[0-9]' 'build["\s:=]+[0-9a-f]' 'commit["\s:=]+[0-9a-f]{7}' 'php/[0-9]' 'apache/[0-9]' 'nginx/[0-9]'; do |
| 150 |
FOUND=$(echo "$PAGE_BODY" | grep -ioE "$PATTERN" | head -2) |
| 151 |
[ -n "$FOUND" ] && echo " [WARN] $PATTERN โ $FOUND" | tee -a "$OUT_FILE" |
| 152 |
done |
| 153 |
echo " (version pattern scan complete)" | tee -a "$OUT_FILE" |
| 154 |
echo "" | tee -a "$OUT_FILE" |
| 155 |
|
| 156 |
| 157 |
echo "--- Sensitive Field Reflection (log/response test) ---" | tee -a "$OUT_FILE" |
| 158 |
FAKE_CC='4111-1111-1111-1111' |
| 159 |
FAKE_SSN='123-45-6789' |
| 160 |
FAKE_EMAIL='piitest_probe@example.com' |
| 161 |
|
| 162 |
do_body_check "Credit card reflected in API error" \ |
| 163 |
"$FAKE_CC" \ |
| 164 |
-A "$BROWSER_UA" -X POST -H 'Content-Type: application/json' \ |
| 165 |
-d "{\"data\":\"$FAKE_CC\",\"algorithm\":\"invalid_algo_xyz\"}" "$API_TARGET/encrypt" |
| 166 |
do_body_check "SSN reflected in API error" \ |
| 167 |
"$FAKE_SSN" \ |
| 168 |
-A "$BROWSER_UA" -X POST -H 'Content-Type: application/json' \ |
| 169 |
-d "{\"data\":\"$FAKE_SSN\",\"algorithm\":\"invalid_algo_xyz\"}" "$API_TARGET/encrypt" |
| 170 |
do_body_check "Email reflected in API error" \ |
| 171 |
"$FAKE_EMAIL" \ |
| 172 |
-A "$BROWSER_UA" -X POST -H 'Content-Type: application/json' \ |
| 173 |
-d "{\"data\":\"$FAKE_EMAIL\",\"algorithm\":\"invalid_algo_xyz\"}" "$API_TARGET/encrypt" |
| 174 |
echo "" | tee -a "$OUT_FILE" |
| 175 |
|
| 176 |
| 177 |
echo "--- GDPR & Privacy Compliance Indicators ---" | tee -a "$OUT_FILE" |
| 178 |
do_test "Privacy policy page exists" -A "$BROWSER_UA" "$TARGET/privacy" |
| 179 |
do_test "Privacy policy /privacy-policy" -A "$BROWSER_UA" "$TARGET/privacy-policy" |
| 180 |
do_test "Cookie consent endpoint" -A "$BROWSER_UA" "$TARGET/cookie-consent" |
| 181 |
do_test "GDPR data request endpoint" -A "$BROWSER_UA" "$TARGET/gdpr" |
| 182 |
do_test "Data deletion endpoint" -A "$BROWSER_UA" "$TARGET/delete-account" |
| 183 |
|
| 184 |
| 185 |
PAGE_HTML=$(curl -sk -A "$BROWSER_UA" "$TARGET/") |
| 186 |
if echo "$PAGE_HTML" | grep -qiE '(google-analytics|googletagmanager|gtag|fbq|hotjar|mixpanel|segment\.com|amplitude)'; then |
| 187 |
echo " [WARN] Third-party analytics/tracking detected in page source" | tee -a "$OUT_FILE" |
| 188 |
echo "$PAGE_HTML" | grep -ioE '(google-analytics\.com|googletagmanager\.com|connect\.facebook\.net|static\.hotjar\.com|cdn\.segment\.com|cdn\.amplitude\.com|cdn\.mixpanel\.com)[^"'"'"' >]*' | head -5 | sed 's/^/ /' | tee -a "$OUT_FILE" |
| 189 |
else |
| 190 |
echo " [OK] No third-party analytics trackers detected" | tee -a "$OUT_FILE" |
| 191 |
fi |
| 192 |
echo "" | tee -a "$OUT_FILE" |
| 193 |
|
| 194 |
| 195 |
echo "--- API Response Data Minimization ---" | tee -a "$OUT_FILE" |
| 196 |
API_RESP=$(curl -sk -A "$BROWSER_UA" "$API_TARGET/status") |
| 197 |
echo " Status response fields: $(echo "$API_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(list(d.keys()))" 2>/dev/null || echo "(non-JSON or error)")" | tee -a "$OUT_FILE" |
| 198 |
| 199 |
for FIELD in password secret token key_material private_key db_host db_pass internal_ip server_path config; do |
| 200 |
if echo "$API_RESP" | grep -qi "\"$FIELD\""; then |
| 201 |
echo " [WARN] Sensitive field '$FIELD' in status response" | tee -a "$OUT_FILE" |
| 202 |
fi |
| 203 |
done |
| 204 |
echo "" | tee -a "$OUT_FILE" |
| 205 |
|
| 206 |
echo "=== 24. Data Privacy & PII Exposure COMPLETE ===" | tee -a "$OUT_FILE" |
| 207 |
|