diff --git a/docker-compose.yml b/docker-compose.yml index 9d61378..42a6dac 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,8 @@ services: network_mode: host restart: unless-stopped environment: - - PORT=5001 + - PORT=5000 + - REDIS_ADDR=localhost:6379 depends_on: redis: condition: service_healthy @@ -21,9 +22,8 @@ services: ports: - "6379:6379" healthcheck: - test: ["executable", "arg"] - interval: 1m30s - timeout: 30s + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s retries: 5 - start_period: 30s - \ No newline at end of file + start_period: 10s diff --git a/go_security_app/blocker_test.go b/go_security_app/blocker_test.go index 3705e09..f0cf160 100644 --- a/go_security_app/blocker_test.go +++ b/go_security_app/blocker_test.go @@ -100,6 +100,15 @@ func TestAuthCheckHandler(t *testing.T) { {"Allowed Path GET with Query", "GET", "/some/valid/path?user=1", http.StatusOK}, {"Allowed Path POST", "POST", "/api/submit", http.StatusOK}, + // Legitimate traffic that must not be blocked. + {"Allowed category query", "GET", "/products?category=books", http.StatusOK}, + {"Allowed selected query", "GET", "/list?selected=1&sort=updated", http.StatusOK}, + {"Allowed word echo in path", "GET", "/blog/echolocation-in-bats", http.StatusOK}, + {"Allowed empty parens", "GET", "/calc?formula=sum()", http.StatusOK}, + {"Allowed concatenate param", "GET", "/api?op=concatenate&drop=false", http.StatusOK}, + {"Allowed dropdown param", "GET", "/ui?dropdown=open", http.StatusOK}, + {"Allowed cat substring", "GET", "/vacation-rentals?location=france", http.StatusOK}, + // Blocked Paths {"Blocked PHP Suffix", "GET", "/index.php", http.StatusForbidden}, {"Blocked WP-Admin Prefix", "GET", "/wp-admin/options.php", http.StatusForbidden}, diff --git a/go_security_app/main.go b/go_security_app/main.go index 81e3d78..ff4b1a3 100644 --- a/go_security_app/main.go +++ b/go_security_app/main.go @@ -20,46 +20,42 @@ import ( var blockedPathSuffixes = []string{ ".php", ".aspx", ".jsp", ".htaccess", ".htpasswd", ".git/", ".env", } -var blockedPathPrefixes = []string{ - "/wp-admin/", "/wp-includes/", "/admin/", "/remote/", "/manager/", -} -var blockedExactPaths = []string{ - "/xmlrpc.php", "/config.json", "/configuration.php", -} var blockedPathContains = []string{ - ".git/", "wp-includes/", "wp-admin/", "admin/", "remote/", "manager/", "config.json", "configuration.php", + ".git/", "wp-includes/", "wp-admin/", "admin/", "remote/", "manager/", + "xmlrpc.php", "config.json", "configuration.php", } + +// Encoded markers matched against the raw query before decoding, used as a +// fallback when the query has malformed encoding. Plain keywords are left to +// suspiciousDecodedQueryRegex so values like ?category= are not blocked. var suspiciousRawQuerySubstrings = []string{ - "%24%7B", "%3Cscript", "%27%20OR%20", "UNION%20SELECT", "%2E%2E%2F", "../", "etc/passwd", "eval(", "base64_decode", "()", - "cat", "passwd", "%2Fetc%2F", "%2F..%2F", "..%2F", "%2F..", "phpinfo", "%28%29", - "SELECT", "INSERT", "UPDATE", "DELETE", "DROP", "UNION", "alert(", "onerror=", "onload=", "'OR", "OR'", "'AND", "AND'", + "%24%7b", // ${ + "%3cscript", + "%2e%2e%2f", + "..%2f", + "%2f..", + "../", + "%2fetc%2f", + "etc/passwd", } -var commandInjectionPatterns = []string{ - `\|\s*\w+`, - `echo\s+['"]?.*['"]?\s*\|`, - `[;&\|\\` + "`" + `]\s*\w+`, - `\$\(\w+`, - `\` + "`" + `\w+`, - `echo ['"].*?['"].*?\|.*?grep`, // Specifically match "echo 'evil' | grep e" - `%7C`, // URL encoded pipe character | - `echo`, // detects the "echo" command -} +// Checked against both the decoded path and query. +var commandInjectionRegex = regexp.MustCompile( + "(?i)(&&|\\|\\||;\\s*\\w|\\s\\|\\s|`|\\$\\(|\\bcat\\s+/|\\beval\\(|base64_decode|phpinfo\\(\\)|system\\(\\)|exec\\(\\)|shell_exec\\(\\)|passthru\\()") var suspiciousDecodedQueryRegex = []*regexp.Regexp{ - regexp.MustCompile(`(?i)(\s(SELECT|UNION|INSERT|UPDATE|DELETE)\s|\s(OR|AND)\s*['"]?1['"]?\s*=\s*['"]?1|--|#|\/\*.*\*\/)`), - regexp.MustCompile(`(?i)(