Cómo prevenir SQL injection en Go
Clase 5 de 30 • Curso de Ciberseguridad para Desarrollo Web
Contenido del curso
Funciona en mi local
Introducción a DevSecOps
Seguridad en la arquitectura
- 11

Arquitectura AWS para métricas de Git
02:24 min - 12

Configuración de AWS CLI para Terraform
09:34 min - 13

Terraform IAM: roles y policies automáticos
17:44 min - 14

Modificando main.tf para enlazar módulos IAM en Terraform
06:02 min - 15

Bucket S3 para Lambdas con Terraform
16:44 min - 16

Configuración de Postgres RDS con VPC y seguridad
14:10 min - 17

Configurando VPC para AWS Lambda con Terraform
12:29 min - 18

Cómo configurar API Gateway para Lambdas
05:42 min
Evitando vulnerabilidades en el código
- 19

Configuración completa de Auth0 para tokens
07:14 min - 20

Authorizer Lambda con Auth0 y JWT
16:56 min - 21

Conecta Go a Postgres usando AWS Secrets
13:35 min - 22

Conexión segura de Lambdas a Secrets con VPC
11:27 min - 23

Validación de webhooks desde GitHub con user-agent
12:08 min - 24

Cómo validar integridad de webhooks con HMAC
14:32 min
Controles de seguridad sobre datos
Monitoring y alertas
CORS y cierre
Construye un flujo robusto en Go para guardar un webhook de GitHub en base de datos con un repositorio seguro. Aquí verás cómo modelar el payload, definir una entidad para la tabla de commits, crear una interfaz Commit para poder mockear, y usar placeholders con database/sql para prevenir SQL injection con total claridad.
¿Cómo modelar el webhook y reducir el payload en Go?
Al recibir el webhook, la clave está en quedarse solo con lo útil. Se propone crear un paquete models y pedir a una IA (por ejemplo, Google Gemini) una estructura inicial a partir del JSON del payload, para luego recortar campos. La meta: usar solo head commit y datos del repository que importan.
¿Qué campos del webhook son esenciales?
- repository.full_name: nombre completo del repositorio.
- head commit: solo un commit, no la lista completa.
- En el commit: id, message, author.username y author.email.
- Se descartan: user, pusher y otros campos no usados.
¿Cómo queda el modelo en Go?
package models
type Author struct {
Username string `json:"username"`
Email string `json:"email"`
}
type Commit struct {
ID string `json:"id"`
Message string `json:"message"`
Author Author `json:"author"`
}
type Repository struct {
FullName string `json:"full_name"`
}
type Webhook struct {
Repository Repository `json:"repository"`
Commit Commit `json:"head_commit"`
}
Con esto, el webhook se puede hacer unmarshal a un modelo claro y mínimo, listo para persistir lo relevante.
¿Qué entidad de base de datos representa cada commit?
Antes del repository, define una estructura de entidad que represente una fila en la tabla commits. Crea un paquete entity y una estructura que incluya metadatos y el payload completo para auditoría.
¿Qué atributos guarda cada fila?
- ID de la fila.
- repo_full_name: nombre del repositorio.
- commit_id y message.
- author_username y author_email.
- payload: el JSON del webhook para trazabilidad.
- created_at y updated_at usando la librería de Go de time.
¿Cómo se define en Go?
package entity
import "time"
type Commit struct {
ID int64 `db:"id"`
RepoFullName string `db:"repo_full_name"`
CommitID string `db:"commit_id"`
Message string `db:"message"`
AuthorUsername string `db:"author_username"`
AuthorEmail string `db:"author_email"`
Payload []byte `db:"payload"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
Esta entidad centraliza lo que tu engineering manager necesita para analizar commits por desarrollador y mantener historial.
¿Cómo crear un repositorio en Go y prevenir SQL injection?
Crea una carpeta repository y define una interfaz para poder mockear y testear. Implementa métodos clave: Insert y GetByAuthorEmail, siempre con context y placeholders.
¿Cómo luce la interfaz commit?
package repository
import (
"context"
"database/sql"
"your/module/entity"
)
type Commit interface {
Insert(ctx context.Context, c entity.Commit) error
GetByAuthorEmail(ctx context.Context, email string) ([]entity.Commit, error)
}
type commitRepo struct { db *sql.DB }
func NewCommit(db *sql.DB) Commit { return &commitRepo{db: db} }
Tener una interfaz permite inyectar dobles en pruebas y desacoplar la base de datos.
¿Qué consulta evita SQL injection?
Nunca concatener strings para armar queries. Ejemplo inseguro:
// Inseguro: expuesto a SQL injection
q := "INSERT INTO commits (author_email) VALUES (" + email + ")"
Usa placeholders y parámetros, para que un ; delete from ... quede como string y no se ejecute:
func (r *commitRepo) Insert(ctx context.Context, c entity.Commit) error {
const q = `
INSERT INTO commits
(repo_full_name, commit_id, message, author_username, author_email, payload, created_at, updated_at)
VALUES
(?, ?, ?, ?, ?, ?, ?, ?)`
_, err := r.db.ExecContext(ctx, q,
c.RepoFullName,
c.CommitID,
c.Message,
c.AuthorUsername,
c.AuthorEmail,
c.Payload,
c.CreatedAt,
c.UpdatedAt,
)
return err
}
Esto aplica lo aprendido: database/sql, context, y placeholders para proteger tus operaciones.
¿Cómo obtener commits por author email?
func (r *commitRepo) GetByAuthorEmail(ctx context.Context, email string) ([]entity.Commit, error) {
const q = `
SELECT id, repo_full_name, commit_id, message, author_username, author_email, payload, created_at, updated_at
FROM commits
WHERE author_email = ?`
rows, err := r.db.QueryContext(ctx, q, email)
if err != nil { return nil, err }
defer rows.Close()
var out []entity.Commit
for rows.Next() {
var c entity.Commit
if err := rows.Scan(
&c.ID, &c.RepoFullName, &c.CommitID, &c.Message,
&c.AuthorUsername, &c.AuthorEmail, &c.Payload,
&c.CreatedAt, &c.UpdatedAt,
); err != nil {
return nil, err
}
out = append(out, c)
}
return out, rows.Err()
}
Con este método, tu repositorio devuelve un arreglo de commits filtrados por author_email, listo para reportes y análisis.
¿Qué dudas quieres compartir?
¿Te gustaría ver pruebas unitarias con mocking del repositorio o ajustar el modelo del webhook para nuevos campos del payload? Comparte tus preguntas y experiencias en los comentarios: construyamos mejoras entre todos.