add support for some security headers
This commit adds support for Content-Security-Policy and X-Frame-Options using the ContentSecurityPolicy middleware.
This commit is contained in:
parent
70cff4431d
commit
5e7e96af01
40
csp.go
Normal file
40
csp.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
cspHeader = "Content-Security-Policy"
|
||||||
|
frameOptionsHeader = "X-Frame-Options"
|
||||||
|
)
|
||||||
|
|
||||||
|
type csp struct {
|
||||||
|
h http.Handler
|
||||||
|
opts CSPOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
type CSPOptions struct {
|
||||||
|
policy string
|
||||||
|
frame string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c csp) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// only add a CSP if one is not already set
|
||||||
|
if existing := w.Header().Get(cspHeader); existing == "" {
|
||||||
|
w.Header().Add(cspHeader, c.opts.policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set(frameOptionsHeader, c.opts.frame)
|
||||||
|
|
||||||
|
c.h.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContentSecurityPolicy(o CSPOptions) func(http.Handler) http.Handler {
|
||||||
|
fn := func(h http.Handler) http.Handler {
|
||||||
|
return csp{h, o}
|
||||||
|
}
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
|
||||||
|
// vim:set ts=8 sw=8 noet:
|
38
csp_test.go
Normal file
38
csp_test.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/zenazn/goji"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testCSPHeaders = map[string]string{
|
||||||
|
"Content-Security-Policy": "default-src 'none'; style-src 'self';",
|
||||||
|
"X-Frame-Options": "SAMEORIGIN",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContentSecurityPolicy(t *testing.T) {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", "/", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
goji.Use(ContentSecurityPolicy(CSPOptions{
|
||||||
|
policy: testCSPHeaders["Content-Security-Policy"],
|
||||||
|
frame: testCSPHeaders["X-Frame-Options"],
|
||||||
|
}))
|
||||||
|
|
||||||
|
goji.DefaultMux.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
for k, v := range testCSPHeaders {
|
||||||
|
if w.HeaderMap[k][0] != v {
|
||||||
|
t.Fatalf("%s header did not match expected value set by middleware", k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// vim:set ts=8 sw=8 noet:
|
@ -26,6 +26,8 @@ func fileServeHandler(c web.C, w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Security-Policy", Config.fileContentSecurityPolicy)
|
||||||
|
|
||||||
http.ServeFile(w, r, filePath)
|
http.ServeFile(w, r, filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
34
server.go
34
server.go
@ -19,15 +19,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var Config struct {
|
var Config struct {
|
||||||
bind string
|
bind string
|
||||||
filesDir string
|
filesDir string
|
||||||
metaDir string
|
metaDir string
|
||||||
noLogs bool
|
noLogs bool
|
||||||
allowHotlink bool
|
allowHotlink bool
|
||||||
siteName string
|
siteName string
|
||||||
siteURL string
|
siteURL string
|
||||||
fastcgi bool
|
fastcgi bool
|
||||||
remoteUploads bool
|
remoteUploads bool
|
||||||
|
contentSecurityPolicy string
|
||||||
|
fileContentSecurityPolicy string
|
||||||
|
xFrameOptions string
|
||||||
}
|
}
|
||||||
|
|
||||||
var Templates = make(map[string]*pongo2.Template)
|
var Templates = make(map[string]*pongo2.Template)
|
||||||
@ -37,6 +40,11 @@ var timeStarted time.Time
|
|||||||
var timeStartedStr string
|
var timeStartedStr string
|
||||||
|
|
||||||
func setup() {
|
func setup() {
|
||||||
|
goji.Use(ContentSecurityPolicy(CSPOptions{
|
||||||
|
policy: Config.contentSecurityPolicy,
|
||||||
|
frame: Config.xFrameOptions,
|
||||||
|
}))
|
||||||
|
|
||||||
if Config.noLogs {
|
if Config.noLogs {
|
||||||
goji.Abandon(middleware.Logger)
|
goji.Abandon(middleware.Logger)
|
||||||
}
|
}
|
||||||
@ -126,6 +134,14 @@ func main() {
|
|||||||
"serve through fastcgi")
|
"serve through fastcgi")
|
||||||
flag.BoolVar(&Config.remoteUploads, "remoteuploads", false,
|
flag.BoolVar(&Config.remoteUploads, "remoteuploads", false,
|
||||||
"enable remote uploads")
|
"enable remote uploads")
|
||||||
|
flag.StringVar(&Config.contentSecurityPolicy, "contentSecurityPolicy",
|
||||||
|
"default-src 'self'; img-src 'self' data:; referrer none;",
|
||||||
|
"value of default Content-Security-Policy header")
|
||||||
|
flag.StringVar(&Config.fileContentSecurityPolicy, "fileContentSecurityPolicy",
|
||||||
|
"default-src 'none'; img-src 'self'; object-src 'self'; media-src 'self'; sandbox; referrer none;",
|
||||||
|
"value of Content-Security-Policy header for file access")
|
||||||
|
flag.StringVar(&Config.xFrameOptions, "xFrameOptions", "SAMEORIGIN",
|
||||||
|
"value of X-Frame-Options header")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
setup()
|
setup()
|
||||||
|
Loading…
Reference in New Issue
Block a user