No se si alguna vez has visto blogs que tienen uno o mas de un post que son exactamente iguales, pero puedo decirte que es porque un usuario envió dos veces el mismo formulario. Existen muchas cosas que pueden cambiar envíos duplicados; algunas veces los usuarios hacen doble click en el botón de enviar, o quieren modificar el contenido después de postear y usan el botón de atrás. En algunos casos es por una acción intencional de usuarios malicioso. Es facil ver como los envíos duplicados pueden generar muchos problemas. Afortunadamente tenemos herramientas para prevenirlos.
La solución es añadir un campo oculto con un único token al formulario, y siempre verificar si el token ha sido procesado con anterioridad. También si quieres usar ajax para enviar un formulario, puedes usar javascript para deshabilitar el botón una vez se ha presionado.
Vamos a mejorar el ejemplo de la sección 4.2:
<input type="checkbox" name="interest" value="football">Futbol
<input type="checkbox" name="interest" value="basketball">Basquetbol
<input type="checkbox" name="interest" value="tennis">Tenis
Nombre de usuario:<input type="text" name="username">
Contraseña:<input type="password" name="password">
<input type="hidden" name="token" value="{{.}}">
<input type="submit" value="Ingresar">
Nosotros usamos un hash MD5 con la hora actual para generar el token, y agregamos esto a un campo oculto del lado del cliente y a una cookie en el lado del servidor (Capítulo 6). Nosotros usamos este token para verificar si el formulario ha sido enviado.
func login(w http.ResponseWriter, r *http.Request) {
fmt.Println("method:", r.Method) // get request method
if r.Method == "GET" {
crutime := time.Now().Unix()
h := md5.New()
io.WriteString(h, strconv.FormatInt(crutime, 10))
token := fmt.Sprintf("%x", h.Sum(nil))
t, _ := template.ParseFiles("login.gtpl")
t.Execute(w, token)
} else {
// log in request
r.ParseForm()
token := r.Form.Get("token")
if token != "" {
// check token validity
} else {
// give error if no token
}
fmt.Println("username length:", len(r.Form["username"][0]))
fmt.Println("username:", template.HTMLEscapeString(r.Form.Get("username"))) // print in server side
fmt.Println("password:", template.HTMLEscapeString(r.Form.Get("password")))
template.HTMLEscape(w, []byte(r.Form.Get("username"))) // respond to client
}
}
Figure 4.4 El contenido del navegador después de agregar el Token
Puedes refrescar la página y verás un token diferente cada vez. Esto asegura que cada formulario es único.
Por ahora, puedes prevenir ataques de envíos duplicados añadiendo tokens a tus formularios, pero no puedes prevenir todos los ataques de este tipo, aún hay mucho trabajo por hacer.
- Índice
- Sección anterior: Cross site scripting
- Siguiente sección: Subida de archivos