In this article
Cómo escribir código seguro en Go
Hablemos de la importancia de escribir código seguro en Go. Escribir código seguro es una responsabilidad fundamental de los desarrolladores, en especial en un lenguaje como Go, que se usa ampliamente para crear sistemas escalables y eficientes. La simplicidad y el rendimiento de Go lo convierten en una opción popular para las aplicaciones nativas de la nube, sobre todo por su relación con el ecosistema de herramientas de Kubernetes, pero las vulnerabilidades de seguridad pueden socavar estos beneficios. Asegurarte de que tu código Go sea seguro protege tu aplicación, resguarda los datos de los usuarios y mantiene la confianza.
¿Cuáles de estas vulnerabilidades de seguridad comunes afectan a las aplicaciones escritas en Go?
Descripción general de las vulnerabilidades de seguridad comunes en las aplicaciones Go
Las aplicaciones Go son susceptibles a diversas vulnerabilidades de seguridad que pueden comprometer su integridad, confidencialidad y disponibilidad (también conocidas como la tríada CIA en la jerga de seguridad). Algunas de las vulnerabilidades más comunes incluyen:
La inyección SQL ocurre cuando se concatena información no confiable en las consultas SQL, lo que permite que los atacantes manipulen su ejecución.
La inyección de comandos ocurre cuando la entrada del usuario no se maneja correctamente en los comandos de shell, lo que permite ejecutar comandos arbitrarios.
La falsificación de solicitudes del lado del servidor (SSRF) ocurre cuando una aplicación obtiene una URL proporcionada por el usuario, lo que puede permitir que los atacantes envíen solicitudes a servicios internos.
El papel de herramientas como Snyk Code para identificar y corregir vulnerabilidades
Herramientas como Snyk Code son esenciales para identificar y corregir vulnerabilidades en aplicaciones Go. Snyk Code analiza tu código fuente en busca de problemas de seguridad y ofrece información práctica y recomendaciones para corregirlos. Integrar Snyk en tu flujo de trabajo de desarrollo te permite detectar vulnerabilidades a tiempo, reducir el riesgo de brechas de seguridad y mantener una base de código segura.
Ejemplo en Go: detectar inyección SQL con Snyk Code
Considera el siguiente fragmento de código Go, vulnerable a la inyección SQL:
package main
import (
"database/sql"
"fmt"
"log"
"net/http"
_ "github.com/lib/pq"
)
func main() {
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
username := r.URL.Query().Get("username")
db, err := sql.Open("postgres", "user=youruser dbname=yourdb sslmode=disable")
if err != nil {
log.Fatal(err)
}
defer db.Close()
query := fmt.Sprintf("SELECT * FROM users WHERE username = '%s'", username)
rows, err := db.Query(query)
if err != nil {
http.Error(w, "Database error", http.StatusInternalServerError)
return
}
defer rows.Close()
// Process rows...
})
log.Fatal(http.ListenAndServe(":8080", nil))
}En este ejemplo, el parámetro username se inserta directamente en la consulta SQL, lo que la hace vulnerable a la inyección SQL. Snyk Code puede detectar esta vulnerabilidad y recomendar el uso de consultas parametrizadas para prevenirla:
query := "SELECT * FROM users WHERE username = $1"
rows, err := db.Query(query, username)1. Validar y sanitizar la entrada del usuario en Go
La validación y sanitización de entradas son fundamentales para proteger tus aplicaciones Go contra vectores de ataque comunes, como la inyección SQL y el scripting entre sitios (XSS). Nunca se debe confiar en la entrada del usuario, ya provenga de parámetros de consulta, cuerpos de respuestas JSON de solicitudes HTTP o contenido de bases de datos. Valida siempre las entradas según los esquemas esperados, aplica la lógica de sanitización cuando sea necesario y codifica la salida según el contexto.
Prácticas recomendadas para validar entradas en Go
Al manejar entradas del usuario en Go, sigue estas prácticas recomendadas:
Define esquemas de entrada: usa esquemas estrictos para definir las entradas válidas. Bibliotecas como
go-playground/validatorpueden ayudarte a aplicar estas reglas.Sanitiza las entradas: elimina o escapa los caracteres potencialmente dañinos de las entradas del usuario. Por ejemplo, codifica el HTML de las entradas para prevenir XSS. En gran medida, esta práctica es útil y se implementa en las herramientas de motores de plantillas.
Usa consultas parametrizadas: prevén la inyección SQL con consultas parametrizadas en lugar de concatenar cadenas.
Codifica la salida: codifica la salida según el contexto, como HTML para páginas web o JSON para respuestas de API.
Ejemplo en Go: vinculación de parámetros para prevenir la inyección SQL
La inyección SQL es una vulnerabilidad grave de seguridad a nivel de aplicación que ocurre cuando la entrada del usuario se incluye directamente en las consultas SQL. Para prevenirla, usa consultas parametrizadas:
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
func getUser(db *sql.DB, userID string) (User, error) {
var user User
err := db.QueryRow("SELECT id, name FROM users WHERE id = ?", userID).Scan(&user.ID, &user.Name)
if err != nil {
return user, err
}
return user, nil
}En este ejemplo, el marcador de posición ? de la consulta SQL se reemplaza de forma segura por el parámetro userID, lo que previene los ataques de inyección SQL. Esta práctica recomendada de seguridad ayuda al motor de base de datos a diferenciar el significado original de la consulta de los valores que utiliza.
2. Evitar el uso de subshells en Go
El uso de subshells en Go se refiere al inicio de subprocesos mediante funciones como exec.CommandContext() o exec.Command(). Aunque ofrecen flexibilidad, estas funciones también presentan riesgos de seguridad importantes, en especial vulnerabilidades de inyección de comandos. La inyección de comandos ocurre cuando un atacante manipula datos de entrada para ejecutar comandos arbitrarios en el sistema host. Esto puede provocar acceso no autorizado a datos, corrupción de datos o incluso un mayor compromiso del sistema.
Alternativas al uso de subshells en Go: API de bibliotecas nativas, FFI, etc.
Para mitigar los riesgos asociados al uso de subshells, los desarrolladores deberían preferir las API de bibliotecas nativas de Go o la interfaz de funciones externas (FFI) para realizar tareas que, de otro modo, requerirían comandos externos. La biblioteca estándar de Go es sólida y ofrece muchas utilidades para manipular archivos, procesar imágenes y más, que a menudo pueden reemplazar la necesidad de usar subshells.
Ejemplo en Go: inyección de comandos al manipular imágenes
Considera el ejemplo en el que se usa la entrada del usuario para manipular un archivo de imagen. Este ejemplo muestra un error común que puede dar lugar a una inyección de comandos:
package main
import (
"os/exec"
)
func manipulateImage(targetFilename string) error {
convertCmd := fmt.Sprintf("convert %s -resize %sx%s %s", targetFilename, fileSize, fileSize, targetFilename)
_, err = exec.CommandContext(ctx, "sh", "-c", convertCmd).CombinedOutput()
}
En este ejemplo, si targetFilename se obtiene de la entrada del usuario, un atacante podría inyectar comandos adicionales creando un nombre de archivo malicioso. Por ejemplo, un nombre de archivo como image.png; rm -rf /tmp podría ejecutar un comando destructivo si no se sanitiza correctamente.
El riesgo se debe al uso directo de la entrada del usuario para construir comandos de shell y al hecho de que el código no usa argumentos de comando preparados para crear el comando. Para mitigar este riesgo, considera las siguientes estrategias:
Usa bibliotecas nativas: en lugar de depender de programas externos de línea de comandos, usa las bibliotecas nativas de Go.
Valida y sanitiza las entradas: valida y sanitiza siempre las entradas del usuario para asegurarte de que cumplan con los patrones esperados. Esto puede impedir que se ejecuten entradas maliciosas. En el ejemplo de código vulnerable, se debería evitar la interpolación directa en el shell mediante
sh -cy ejecutar el comando directamente. Sin embargo, esto también implica riesgos de vulnerabilidades de inyección de argumentos, que deben sanitizarse y escaparse correctamente.
3. Construir URL de forma segura
La necesidad de construir URL seguras se relaciona directamente con una de las 10 principales vulnerabilidades de seguridad de OWASP: la falsificación de solicitudes del lado del servidor (SSRF). La SSRF es una vulnerabilidad de seguridad crítica en la que un atacante puede manipular un servidor para que envíe solicitudes HTTP no deseadas, por ejemplo, a recursos internos, otros microservicios privados o, en general, a cualquier otro servicio no autorizado. Esto puede provocar acceso no autorizado a servicios internos, exfiltración de datos o incluso ejecución remota de código. Para comprender la SSRF en detalle, consultaré la lección sobre SSRF de Snyk Learn.
Estrategias para construir URL seguras en Go
Al construir URL en Go, sobre todo cuando se incluye información del usuario, es fundamental implementar estrategias que protejan contra la SSRF. Estas son algunas prácticas recomendadas:
Valida las URL: valida siempre las URL proporcionadas por los usuarios con una lista de dominios o patrones verificados y aprobados. Así te aseguras de que solo se realicen solicitudes legítimas.
Usa el análisis de URL: utiliza el paquete
net/urlde Go para analizar y validar las URL. Esto ayuda a desglosar la URL en sus componentes y verificar cada parte. Por ejemplo, en esta etapa puedes aplicar un control de seguridad para asegurarte de que solo se permita un conjunto específico de esquemas de protocolo compatibles (https://), y nohttpu otros.Restringe el acceso a la red interna: asegúrate de que tu aplicación no solicite direcciones IP internas ni puntos de conexión sensibles. Para ello, aplica sidecars de Kubernetes, proxies HTTP, gateways de API y otros controles de seguridad que tengan en cuenta la red.
Establece tiempos de espera y límites: aunque no se relaciona directamente con la SSRF, establecer tiempos de espera y límites adecuados para el tamaño de las respuestas de solicitudes salientes ayuda a evitar el agotamiento de recursos y la exfiltración de grandes cantidades de datos, con el objetivo de minimizar el impacto de una SSRF.
Ejemplo en Go: vulnerabilidad SSRF a partir de URL proporcionadas por el usuario
El siguiente ejemplo muestra una forma insegura de manejar URL proporcionadas por el usuario, que puede dar lugar a una vulnerabilidad SSRF:
package main
import (
"net/http"
)
func fetchUserProfileImage(userURL string) (*http.Response, error) {
// Directly using user input to fetch remote data
resp, err := http.Get(userURL)
return resp, err
}En este código, la función fetchUserProfileImage toma una URL del usuario y la usa directamente en una solicitud HTTP GET. Un atacante puede aprovechar esto para enviar solicitudes a servicios internos.
Conclusión
Escribir código seguro en Go requiere vigilancia y apegarse a las prácticas recomendadas. Estos son los controles de seguridad clave que analizamos específicamente para desarrollar de forma segura en Go:
Validar y sanitizar las entradas del usuario que circulan por las rutas del código en Go.
Evitar las operaciones de subshell y preferir las API nativas de Go y las FFI cuando estén disponibles.
Tratar con extrema cautela las URL que provienen de la entrada del usuario para prevenir vulnerabilidades SSRF.
Usa Snyk Code para realizar análisis de seguridad continuos
Da el siguiente paso para proteger tus aplicaciones Go. Crea una cuenta gratuita de Snyk hoy mismo y empieza a integrar la seguridad en tu flujo de trabajo de desarrollo. Con Snyk, puedes crear aplicaciones seguras con confianza y proteger a tus usuarios.
Para asegurarte de que tu código Go siga siendo seguro, aprovecha Snyk Code. Ofrece análisis de seguridad continuos que te ayudan a identificar y corregir vulnerabilidades en tu base de código antes de que se conviertan en un problema.
Mira Snyk Code en acción en mi IDE de VS Code, donde detecta vulnerabilidades y utiliza un motor de aprendizaje automático para ayudarme con sugerencias sobre cómo escribir código más seguro en Golang:

¡Compite en Fetch the Flag 2026!
Pon a prueba tus habilidades de seguridad en nuestro evento Capture the Flag, del 12 al 13 de febrero, de 12 p. m. ET a 12 p. m. ET.