Replace CSRF token checking with Referer checking
This commit is contained in:
parent
159638084e
commit
08811a6da7
@ -164,8 +164,6 @@ func (a *api) Shutdown(ctx context.Context) error {
|
||||
func (a *api) apiHandler() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.Handle("/csrf", a.getCSRFTokenHandler())
|
||||
|
||||
mux.Handle("/pow/challenge", a.newPowChallengeHandler())
|
||||
mux.Handle("/pow/check",
|
||||
a.requirePowMiddleware(
|
||||
@ -250,11 +248,10 @@ func (a *api) handler() http.Handler {
|
||||
h := apiutil.MethodMux(map[string]http.Handler{
|
||||
"GET": applyMiddlewares(
|
||||
mux,
|
||||
setCSRFMiddleware,
|
||||
),
|
||||
"*": applyMiddlewares(
|
||||
mux,
|
||||
checkCSRFMiddleware,
|
||||
a.checkCSRFMiddleware,
|
||||
addResponseHeadersMiddleware(map[string]string{
|
||||
"Cache-Control": "no-store, max-age=0",
|
||||
"Pragma": "no-cache",
|
||||
|
@ -3,76 +3,26 @@ package http
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/mediocregopher/blog.mediocregopher.com/srv/http/apiutil"
|
||||
)
|
||||
|
||||
const (
|
||||
csrfTokenCookieName = "csrf_token"
|
||||
csrfTokenHeaderName = "X-CSRF-Token"
|
||||
csrfTokenFormName = "csrfToken"
|
||||
)
|
||||
|
||||
func setCSRFMiddleware(h http.Handler) http.Handler {
|
||||
func (a *api) checkCSRFMiddleware(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
csrfTok, err := apiutil.GetCookie(r, csrfTokenCookieName, "")
|
||||
|
||||
refererURL, err := url.Parse(r.Referer())
|
||||
if err != nil {
|
||||
apiutil.InternalServerError(rw, r, err)
|
||||
return
|
||||
|
||||
} else if csrfTok == "" {
|
||||
http.SetCookie(rw, &http.Cookie{
|
||||
Name: csrfTokenCookieName,
|
||||
Value: apiutil.RandStr(32),
|
||||
Secure: true,
|
||||
})
|
||||
}
|
||||
|
||||
h.ServeHTTP(rw, r)
|
||||
})
|
||||
}
|
||||
|
||||
func checkCSRFMiddleware(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
csrfTok, err := apiutil.GetCookie(r, csrfTokenCookieName, "")
|
||||
|
||||
if err != nil {
|
||||
apiutil.InternalServerError(rw, r, err)
|
||||
apiutil.BadRequest(rw, r, errors.New("invalid Referer"))
|
||||
return
|
||||
}
|
||||
|
||||
givenCSRFTok := r.Header.Get(csrfTokenHeaderName)
|
||||
if givenCSRFTok == "" {
|
||||
givenCSRFTok = r.FormValue(csrfTokenFormName)
|
||||
}
|
||||
|
||||
if csrfTok == "" || givenCSRFTok != csrfTok {
|
||||
apiutil.BadRequest(rw, r, errors.New("invalid CSRF token"))
|
||||
if refererURL.Scheme != a.params.PublicURL.Scheme ||
|
||||
refererURL.Host != a.params.PublicURL.Host {
|
||||
apiutil.BadRequest(rw, r, errors.New("invalid Referer"))
|
||||
return
|
||||
}
|
||||
|
||||
h.ServeHTTP(rw, r)
|
||||
})
|
||||
}
|
||||
|
||||
func (a *api) getCSRFTokenHandler() http.Handler {
|
||||
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
csrfTok, err := apiutil.GetCookie(r, csrfTokenCookieName, "")
|
||||
|
||||
if err != nil {
|
||||
apiutil.InternalServerError(rw, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
apiutil.JSONResult(rw, r, struct {
|
||||
CSRFToken string
|
||||
}{
|
||||
CSRFToken: csrfTok,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@ -100,21 +99,12 @@ func (a *api) mustParseTpl(name string) *template.Template {
|
||||
|
||||
func (a *api) mustParseBasedTpl(name string) *template.Template {
|
||||
tpl := a.mustParseTpl(name)
|
||||
tpl = template.Must(tpl.New("load-csrf.html").Parse(mustReadTplFile("load-csrf.html")))
|
||||
tpl = template.Must(tpl.New("base.html").Parse(mustReadTplFile("base.html")))
|
||||
return tpl
|
||||
}
|
||||
|
||||
type tplData struct {
|
||||
Payload interface{}
|
||||
CSRFToken string
|
||||
}
|
||||
|
||||
func (t tplData) CSRFFormInput() template.HTML {
|
||||
return template.HTML(fmt.Sprintf(
|
||||
`<input type="hidden" name="%s" class="csrfHiddenInput" />`,
|
||||
csrfTokenFormName,
|
||||
))
|
||||
Payload interface{}
|
||||
}
|
||||
|
||||
// executeTemplate expects to be the final action in an http.Handler
|
||||
@ -123,11 +113,8 @@ func executeTemplate(
|
||||
tpl *template.Template, payload interface{},
|
||||
) {
|
||||
|
||||
csrfToken, _ := apiutil.GetCookie(r, csrfTokenCookieName, "")
|
||||
|
||||
tplData := tplData{
|
||||
Payload: payload,
|
||||
CSRFToken: csrfToken,
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
if err := tpl.Execute(rw, tplData); err != nil {
|
||||
@ -141,7 +128,6 @@ func executeTemplate(
|
||||
func (a *api) executeRedirectTpl(
|
||||
rw http.ResponseWriter, r *http.Request, url string,
|
||||
) {
|
||||
log.Printf("here url:%q", url)
|
||||
executeTemplate(rw, r, a.redirectTpl, struct {
|
||||
URL string
|
||||
}{
|
||||
|
@ -1,7 +1,5 @@
|
||||
{{ define "body" }}
|
||||
|
||||
{{ $csrfFormInput := .CSRFFormInput }}
|
||||
|
||||
<h2>Upload Asset</h2>
|
||||
|
||||
<p>
|
||||
@ -10,7 +8,6 @@
|
||||
</p>
|
||||
|
||||
<form action="{{ BlogURL "assets/" }}" method="POST" enctype="multipart/form-data">
|
||||
{{ $csrfFormInput }}
|
||||
<div class="row">
|
||||
<div class="four columns">
|
||||
<input type="text" placeholder="Unique ID" name="id" />
|
||||
@ -37,7 +34,6 @@
|
||||
method="POST"
|
||||
style="margin-bottom: 0;"
|
||||
>
|
||||
{{ $csrfFormInput }}
|
||||
<input type="submit" value="Delete" />
|
||||
</form>
|
||||
</td>
|
||||
@ -46,8 +42,6 @@
|
||||
|
||||
</table>
|
||||
|
||||
{{ template "load-csrf.html" . }}
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ template "base.html" . }}
|
||||
|
@ -2,8 +2,6 @@
|
||||
|
||||
<form method="POST" action="{{ BlogURL "posts/" }}">
|
||||
|
||||
{{ .CSRFFormInput }}
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="columns six">
|
||||
@ -99,8 +97,6 @@
|
||||
|
||||
</form>
|
||||
|
||||
{{ template "load-csrf.html" . }}
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ template "base.html" . }}
|
||||
|
@ -1,13 +0,0 @@
|
||||
<script async type="module" src="{{ StaticURL "api.js" }}"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
(async () => {
|
||||
const api = await import("{{ StaticURL "api.js" }}");
|
||||
const res = await api.call("/api/csrf");
|
||||
|
||||
const els = document.getElementsByClassName("csrfHiddenInput");
|
||||
for (let i = 0; i < els.length; i++) {
|
||||
els[i].value = res.CSRFToken;
|
||||
}
|
||||
})();
|
||||
</script>
|
@ -18,8 +18,6 @@
|
||||
|
||||
{{ define "body" }}
|
||||
|
||||
{{ $csrfFormInput := .CSRFFormInput }}
|
||||
|
||||
<p style="text-align: center;">
|
||||
<a href="{{ BlogURL "posts/" }}?edit">
|
||||
<button>New Post</button>
|
||||
@ -44,7 +42,6 @@
|
||||
action="{{ PostURL .ID }}?method=delete"
|
||||
method="POST"
|
||||
>
|
||||
{{ $csrfFormInput }}
|
||||
<input type="submit" value="Delete" />
|
||||
</form>
|
||||
</td>
|
||||
@ -55,8 +52,6 @@
|
||||
|
||||
{{ template "posts-nextprev" . }}
|
||||
|
||||
{{ template "load-csrf.html" . }}
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ template "base.html" . }}
|
||||
|
Loading…
Reference in New Issue
Block a user