Paquete html en Go: Guía Completa para Manejo Seguro de HTML
El paquete html en Go proporciona herramientas esenciales para trabajar con contenido HTML de manera segura, previniendo ataques comunes como XSS (Cross-Site Scripting). Esta guía cubre tanto el paquete principal html como el subpaquete html/template con ejemplos avanzados y mejores prácticas.
1. Funciones Principales del Paquete html
1.1 html.EscapeString
Propósito: Convertir caracteres especiales en entidades HTML para prevenir inyección de código.
Caracteres Escapados:
<→<>→>&→&"→"'→'
Ejemplo Detallado:
package main
import (
"fmt"
"html"
)
func main() {
userInput := `<script>alert("Hacked!")</script>`
safeOutput := html.EscapeString(userInput)
fmt.Println(safeOutput)
// Output: <script>alert("Hacked!")</script>
}
1.2 html.UnescapeString
Propósito: Revertir entidades HTML a sus caracteres originales.
Casos de Uso:
- Procesar contenido almacenado previamente escapado
- Mostrar texto original en editores HTML
Ejemplo con Entidades Complejas:
func main() {
escaped := "<div class="alert">&Atención</div>"
unescaped := html.UnescapeString(escaped)
fmt.Println(unescaped)
// Output: <div class="alert">&Atención</div>
}
2. Subpaquete html/template: Plantillas Seguras
2.1 Creación de Plantillas
Flujo Básico:
- Definir plantillas
- Parsear contenido
- Ejecutar con datos
Ejemplo de Plantilla con Estructuras:
type User struct {
Name string
Email string
IsAdmin bool
}
func main() {
const tpl = `
<!DOCTYPE html>
<html>
<head>
<title>Perfil de {{.Name}} </title>
</head>
<body>
<h1>{{.Name}}</h1>
{{if .IsAdmin}}<p class="admin">Administrador</p>{{end}}
<p>Contacto: <a href="mailto:{{.Email}}">{{.Email}}</a></p>
</body>
</html>`
tmpl := template.Must(template.New("profile").Parse(tpl))
user := User{"Ana", "ana@example.com", true}
tmpl.Execute(os.Stdout, user)
}
2.2 Funciones de Plantilla Personalizadas
Registro de Funciones:
func formatDate(t time.Time) string {
return t.Format("2006-01-02")
}
func main() {
tmpl := template.Must(template.New("").Funcs(template.FuncMap{
"formatDate": formatDate,
}).Parse(`<p>Fecha: {{. | formatDate}}</p>`))
tmpl.Execute(os.Stdout, time.Now())
}
3. Características Avanzadas de html/template
3.1 Herencia de Plantillas
Estructura de Archivos:
templates/
├── base.html
└── home.html
base.html:
{{define "base"}}
<!DOCTYPE html>
<html>
<head>
<title>{{template "title" .}}</title>
</head>
<body>
{{template "content" .}}
</body>
</html>
{{end}}
home.html:
{{define "title"}}Inicio{{end}}
{{define "content"}}
<h1>Bienvenido, {{.User.Name}}</h1>
{{end}}
Código Go:
func main() {
tmpl := template.Must(template.ParseGlob("templates/*.html"))
data := map[string]any{
"User": User{Name: "Carlos"},
}
tmpl.ExecuteTemplate(os.Stdout, "base", data)
}
3.2 Protección Automática contra XSS
Característica Clave: Todas las variables insertadas se escapan automáticamente.
Excepciones Controladas:
// Usar tipo template.HTML para contenido confiable
tmpl := template.Must(template.New("").Parse(`
<div>{{.SafeContent}}</div>
`))
tmpl.Execute(os.Stdout, map[string]any{
"SafeContent": template.HTML("<b>Texto seguro</b>"),
})
4. Mejores Prácticas y Seguridad
4.1 Reglas de Escape Contextual
- Atributos HTML: Escape automático de
"y espacios - URLs: Validación de esquemas (
javascript:bloqueado) - CSS/JS: Requiere tratamiento especial
4.2 Validación de Datos
func sanitizeInput(input string) string {
// Eliminar etiquetas HTML
return html.EscapeString(input)
}
// Uso en plantillas:
tmpl.Parse(`<p>{{. | sanitizeInput}}</p>`)
4.3 Manejo de Errores
tmpl, err := template.ParseFiles("plantilla.html")
if err != nil {
log.Fatalf("Error cargando plantilla: %v", err)
}
err = tmpl.Execute(os.Stdout, data)
if err != nil {
log.Printf("Error renderizando: %v", err)
}
5. Comparativa de Métodos
| Método | Uso Recomendado | Seguridad |
|---|---|---|
html.EscapeString |
Escape manual en código dinámico | Alta |
text/template |
Contenido no HTML | Baja |
html/template |
Cualquier salida HTML | Máxima |
6. Ejemplo Completo: Sistema de Comentarios
Estructura de Datos:
type Comment struct {
Author string
Content string
Date time.Time
}
Plantilla (comments.html):
{{define "comment"}}
<div class="comment">
<h3>{{.Author}}</h3>
<p class="content">{{.Content}}</p>
<small>{{.Date | formatDate}}</small>
</div>
{{end}}
Código:
func main() {
comments := []Comment{
{"Alice", "¡Gran publicación!", time.Now()},
{"Bob", "<script>alert('XSS')</script>", time.Now()},
}
tmpl := template.Must(template.New("").Funcs(template.FuncMap{
"formatDate": formatDate,
}).ParseFiles("comments.html"))
for _, c := range comments {
tmpl.ExecuteTemplate(os.Stdout, "comment", c)
// El contenido de Bob se escapa automáticamente
}
}
Esta guía proporciona un entendimiento profundo del paquete html en Go, incluyendo técnicas avanzadas de plantillas y prácticas esenciales de seguridad. Los ejemplos prácticos demuestran cómo implementar funcionalidades reales manteniendo la protección contra vulnerabilidades comunes.