README.md
Rendering markdown...
package main
import (
"crypto/rand"
"encoding/base64"
"html/template"
"io"
"net/http"
)
// start: vendored from nosurf
const tokenLength = 32
func generateToken() []byte {
bytes := make([]byte, tokenLength)
if _, err := io.ReadFull(rand.Reader, bytes); err != nil {
panic(err)
}
return bytes
}
func b64encode(data []byte) string {
return base64.StdEncoding.EncodeToString(data)
}
func b64decode(data string) []byte {
decoded, err := base64.StdEncoding.DecodeString(data)
if err != nil {
return nil
}
return decoded
}
// Masks/unmasks the given data *in place*
// with the given key
// Slices must be of the same length, or oneTimePad will panic
func oneTimePad(data, key []byte) {
n := len(data)
if n != len(key) {
panic("Lengths of slices are not equal")
}
for i := 0; i < n; i++ {
data[i] ^= key[i]
}
}
func maskToken(data []byte) []byte {
if len(data) != tokenLength {
return nil
}
// tokenLength*2 == len(enckey + token)
result := make([]byte, 2*tokenLength)
// the first half of the result is the OTP
// the second half is the masked token itself
key := result[:tokenLength]
token := result[tokenLength:]
copy(token, data)
// generate the random token
if _, err := io.ReadFull(rand.Reader, key); err != nil {
panic(err)
}
oneTimePad(token, key)
return result
}
// end: vendored from nosurf
var indexHtml = template.Must(template.New("index").Parse(`
<!doctype html>
<body>
<form action="https://target.localhost/mutate" method="post">
<input type="submit" value="Submit">
<input type="hidden" name="csrf_token" value="{{ .Token }}">
</form>
</body>
`))
func main() {
m := http.NewServeMux()
m.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
unmaskedToken := generateToken()
maskedToken := maskToken(unmaskedToken)
http.SetCookie(w, &http.Cookie{
Name: "csrf_token",
Value: b64encode(unmaskedToken),
Domain: "target.localhost",
Path: "/mutate",
MaxAge: 3600,
})
err := indexHtml.Execute(w, struct {
Token string
}{
b64encode(maskedToken),
})
if err != nil {
panic(err)
}
})
m.HandleFunc("POST /mutate", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Mutating request successful."))
})
if err := http.ListenAndServe(":5001", m); err != nil {
panic(err)
}
}