Security of Go Applications: Top 10 Vulnerabilities for Beginners
\n\nGo (Golang) is one of the most popular languages for building high-load web services, microservices, and network applications. However, like any other language, Go requires a careful approach to security. If you are just starting to learn Go, it is important to know the main vulnerabilities that may threaten your application. In this article, we will cover the 10 most common threats and show how to avoid them.
\n\n1. SQL Injection
\n\nSQL injection is one of the oldest and most dangerous vulnerabilities. It occurs when user input is directly inserted into an SQL query.
\n\nExample of vulnerable code:
\nfunc getUser(db *sql.DB, id string) (*User, error) {\n row := db.QueryRow("SELECT * FROM users WHERE id = " + id)\n // ...\n}\n\nHow to fix:
\nUse parameterized queries:
\nfunc getUser(db *sql.DB, id string) (*User, error) {\n row := db.QueryRow("SELECT * FROM users WHERE id = $1", id)\n // ...\n}\n\n2. Cross-Site Scripting (XSS)
\n\nXSS occurs when unescaped user input is displayed on a web page. In Go, this often happens when using html/template instead of text/template.
Example of vulnerable code:
\nfmt.Fprintf(w, "<p>Hello, %s</p>", r.URL.Query().Get("name"))\n\nHow to fix:
\nUse html/template for automatic escaping:
tmpl := template.Must(template.New("").Parse("<p>Hello, {{.}}</p>"))\ntmpl.Execute(w, r.URL.Query().Get("name"))\n\n3. Cross-Site Request Forgery (CSRF)
\n\nCSRF attacks force a user to perform an unwanted action on a site where they are authenticated. Go applications often forget about CSRF protection.
\n\nSolution:
\nUse the gorilla/csrf library or implement your own middleware:
import "github.com/gorilla/csrf"\n\nfunc main() {\n r := mux.NewRouter()\n r.Use(csrf.Protect([]byte("32-byte-key")))\n // ...\n}\n\n4. Insecure Error Handling
\n\nA common mistake is returning system error messages to the user, which can reveal the internal structure of the application.
\n\nExample of vulnerable code:
\nif err != nil {\n http.Error(w, err.Error(), http.StatusInternalServerError)\n}\n\nHow to fix:
\nif err != nil {\n log.Printf("Error: %v", err)\n http.Error(w, "Internal Server Error", http.StatusInternalServerError)\n}\n\n5. Information Leakage via Headers
\n\nBy default, a Go server may reveal the Go version, server type, and other service information.
\n\nSolution:
\ntype secureHandler struct {\n next http.Handler\n}\n\nfunc (h *secureHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n w.Header().Set("X-Content-Type-Options", "nosniff")\n w.Header().Set("X-Frame-Options", "DENY")\n w.Header().Set("X-XSS-Protection", "1; mode=block")\n h.next.ServeHTTP(w, r)\n}\n\n6. Incorrect Goroutine Handling
\n\nGoroutines are a powerful Go tool, but their incorrect use can lead to race conditions and memory leaks.
\n\nExample of vulnerable code:
\nvar counter int\nfor i := 0; i < 1000; i++ {\n go func() {\n counter++ // race condition!\n }()\n}\n\nHow to fix:
\nvar mu sync.Mutex\nvar counter int\nfor i := 0; i < 1000; i++ {\n go func() {\n mu.Lock()\n counter++\n mu.Unlock()\n }()\n}\n\n7. Insecure Password Storage
\n\nStoring passwords in plain text or using weak hashes (MD5, SHA1) is a serious mistake.<