Skip to content

Instantly share code, notes, and snippets.

@armamini
Created June 6, 2025 09:31
Show Gist options
  • Select an option

  • Save armamini/e09bd006478d1aa4cc188f353f44bbc4 to your computer and use it in GitHub Desktop.

Select an option

Save armamini/e09bd006478d1aa4cc188f353f44bbc4 to your computer and use it in GitHub Desktop.
CORS Checker
package main
import (
"flag"
"fmt"
"net/http"
"net/url"
"os"
"strings"
"time"
)
type result struct {
TestOrigin string
ACAOHeader string // Access-Control-Allow-Origin
ACACHeader string // Access-Control-Allow-Credentials
IsPotentiallyInsecure bool
Reason string
IsMajorIssue bool
}
func main() {
targetURLArg := flag.String("url", "", "The target URL to check for CORS policy")
flag.Parse()
if *targetURLArg == "" {
fmt.Println("Error: -url flag is required.")
flag.Usage()
os.Exit(1)
}
targetURL := *targetURLArg
if !strings.HasPrefix(targetURL, "http://") && !strings.HasPrefix(targetURL, "https://") {
targetURL = "http://" + targetURL // Default to http if no scheme
}
parsedTargetURL, err := url.Parse(targetURL)
if err != nil {
fmt.Printf("Error: Invalid URL provided: %v\n", err)
os.Exit(1)
}
// Derive the actual origin of the target URL for comparison
actualTargetOrigin := fmt.Sprintf("%s://%s", parsedTargetURL.Scheme, parsedTargetURL.Host)
fmt.Printf("🔍 Checking CORS policy for: %s\n", targetURL)
fmt.Printf(" (Target's own origin: %s)\n\n", actualTargetOrigin)
testOrigins := []string{
"http://evil-attacker.com",
"https://another-random-site.org",
"null",
}
// HTTP client with a timeout
client := &http.Client{
Timeout: 10 * time.Second,
// Prevent following redirects automatically, as we want to inspect headers of the initial response
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
var results []result
overallInsecureDetected := false
majorIssueFound := false
for _, testOrigin := range testOrigins {
fmt.Printf("🧪 Testing with Origin: %s\n", testOrigin)
req, err := http.NewRequest("GET", targetURL, nil)
if err != nil {
fmt.Printf(" ❌ Error creating request for origin %s: %v\n\n", testOrigin, err)
results = append(results, result{TestOrigin: testOrigin, Reason: fmt.Sprintf("Error creating request: %v", err)})
continue
}
req.Header.Set("Origin", testOrigin)
req.Header.Set("User-Agent", "Go-CORS-Checker/1.0")
resp, err := client.Do(req)
if err != nil {
fmt.Printf(" ❌ Error sending request for origin %s: %v\n\n", testOrigin, err)
results = append(results, result{TestOrigin: testOrigin, Reason: fmt.Sprintf("Error sending request: %v", err)})
continue
}
defer resp.Body.Close()
acaoHeader := resp.Header.Get("Access-Control-Allow-Origin")
acacHeader := resp.Header.Get("Access-Control-Allow-Credentials")
currentResult := result{
TestOrigin: testOrigin,
ACAOHeader: acaoHeader,
ACACHeader: acacHeader,
}
fmt.Printf(" ➡️ Access-Control-Allow-Origin: %s\n", formatHeaderValue(acaoHeader))
fmt.Printf(" ➡️ Access-Control-Allow-Credentials: %s\n", formatHeaderValue(acacHeader))
if acaoHeader == "*" {
currentResult.IsPotentiallyInsecure = true
currentResult.Reason = "ACAO is '*' (allows any origin)."
if acacHeader == "true" {
currentResult.IsMajorIssue = true
currentResult.Reason += " CRITICAL: ACAC is 'true' with ACAO='*' - this is a severe misconfiguration!"
majorIssueFound = true
}
} else if acaoHeader == testOrigin && testOrigin != actualTargetOrigin {
currentResult.IsPotentiallyInsecure = true
currentResult.Reason = fmt.Sprintf("ACAO reflects the sent arbitrary origin '%s'.", testOrigin)
if acacHeader == "true" {
currentResult.IsMajorIssue = true
currentResult.Reason += " CRITICAL: ACAC is 'true' with dynamically reflected arbitrary origin!"
majorIssueFound = true
}
} else if acaoHeader == "null" && testOrigin == "null" {
currentResult.IsPotentiallyInsecure = true
currentResult.Reason = "ACAO is 'null' when 'null' Origin was sent. This can be an issue for sensitive data accessible via file:// or sandboxed iframes."
if acacHeader == "true" {
currentResult.Reason += " NOTE: ACAC is 'true' with 'null' origin. Review implications."
}
} else if acaoHeader != "" && acaoHeader != actualTargetOrigin {
// ACAO is a specific, different origin, but not one we sent or '*'
// This is generally fine, means it's configured for a specific list.
// We only flag if it matches our *test* arbitrary origins.
}
if currentResult.IsPotentiallyInsecure {
overallInsecureDetected = true
fmt.Printf(" ⚠️ Potentially Insecure: %s\n", currentResult.Reason)
} else if acaoHeader == "" {
fmt.Println(" ℹ️ No Access-Control-Allow-Origin header found. (Standard behavior if CORS not intended for this origin).")
} else {
fmt.Println(" ✅ No obvious CORS misconfiguration detected for this origin.")
}
fmt.Println("") // Newline for readability between origin tests
results = append(results, currentResult)
}
fmt.Println("------------------- SUMMARY -------------------")
if !overallInsecureDetected {
fmt.Println("✅ No obvious insecure CORS configurations detected based on the tested origins.")
fmt.Println(" Note: This tool performs basic checks. For a full assessment, consider the specific application context,")
fmt.Println(" sensitivity of data, and whether OPTIONS preflight requests behave differently.")
os.Exit(0)
} else {
if majorIssueFound {
fmt.Println("🚨 CRITICAL CORS MISCONFIGURATION DETECTED! 🚨")
} else {
fmt.Println("⚠️ Potentially Insecure CORS Configurations Detected:")
}
for _, r := range results {
if r.IsPotentiallyInsecure {
fmt.Printf(" - For Origin '%s': %s (ACAO: '%s', ACAC: '%s')\n",
r.TestOrigin, r.Reason, formatHeaderValue(r.ACAOHeader), formatHeaderValue(r.ACACHeader))
}
}
fmt.Println("\nRecommendations:")
fmt.Println(" - Avoid using `Access-Control-Allow-Origin: *` if sensitive data or credentials are involved.")
fmt.Println(" - Do not dynamically reflect arbitrary `Origin` headers in `Access-Control-Allow-Origin`.")
fmt.Println(" - Use a specific whitelist of allowed origins if possible.")
fmt.Println(" - Be cautious with `Access-Control-Allow-Credentials: true`; only use it with trusted, specific origins.")
fmt.Println(" - Test with OPTIONS requests as well, as preflight behavior can differ.")
os.Exit(1) // Exit with error code if insecurities found
}
}
func formatHeaderValue(headerValue string) string {
if headerValue == "" {
return "(not set)"
}
return headerValue
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment