4.4 Duplicate submissions
I don't know if you've ever seen some blogs or BBS' that have more than one post that are exactly the same, but I can tell you that it's because users submitted duplicate post forms. There are many things that can cause duplicate submissions; sometimes users just double click the submit button, or they want to modify some content after posting and press the back button. In some cases it is by the intentional actions of malicious users. It's easy to see how duplicate submissions can lead to many problems. Thus, we have to use effective means to prevent it.
The solution is to add a hidden field with a unique token to your form, and to always check this token before processing the incoming data. Also, if you are using Ajax to submit a form, use JavaScript to disable the submit button once the form has been submitted.
Let's improve the example from section 4.2:
<input type="checkbox" name="interest" value="football">Football
<input type="checkbox" name="interest" value="basketball">Basketball
<input type="checkbox" name="interest" value="tennis">Tennis
Username:<input type="text" name="username">
Password:<input type="password" name="password">
<input type="hidden" name="token" value="{{.}}">
<input type="submit" value="Login">
We use an MD5 hash (time stamp) to generate the token, and added it to both a hidden field on the client side form and a session cookie on the server side (Chapter 6). We can then use this token to check whether or not this form was submitted.
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 The content in browser after adding a token
You can refresh this page and you will see a different token every time. This ensures that every form is unique.
For now, you can prevent many duplicate submission attacks by adding tokens to your forms, but it cannot prevent all deceptive attacks of this type. There is much more work that needs to be done.
Links
- Directory
- Previous section: Cross site scripting
- Next section: File upload