Cross-Site Request Forgery
By OWASP's definition "Cross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted actions on a web application in which they're currently authenticated.". (source)
CSRF attacks are not focused on data theft. Instead, they target state-changing requests. With a little social engineering (such as sharing a link via email or chat) the attacker may trick users to execute unwanted web-application actions such as changing account's recovery email.
Attack scenario
Let's say that foo.com
uses HTTP GET
requests to set the account's recovery
email as shown:
GET https://foo.com/account/recover?email=me@somehost.com
A simple attack scenario may look like:
- Victim is authenticated at https://foo.com
- Attacker sends a chat message to the Victim with the following link:
https://foo.com/account/recover?email=me@attacker.com
- Victim's account recovery email address is changed to
me@attacker.com
, giving the Attacker full control over it.
The Problem
Changing the HTTP verb from GET
to POST
(or any other) won't solve the
issue. Using secret cookies, URL rewriting, or HTTPS won't do it either.
The attack is possible because the server does not distinguish between requests made during a legit user session workflow (navigation), and "malicious" ones.
The Solution
In theory
As previously mentioned, CSRF targets state-changing requests. Concerning Web
Applications, most of the time that means POST
requests issued by form
submission.
In this scenario, when a user first requests the page which renders the form, the server computes a nonce (an arbitrary number intended to be used once). This token is then included into the form as a field (most of the time this field is hidden but it is not mandatory).
Next, when the form is submitted, the hidden field is sent along with other user input. The server should then validated whether the token is part the request data, and determine if it is valid.
The specific nonce/token should obey to the following requirements:
- Unique per user session
- Large random value
- Generated by a cryptographically-secure random number generator
Note: Although HTTP GET
requests are not expected to change state (said to
be idempotent), due to undesirable programming practices they can in fact modify
resources. Because of that, they could also be targeted by CSRF attacks.
Concerning APIs, PUT
and DELETE
are two other common targets of CSRF
attacks.
In practice
Doing all this by hand is not a good idea, since it is error prone.
Most Web Application Frameworks already offer a solution out-of-the-box and you're advised to enable it. If you're not using a Framework, the advice is to adopt one.
The following example is part of the Gorilla web toolkit for Go programming language. You can find gorilla/csrf on GitHub
package main
import (
"net/http"
"github.com/gorilla/csrf"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/signup", ShowSignupForm)
// All POST requests without a valid token will return HTTP 403 Forbidden.
r.HandleFunc("/signup/post", SubmitSignupForm)
// Add the middleware to your router by wrapping it.
http.ListenAndServe(":8000",
csrf.Protect([]byte("32-byte-long-auth-key"))(r))
// PS: Don't forget to pass csrf.Secure(false) if you're developing locally
// over plain HTTP (just don't leave it on in production).
}
func ShowSignupForm(w http.ResponseWriter, r *http.Request) {
// signup_form.tmpl just needs a {{ .csrfField }} template tag for
// csrf.TemplateField to inject the CSRF token into. Easy!
t.ExecuteTemplate(w, "signup_form.tmpl", map[string]interface{}{
csrf.TemplateTag: csrf.TemplateField(r),
})
// We could also retrieve the token directly from csrf.Token(r) and
// set it in the request header - w.Header.Set("X-CSRF-Token", token)
// This is useful if you're sending JSON to clients or a front-end JavaScript
// framework.
}
func SubmitSignupForm(w http.ResponseWriter, r *http.Request) {
// We can trust that requests making it this far have satisfied
// our CSRF protection requirements.
}
OWASP has a detailed Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet, which you're recommended to read.