parent
c3286fb5da
commit
f1f4e9dcb9
1
go.mod
1
go.mod
|
@ -8,6 +8,7 @@ require (
|
|||
github.com/friendsofgo/errors v0.9.2
|
||||
github.com/go-kit/kit v0.10.0
|
||||
github.com/gofrs/uuid v4.0.0+incompatible // indirect
|
||||
github.com/gorilla/csrf v1.7.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/gorilla/securecookie v1.1.1
|
||||
github.com/gorilla/sessions v1.2.1
|
||||
|
|
2
go.sum
2
go.sum
|
@ -153,6 +153,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
|
|||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/csrf v1.7.0 h1:mMPjV5/3Zd460xCavIkppUdvnl5fPXMpv2uz2Zyg7/Y=
|
||||
github.com/gorilla/csrf v1.7.0/go.mod h1:+a/4tCmqhG6/w4oafeAZ9pEa3/NZOWYVbD9fV0FwIQA=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"go.mindeco.de/http/render"
|
||||
refs "go.mindeco.de/ssb-refs"
|
||||
|
||||
"github.com/gorilla/csrf"
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/admindb"
|
||||
weberrors "github.com/ssb-ngi-pointer/go-ssb-room/web/errors"
|
||||
)
|
||||
|
@ -65,10 +66,11 @@ func (h allowListH) overview(rw http.ResponseWriter, req *http.Request) (interfa
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return struct {
|
||||
Entries admindb.ListEntries
|
||||
Count int
|
||||
}{lst, len(lst)}, nil
|
||||
return map[string]interface{}{
|
||||
csrf.TemplateTag: csrf.TemplateField(req),
|
||||
"Entries": lst,
|
||||
"Count": len(lst),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// TODO: move to render package so that we can decide to not render a page during the controller
|
||||
|
@ -89,10 +91,10 @@ func (h allowListH) removeConfirm(rw http.ResponseWriter, req *http.Request) (in
|
|||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return struct {
|
||||
Entry admindb.ListEntry
|
||||
}{entry}, nil
|
||||
return map[string]interface{}{
|
||||
"Entry": entry,
|
||||
csrf.TemplateTag: csrf.TemplateField(req),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h allowListH) remove(rw http.ResponseWriter, req *http.Request) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/go-kit/kit/log/level"
|
||||
"github.com/gorilla/csrf"
|
||||
"github.com/gorilla/mux"
|
||||
"go.mindeco.de/http/auth"
|
||||
"go.mindeco.de/http/render"
|
||||
|
@ -23,7 +24,12 @@ func Handler(m *mux.Router, r *render.Renderer, a *auth.Handler) http.Handler {
|
|||
m = router.Auth(nil)
|
||||
}
|
||||
|
||||
m.Get(router.AuthFallbackSignInForm).Handler(r.StaticHTML("/auth/fallback_sign_in.tmpl"))
|
||||
m.Get(router.AuthFallbackSignInForm).Handler(r.HTML("/auth/fallback_sign_in.tmpl", func(w http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
return map[string]interface{}{
|
||||
csrf.TemplateTag: csrf.TemplateField(req),
|
||||
}, nil
|
||||
}))
|
||||
|
||||
m.Get(router.AuthFallbackSignIn).HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
logger := logging.FromContext(req.Context())
|
||||
level.Info(logger).Log("event", "authorize request")
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/gorilla/csrf"
|
||||
"github.com/gorilla/sessions"
|
||||
"go.mindeco.de/http/auth"
|
||||
"go.mindeco.de/http/render"
|
||||
|
@ -157,6 +158,20 @@ func New(
|
|||
return nil, fmt.Errorf("web Handler: failed to init fallback auth system: %w", err)
|
||||
}
|
||||
|
||||
// Cross Site Request Forgery prevention middleware
|
||||
csrfKey, err := web.LoadOrCreateCSRFSecret(repo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
CSRF := csrf.Protect(csrfKey,
|
||||
csrf.ErrorHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
err := csrf.FailureReason(req)
|
||||
// TODO: localize error?
|
||||
r.Error(w, req, http.StatusForbidden, err)
|
||||
})),
|
||||
)
|
||||
|
||||
// this router is a bit of a qurik
|
||||
// TODO: explain problem between gorilla/mux named routers and authentication
|
||||
mainMux := &http.ServeMux{}
|
||||
|
@ -181,9 +196,10 @@ func New(
|
|||
|
||||
mainMux.Handle("/", m)
|
||||
|
||||
// apply middleware
|
||||
var finalHandler http.Handler = mainMux
|
||||
|
||||
finalHandler = logging.InjectHandler(logger)(finalHandler)
|
||||
finalHandler = CSRF(finalHandler)
|
||||
|
||||
if web.Production {
|
||||
return finalHandler, nil
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
|
||||
<form id="confirm" action="{{urlTo "admin:allow-list:remove"}}" method="POST">
|
||||
{{ .csrfField }}
|
||||
<input type="hidden" name="id" value={{.Entry.ID}}>
|
||||
<input type="submit" value="{{i18n "Confirm"}}">
|
||||
</form>
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
<h3>{{i18n "AdminAllowListAdd"}}</h3>
|
||||
<form id="add-entry" action="{{urlTo "admin:allow-list:add"}}" method="POST">
|
||||
{{ .csrfField }}
|
||||
<input type="text" name="pub_key">
|
||||
<input type="submit" value="{{i18n "AdminAllowListAdd"}}">
|
||||
</form>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<form method="POST" action={{urlTo "auth:fallback:signin"}}>
|
||||
{{ .csrfField }}
|
||||
<input type="text" name="user">
|
||||
<input type="password" name="pass">
|
||||
<input type="submit">
|
||||
|
|
35
web/utils.go
35
web/utils.go
|
@ -140,3 +140,38 @@ func LoadOrCreateCookieSecrets(repo repo.Interface) ([]securecookie.Codec, error
|
|||
sc := securecookie.CodecsFromPairs(pairs...)
|
||||
return sc, nil
|
||||
}
|
||||
|
||||
const csrfKeyLength = 32
|
||||
|
||||
// LoadOrCreateCSRFSecret either loads the bytes from $repo/web/csrf-secret or creates a new file with suitable keys in it
|
||||
func LoadOrCreateCSRFSecret(repo repo.Interface) ([]byte, error) {
|
||||
secretPath := repo.GetPath("web", "csrf-secret")
|
||||
err := os.MkdirAll(filepath.Dir(secretPath), 0700)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
return nil, fmt.Errorf("failed to create folder for csrf secret: %w", err)
|
||||
}
|
||||
|
||||
// load the existing data
|
||||
secret, err := ioutil.ReadFile(secretPath)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("failed to load csrf secrets: %w", err)
|
||||
}
|
||||
|
||||
// create a new key, save and return it
|
||||
freshKey := securecookie.GenerateRandomKey(csrfKeyLength)
|
||||
|
||||
err = ioutil.WriteFile(secretPath, freshKey, 0600)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return freshKey, nil
|
||||
}
|
||||
|
||||
if n := len(secret); csrfKeyLength != n {
|
||||
return nil, fmt.Errorf("expected %d bytes csrf secert but got %d", csrfKeyLength, n)
|
||||
}
|
||||
|
||||
return secret, nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue