Skip to content

Commit 3d35e6b

Browse files
committed
feat: implement aditional SQLI tests
1 parent 3307d28 commit 3d35e6b

File tree

2 files changed

+876
-57
lines changed

2 files changed

+876
-57
lines changed
Lines changed: 150 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,196 @@
1-
#!/bin/bash
2-
# SQL Injection Protection Test
1+
#!/usr/bin/env bash
2+
# SQL Injection Protection Test (Quick)
3+
#
4+
# Phase 1 (unauthenticated): confirms endpoints reject unauthenticated requests.
5+
# Phase 2 (authenticated): confirms authenticated requests with SQLi payloads
6+
# do NOT trigger SQL errors or return leaked data.
7+
#
8+
# For comprehensive SQLi coverage (UNION, error-based, boolean, blind, time-based)
9+
# run: test-sqli-advanced.sh
310

4-
API_URL="http://localhost:3333"
11+
API_URL="${API_URL:-http://localhost:3333}"
12+
API="${API_URL}/api/v1"
13+
TEST_EMAIL="${TEST_EMAIL:-test@prostaff.gg}"
14+
TEST_PASSWORD="${TEST_PASSWORD:-Test123!@#}"
515
GREEN='\033[0;32m'
616
RED='\033[0;31m'
17+
YELLOW='\033[1;33m'
718
NC='\033[0m'
819

9-
echo "SQL Injection Protection Test"
10-
echo "=============================="
20+
echo "SQL Injection Protection Test (Quick)"
21+
echo "======================================"
1122
echo ""
1223

1324
PASSED=0
1425
FAILED=0
26+
WARNINGS=0
1527

16-
test_result() {
17-
if [ "$1" = "PASS" ]; then
18-
echo -e "${GREEN}[PASS]${NC} $2"
19-
PASSED=$((PASSED + 1))
20-
else
21-
echo -e "${RED}[FAIL]${NC} $2"
22-
FAILED=$((FAILED + 1))
23-
fi
28+
pass() { echo -e "${GREEN}[PASS]${NC} $1"; PASSED=$(( PASSED + 1 )); }
29+
fail() { echo -e "${RED}[FAIL]${NC} $1"; FAILED=$(( FAILED + 1 )); }
30+
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; WARNINGS=$(( WARNINGS + 1 )); }
31+
32+
has_sql_error() {
33+
echo "$1" | grep -qiE '(PG::|pg::|syntax error at or near|unterminated quoted|SQLSTATE|ORA-[0-9]{5})'
2434
}
2535

26-
# All these should return 401 (need auth) or 400/422 (validation error)
27-
# NOT 500 (SQL error) or 200 with leaked data
36+
# ---------------------------------------------------------------------------
37+
# Phase 1: Unauthenticated (must return 401, not 500 or SQL error)
38+
# ---------------------------------------------------------------------------
39+
echo "--- Phase 1: Unauthenticated requests ---"
40+
echo ""
2841

29-
echo "[1/4] Testing SQL injection in search parameter..."
30-
RESULT=$(curl -s -w "\n%{http_code}" "$API_URL/api/v1/players?search=admin'%20OR%20'1'='1")
42+
echo "[1/4] Search param (no auth)..."
43+
RESULT=$(curl -s -w "\n%{http_code}" "${API}/players?search=admin'%20OR%20'1'='1")
3144
HTTP_CODE=$(echo "$RESULT" | tail -n1)
3245
BODY=$(echo "$RESULT" | head -n-1)
33-
3446
if [ "$HTTP_CODE" = "401" ]; then
35-
test_result "PASS" "Search parameter protected (requires auth)"
36-
elif [ "$HTTP_CODE" = "500" ] || echo "$BODY" | grep -qi "syntax error\|pg::"; then
37-
test_result "FAIL" "SQL injection may be possible (HTTP $HTTP_CODE)"
47+
pass "Unauthenticated search blocked (401)"
48+
elif has_sql_error "$BODY" || [ "$HTTP_CODE" = "500" ]; then
49+
fail "SQL error or 500 without auth (HTTP $HTTP_CODE)"
3850
else
39-
test_result "PASS" "Search parameter handled safely (HTTP $HTTP_CODE)"
51+
warn "Unexpected HTTP $HTTP_CODE without auth — verify response"
4052
fi
4153

