49 lines
1.6 KiB
Go
49 lines
1.6 KiB
Go
package auth
|
|
|
|
import (
|
|
"crypto/subtle"
|
|
"net/http"
|
|
)
|
|
|
|
// CSRFFieldName ist der Name des versteckten Formularfelds in den Templates.
|
|
const CSRFFieldName = "csrf_token"
|
|
|
|
// CSRFHeaderName erlaubt es, das Token bei einem fetch()-Aufruf im Header statt
|
|
// im Formular zu schicken.
|
|
const CSRFHeaderName = "X-CSRF-Token"
|
|
|
|
// CSRFToken berechnet das Token zu einer Sitzung.
|
|
//
|
|
// # Wogegen schützt das?
|
|
//
|
|
// CSRF ("Cross-Site Request Forgery") nutzt aus, dass der Browser Cookies
|
|
// automatisch mitschickt. Eine fremde Seite kann ein Formular auf unseren Server
|
|
// abschicken lassen, und für den Server sieht es aus wie eine Aktion des
|
|
// angemeldeten Nutzers:
|
|
//
|
|
// <form method="POST" action="https://munin.intern/projects/x/delete">
|
|
// <script>document.forms[0].submit()</script>
|
|
//
|
|
// Das Token unterbindet das: Die fremde Seite kennt es nicht und kann es wegen
|
|
// der Same-Origin-Policy des Browsers auch nicht auslesen.
|
|
//
|
|
// Das Präfix "csrf:" sorgt für Domänentrennung — dasselbe Geheimnis signiert auch
|
|
// Session-Cookies, und eine Signatur aus dem einen Zusammenhang darf im anderen
|
|
// nicht gelten.
|
|
func (s *Signer) CSRFToken(subject string) string {
|
|
return s.sign("csrf:" + subject)
|
|
}
|
|
|
|
// ValidCSRFToken prüft ein übermitteltes Token gegen die Sitzung.
|
|
func (s *Signer) ValidCSRFToken(token, subject string) bool {
|
|
expected := s.CSRFToken(subject)
|
|
return subtle.ConstantTimeCompare([]byte(token), []byte(expected)) == 1
|
|
}
|
|
|
|
// CSRFTokenFromRequest holt das Token aus dem Formular oder dem Header.
|
|
func CSRFTokenFromRequest(r *http.Request) string {
|
|
if token := r.Header.Get(CSRFHeaderName); token != "" {
|
|
return token
|
|
}
|
|
return r.FormValue(CSRFFieldName)
|
|
}
|