Seguridad de aplicaciones Go: top 10 vulnerabilidades y métodos de protección
El lenguaje Go (Golang) ha ganado popularidad gracias a su alto rendimiento, simplicidad y mecanismos de seguridad integrados. Sin embargo, como cualquier otro lenguaje, no protege al desarrollador de todas las amenazas posibles. Errores de diseño, falta de atención al trabajar con datos externos y el uso incorrecto de las bibliotecas estándar pueden provocar vulnerabilidades graves. En este artículo analizaremos las 10 vulnerabilidades más críticas de las aplicaciones Go, proporcionaremos ejemplos de código y explicaremos cómo evitarlas.
1. Inyección SQL (SQL Injection)
A pesar de la existencia del paquete database/sql, los desarrolladores a menudo cometen el error de concatenar cadenas al construir consultas SQL. Esta es una vulnerabilidad clásica que permite a un atacante ejecutar código SQL arbitrario.
Código inseguro:
// PELIGROSO: concatenación de cadenasfunc getUser(w http.ResponseWriter, r *http.Request) { username := r.URL.Query().Get("username") query := "SELECT * FROM users WHERE username = '" + username + "'" rows, err := db.Query(query) // ...}Código seguro con parametrización:
func getUser(w http.ResponseWriter, r *http.Request) { username := r.URL.Query().Get("username") // Usamos placeholders query := "SELECT * FROM users WHERE username = $1" rows, err := db.Query(query, username) // ...}Recomendación: Utilice siempre consultas parametrizadas. Nunca concatene la entrada del usuario en cadenas SQL.
2. Cross-Site Scripting (XSS)
Si su aplicación Go renderiza HTML en el servidor (por ejemplo, usando html/template), es necesario escapar la salida. El paquete integrado text/template no lo hace.
Código inseguro:
// Usar text/template es peligrosotmpl, _ := template.New("test").Parse("<h1>{{.Name}}</h1>")tmpl.Execute(w, map[string]string{"Name": "<script>alert('XSS')</script>"})Código seguro:
// Siempre use html/template para plantillas HTMLimport "html/template"tmpl, _ := template.New("test").Parse("<h1>{{.Name}}</h1>")tmpl.Execute(w, map[string]string{"Name": "<script>alert('XSS')</script>"})// Salida: <script>alert('XSS')</script>Recomendación: Para trabajar con HTML, use solo html/template. Siempre valide y escape la entrada del usuario, especialmente si se muestra en atributos de etiquetas o JavaScript.
3. Race Condition (Condición de carrera)
Go es un lenguaje con concurrencia, y la condición de carrera es uno de los problemas más comunes. Puede provocar corrupción de datos, fugas de memoria y fallos inesperados.
Código inseguro:
var counter int
func increment() { for i := 0; i < 1000; i++ { counter++ // Operación no atómica }}
func main() { go increment() go increment()}Código seguro con mutex:
var ( counter int mu sync.Mutex)
func increment() { for i := 0; i < 1000; i++ { mu.Lock() counter++ mu.Unlock() }}
func main() { go increment() go increment()}Recomendación: Use sync.Mutex, sync.RWMutex u operaciones atómicas (sync/atomic). Ejecute pruebas con la bandera -race para detectar condiciones de carrera.
4. Path Traversal (Recorrido de rutas)
La vulnerabilidad surge cuando la aplicación utiliza la entrada del usuario para formar la ruta a un archivo sin la verificación adecuada.
Código inseguro:
func serveFile(w http.ResponseWriter, r *http.Request) { f