42-
echo "[2/4] Testing SQL injection in login..."
43-
RESULT=$(curl -s -w "\n%{http_code}" -X POST "$API_URL/api/v1/auth/login" \
54+
echo "[2/4] Login SQLi (no auth required)..."
55+
RESULT=$(curl -s -w "\n%{http_code}" -X POST "${API}/auth/login" \
4456
-H "Content-Type: application/json" \
4557
-d "{\"email\":\"admin' OR '1'='1\",\"password\":\"test\"}")
4658
HTTP_CODE=$(echo "$RESULT" | tail -n1)
4759
BODY=$(echo "$RESULT" | head -n-1)
48-
4960
if [ "$HTTP_CODE" = "401" ] || [ "$HTTP_CODE" = "422" ]; then
50-
test_result "PASS" "Login protected against SQL injection"
51-
elif [ "$HTTP_CODE" = "500" ] || echo "$BODY" | grep -qi "syntax error\|pg::"; then
52-
test_result "FAIL" "SQL injection may be possible in login"
61+
pass "Login SQLi rejected (HTTP $HTTP_CODE)"
62+
elif has_sql_error "$BODY" || [ "$HTTP_CODE" = "500" ]; then
63+
fail "SQL error or 500 on login SQLi (HTTP $HTTP_CODE)"
5364
else
54-
test_result "PASS" "Login handled safely (HTTP $HTTP_CODE)"
65+
warn "Login SQLi returned HTTP $HTTP_CODE — verify response manually"
5566
fi
5667

57-
echo "[3/4] Testing UNION-based SQL injection..."
58-
RESULT=$(curl -s -w "\n%{http_code}" "$API_URL/api/v1/players?role=top'%20UNION%20SELECT%20*%20FROM%20users--")
68+
echo "[3/4] UNION injection (no auth)..."
69+
RESULT=$(curl -s -w "\n%{http_code}" "${API}/players?role=top'%20UNION%20SELECT%20*%20FROM%20users--")
5970
HTTP_CODE=$(echo "$RESULT" | tail -n1)
6071
BODY=$(echo "$RESULT" | head -n-1)
61-
6272
if [ "$HTTP_CODE" = "401" ]; then
63-
test_result "PASS" "UNION injection blocked (requires auth)"
64-
elif [ "$HTTP_CODE" = "500" ] || echo "$BODY" | grep -qi "syntax error\|pg::"; then
65-
test_result "FAIL" "UNION injection may be possible"
73+
pass "UNION injection blocked without auth (401)"
74+
elif has_sql_error "$BODY" || [ "$HTTP_CODE" = "500" ]; then
75+
fail "SQL error or 500 on unauthenticated UNION (HTTP $HTTP_CODE)"
6676
else
67-
test_result "PASS" "UNION injection handled safely (HTTP $HTTP_CODE)"
77+
warn "HTTP $HTTP_CODE on UNION without auth — verify"
6878
fi
6979

70-
echo "[4/4] Testing comment-based SQL injection..."
71-
RESULT=$(curl -s -w "\n%{http_code}" "$API_URL/api/v1/players?role=top';--")
80+
echo "[4/4] Comment injection (no auth)..."
81+
RESULT=$(curl -s -w "\n%{http_code}" "${API}/players?role=top';--")
7282
HTTP_CODE=$(echo "$RESULT" | tail -n1)
7383
BODY=$(echo "$RESULT" | head -n-1)
74-
7584
if [ "$HTTP_CODE" = "401" ]; then
76-
test_result "PASS" "Comment injection blocked (requires auth)"
77-
elif [ "$HTTP_CODE" = "500" ] || echo "$BODY" | grep -qi "syntax error\|pg::"; then
78-
test_result "FAIL" "Comment injection may be possible"
85+
pass "Comment injection blocked without auth (401)"
86+
elif has_sql_error "$BODY" || [ "$HTTP_CODE" = "500" ]; then
87+
fail "SQL error or 500 on unauthenticated comment injection (HTTP $HTTP_CODE)"
7988
else
80-
test_result "PASS" "Comment injection handled safely (HTTP $HTTP_CODE)"
89+
warn "HTTP $HTTP_CODE on comment injection without auth — verify"
8190
fi
8291

92+
# ---------------------------------------------------------------------------
93+
# Phase 2: Authenticated (the real test)
94+
# ---------------------------------------------------------------------------
8395
echo ""
84-
echo "=================================="
85-
echo "RESULTS"
86-
echo "=================================="
87-
echo "Tests run: $((PASSED + FAILED))"
88-
echo -e "${GREEN}Passed: $PASSED${NC}"
89-
echo -e "${RED}Failed: $FAILED${NC}"
96+
echo "--- Phase 2: Authenticated requests ---"
9097
echo ""
9198

92-
if [ $FAILED -eq 0 ]; then
93-
echo -e "${GREEN}✓ SQL Injection protection is SECURE${NC}"
99+
TOKEN=""
100+
TMP_AUTH="$(mktemp)"
101+
AUTH_CODE=$(curl -s -o "${TMP_AUTH}" -w "%{http_code}" --max-time 10 \
102+
-X POST "${API}/auth/login" \
103+
-H "Content-Type: application/json" \
104+
-d "{\"email\":\"${TEST_EMAIL}\",\"password\":\"${TEST_PASSWORD}\"}" 2>/dev/null) || AUTH_CODE="error"
105+
TOKEN=$(python3 -c "
106+
import sys, json
107+
try:
108+
d = json.load(open('${TMP_AUTH}'))
109+
t = (d.get('access_token') or d.get('token')
110+
or d.get('data', {}).get('access_token')
111+
or d.get('data', {}).get('token') or '')
112+
print(t)
113+
except Exception:
114+
pass
115+
" 2>/dev/null) || TOKEN=""
116+
rm -f "${TMP_AUTH}"
117+
118+
if [ -z "${TOKEN}" ]; then
119+
warn "Could not obtain auth token (HTTP ${AUTH_CODE}) — skipping Phase 2"
120+
warn "Start the API and check TEST_EMAIL / TEST_PASSWORD env vars"
121+
else
122+
echo "Auth token obtained. Running authenticated SQLi checks..."
94123
echo ""
95-
echo "Notes:"
96-
echo "- Queries use parameterization"
97-
echo "- Authentication required on endpoints"
98-
echo "- No SQL errors leaked to client"
124+
125+
PAYLOADS=(
126+
"top' OR '1'='1"
127+
"top' OR 1=1--"
128+
"top' UNION SELECT NULL,NULL,NULL--"
129+
"') UNION SELECT username||'_'||password FROM Users--"
130+
"top' AND 1=CAST(version() AS INTEGER)--"
131+
"top'; SELECT pg_sleep(1);--"
132+
"top' AND (SELECT pg_sleep(1))=''--"
133+
)
134+
LABELS=(
135+
"Classic OR injection"
136+
"OR 1=1 with comment"
137+
"UNION column count probe"
138+
"UNION credential extraction"
139+
"Error-based (CAST version)"
140+
"Stacked pg_sleep"
141+
"Subquery pg_sleep"
142+
)
143+
144+
TOTAL="${#PAYLOADS[@]}"
145+
for (( i=0; i<TOTAL; i++ )); do
146+
payload="${PAYLOADS[$i]}"
147+
label="${LABELS[$i]}"
148+
ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${payload//\'/\\\'}', safe=''))" 2>/dev/null)
149+
TMP_RESP="$(mktemp)"
150+
START_MS=$(date +%s%3N)
151+
RESP=$(curl -s -o "${TMP_RESP}" -w "%{http_code}|%{time_total}" --max-time 8 \
152+
-H "Authorization: Bearer ${TOKEN}" \
153+
"${API}/players?role=${ENCODED}" 2>/dev/null) || RESP="error|0"
154+
END_MS=$(date +%s%3N)
155+
ELAPSED=$(( END_MS - START_MS ))
156+
HTTP_CODE="${RESP%%|*}"
157+
RESP_BODY=$(cat "${TMP_RESP}" 2>/dev/null)
158+
rm -f "${TMP_RESP}"
159+
160+
if has_sql_error "${RESP_BODY}"; then
161+
fail "${label} -> HTTP ${HTTP_CODE} [SQL ERROR LEAKED]"
162+
elif echo "${RESP_BODY}" | grep -qiE '(\$2[aby]\$|password_digest|bcrypt)'; then
163+
fail "${label} -> HTTP ${HTTP_CODE} [CREDENTIAL DATA IN RESPONSE]"
164+
elif [ "${HTTP_CODE}" = "500" ]; then
165+
fail "${label} -> HTTP 500"
166+
elif [ "${ELAPSED}" -ge 900 ] && echo "${label}" | grep -qi "sleep"; then
167+
fail "${label} -> HTTP ${HTTP_CODE} ${ELAPSED}ms [POSSIBLE TIME INJECTION]"
168+
else
169+
pass "${label} -> HTTP ${HTTP_CODE} ${ELAPSED}ms"
170+
fi
171+
sleep 0.1
172+
done
173+
fi
174+
175+
echo ""
176+
echo "======================================"
177+
echo "RESULTS"
178+
echo "======================================"
179+
echo "Passed : $PASSED"
180+
echo "Failed : $FAILED"
181+
echo "Warnings: $WARNINGS"
182+
echo ""
183+
184+
if [ "$FAILED" -gt 0 ]; then
185+
echo -e "${RED}VULNERABLE: $FAILED test(s) failed — run test-sqli-advanced.sh for full analysis${NC}"
186+
exit 1
187+
elif [ "$WARNINGS" -gt 0 ]; then
188+
echo -e "${YELLOW}UNCERTAIN: $WARNINGS warning(s) — verify manually or run test-sqli-advanced.sh${NC}"
99189
exit 0
100190
else
101-
echo -e "${RED}✗ SQL Injection vulnerabilities detected!${NC}"
102-
exit 1
191+
echo -e "${GREEN}SECURE: All quick checks passed${NC}"
192+
echo ""
193+
echo "For deeper coverage (blind, time-based, second-order):"
194+
echo " bash test-sqli-advanced.sh"
195+
exit 0
103196
fi

0 commit comments

Comments
 (0)