--- /dev/null
+Copyright © 2013 Keith Rarick.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- /dev/null
+// Package secureheader adds some HTTP header fields widely
+// considered to improve safety of HTTP requests. These fields
+// are documented as follows:
+//
+// Strict Transport Security: https://tools.ietf.org/html/rfc6797
+// Frame Options: https://tools.ietf.org/html/draft-ietf-websec-x-frame-options-00
+// Cross Site Scripting: http://msdn.microsoft.com/en-us/library/dd565647%28v=vs.85%29.aspx
+// Content Type Options: http://msdn.microsoft.com/en-us/library/ie/gg622941%28v=vs.85%29.aspx
+//
+// The easiest way to use this package:
+//
+// http.ListenAndServe(addr, secureheader.DefaultConfig)
+//
+// DefaultConfig is initialized with conservative (safer and more
+// restrictive) behavior. If you want to change that, set its
+// fields to different values before calling ListenAndServe. See
+// the example code below.
+//
+// This package was inspired by Twitter's secureheaders Ruby
+// library. See https://github.com/twitter/secureheaders.
+package secureheader
+
+// TODO(kr): figure out how to add this one:
+// Content Security Policy: https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html
+// See https://github.com/kr/secureheader/issues/1.
+
+import (
+ "net"
+ "net/http"
+ "strconv"
+ "time"
+)
+
+// DefaultConfig is initialized with conservative (safer and more
+// restrictive) behavior.
+var DefaultConfig = &Config{
+ HTTPSRedirect: true,
+ HTTPSUseForwardedProto: ShouldUseForwardedProto(),
+
+ PermitClearLoopback: false,
+
+ ContentTypeOptions: true,
+
+ HSTS: true,
+ HSTSMaxAge: 300 * 24 * time.Hour,
+ HSTSIncludeSubdomains: true,
+
+ FrameOptions: true,
+ FrameOptionsPolicy: Deny,
+
+ XSSProtection: true,
+ XSSProtectionBlock: false,
+}
+
+type Config struct {
+ // If true, redirects any request with scheme http to the
+ // equivalent https URL.
+ HTTPSRedirect bool
+ HTTPSUseForwardedProto bool
+
+ // Allow cleartext (non-HTTPS) HTTP connections to a loopback
+ // address, even if HTTPSRedirect is true.
+ PermitClearLoopback bool
+
+ // If true, sets X-Content-Type-Options to "nosniff".
+ ContentTypeOptions bool
+
+ // If true, sets the HTTP Strict Transport Security header
+ // field, which instructs browsers to send future requests
+ // over HTTPS, even if the URL uses the unencrypted http
+ // scheme.
+ HSTS bool
+ HSTSMaxAge time.Duration
+ HSTSIncludeSubdomains bool
+
+ // If true, sets X-Frame-Options, to control when the request
+ // should be displayed inside an HTML frame.
+ FrameOptions bool
+ FrameOptionsPolicy FramePolicy
+
+ // If true, sets X-XSS-Protection to "1", optionally with
+ // "mode=block". See the official documentation, linked above,
+ // for the meaning of these values.
+ XSSProtection bool
+ XSSProtectionBlock bool
+
+ // Used by ServeHTTP, after setting any extra headers, to
+ // reply to the request. Next is typically nil, in which case
+ // http.DefaultServeMux is used instead.
+ Next http.Handler
+}
+
+// ServeHTTP sets header fields on w according to the options in
+// c, then either replies directly or runs c.Next to reply.
+// Typically c.Next is nil, in which case http.DefaultServeMux is
+// used instead.
+func (c *Config) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ if c.HTTPSRedirect && !c.isHTTPS(r) && !c.okloopback(r) {
+ url := *r.URL
+ url.Scheme = "https"
+ url.Host = r.Host
+ http.Redirect(w, r, url.String(), http.StatusMovedPermanently)
+ return
+ }
+ if c.ContentTypeOptions {
+ w.Header().Set("X-Content-Type-Options", "nosniff")
+ }
+ if c.HSTS && c.isHTTPS(r) {
+ v := "max-age=" + strconv.FormatInt(int64(c.HSTSMaxAge/time.Second), 10)
+ if c.HSTSIncludeSubdomains {
+ v += "; includeSubDomains"
+ }
+ w.Header().Set("Strict-Transport-Security", v)
+ }
+ if c.FrameOptions {
+ w.Header().Set("X-Frame-Options", string(c.FrameOptionsPolicy))
+ }
+ if c.XSSProtection {
+ v := "1"
+ if c.XSSProtectionBlock {
+ v += "; mode=block"
+ }
+ w.Header().Set("X-XSS-Protection", v)
+ }
+ next := c.Next
+ if next == nil {
+ next = http.DefaultServeMux
+ }
+ next.ServeHTTP(w, r)
+}
+
+// Given that r is cleartext (not HTTPS), okloopback returns
+// whether r is on a permitted loopback connection.
+func (c *Config) okloopback(r *http.Request) bool {
+ return c.PermitClearLoopback && isLoopback(r)
+}
+
+func (c *Config) isHTTPS(r *http.Request) bool {
+ if c.HTTPSUseForwardedProto {
+ return r.Header.Get("X-Forwarded-Proto") == "https"
+ }
+ return r.TLS != nil
+}
+
+// FramePolicy tells the browser under what circumstances to allow
+// the response to be displayed inside an HTML frame. There are
+// three options:
+//
+// Deny do not permit display in a frame
+// SameOrigin permit display in a frame from the same origin
+// AllowFrom(url) permit display in a frame from the given url
+type FramePolicy string
+
+const (
+ Deny FramePolicy = "DENY"
+ SameOrigin FramePolicy = "SAMEORIGIN"
+)
+
+// AllowFrom returns a FramePolicy specifying that the requested
+// resource should be included in a frame from only the given url.
+func AllowFrom(url string) FramePolicy {
+ return FramePolicy("ALLOW-FROM: " + url)
+}
+
+// ShouldUseForwardedProto returns whether to trust the
+// X-Forwarded-Proto header field.
+// DefaultConfig.HTTPSUseForwardedProto is initialized to this
+// value.
+//
+// This value depends on the particular environment where the
+// package is built. It is currently true iff build constraint
+// "heroku" is satisfied.
+func ShouldUseForwardedProto() bool {
+ return defaultUseForwardedProto
+}
+
+func isLoopback(r *http.Request) bool {
+ a, err := net.ResolveTCPAddr("tcp", r.RemoteAddr)
+ return err == nil && a.IP.IsLoopback()
+}