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:
mutantmonkey 2015-10-04 14:58:00 -07:00
parent 70cff4431d
commit 5e7e96af01
4 changed files with 105 additions and 9 deletions

40
csp.go Normal file
View 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
View 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:

View File

@ -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)
} }

View File

@ -28,6 +28,9 @@ var Config struct {
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()