OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / github.com / kr / secureheader / secureheader.go
1 // Package secureheader adds some HTTP header fields widely
2 // considered to improve safety of HTTP requests. These fields
3 // are documented as follows:
4 //
5 //   Strict Transport Security: https://tools.ietf.org/html/rfc6797
6 //   Frame Options:             https://tools.ietf.org/html/draft-ietf-websec-x-frame-options-00
7 //   Cross Site Scripting:      http://msdn.microsoft.com/en-us/library/dd565647%28v=vs.85%29.aspx
8 //   Content Type Options:      http://msdn.microsoft.com/en-us/library/ie/gg622941%28v=vs.85%29.aspx
9 //
10 // The easiest way to use this package:
11 //
12 //   http.ListenAndServe(addr, secureheader.DefaultConfig)
13 //
14 // DefaultConfig is initialized with conservative (safer and more
15 // restrictive) behavior. If you want to change that, set its
16 // fields to different values before calling ListenAndServe. See
17 // the example code below.
18 //
19 // This package was inspired by Twitter's secureheaders Ruby
20 // library. See https://github.com/twitter/secureheaders.
21 package secureheader
22
23 // TODO(kr): figure out how to add this one:
24 //   Content Security Policy:   https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html
25 // See https://github.com/kr/secureheader/issues/1.
26
27 import (
28         "net"
29         "net/http"
30         "strconv"
31         "time"
32 )
33
34 // DefaultConfig is initialized with conservative (safer and more
35 // restrictive) behavior.
36 var DefaultConfig = &Config{
37         HTTPSRedirect:          true,
38         HTTPSUseForwardedProto: ShouldUseForwardedProto(),
39
40         PermitClearLoopback: false,
41
42         ContentTypeOptions: true,
43
44         HSTS:                  true,
45         HSTSMaxAge:            300 * 24 * time.Hour,
46         HSTSIncludeSubdomains: true,
47
48         FrameOptions:       true,
49         FrameOptionsPolicy: Deny,
50
51         XSSProtection:      true,
52         XSSProtectionBlock: false,
53 }
54
55 type Config struct {
56         // If true, redirects any request with scheme http to the
57         // equivalent https URL.
58         HTTPSRedirect          bool
59         HTTPSUseForwardedProto bool
60
61         // Allow cleartext (non-HTTPS) HTTP connections to a loopback
62         // address, even if HTTPSRedirect is true.
63         PermitClearLoopback bool
64
65         // If true, sets X-Content-Type-Options to "nosniff".
66         ContentTypeOptions bool
67
68         // If true, sets the HTTP Strict Transport Security header
69         // field, which instructs browsers to send future requests
70         // over HTTPS, even if the URL uses the unencrypted http
71         // scheme.
72         HSTS                  bool
73         HSTSMaxAge            time.Duration
74         HSTSIncludeSubdomains bool
75
76         // If true, sets X-Frame-Options, to control when the request
77         // should be displayed inside an HTML frame.
78         FrameOptions       bool
79         FrameOptionsPolicy FramePolicy
80
81         // If true, sets X-XSS-Protection to "1", optionally with
82         // "mode=block". See the official documentation, linked above,
83         // for the meaning of these values.
84         XSSProtection      bool
85         XSSProtectionBlock bool
86
87         // Used by ServeHTTP, after setting any extra headers, to
88         // reply to the request. Next is typically nil, in which case
89         // http.DefaultServeMux is used instead.
90         Next http.Handler
91 }
92
93 // ServeHTTP sets header fields on w according to the options in
94 // c, then either replies directly or runs c.Next to reply.
95 // Typically c.Next is nil, in which case http.DefaultServeMux is
96 // used instead.
97 func (c *Config) ServeHTTP(w http.ResponseWriter, r *http.Request) {
98         if c.HTTPSRedirect && !c.isHTTPS(r) && !c.okloopback(r) {
99                 url := *r.URL
100                 url.Scheme = "https"
101                 url.Host = r.Host
102                 http.Redirect(w, r, url.String(), http.StatusMovedPermanently)
103                 return
104         }
105         if c.ContentTypeOptions {
106                 w.Header().Set("X-Content-Type-Options", "nosniff")
107         }
108         if c.HSTS && c.isHTTPS(r) {
109                 v := "max-age=" + strconv.FormatInt(int64(c.HSTSMaxAge/time.Second), 10)
110                 if c.HSTSIncludeSubdomains {
111                         v += "; includeSubDomains"
112                 }
113                 w.Header().Set("Strict-Transport-Security", v)
114         }
115         if c.FrameOptions {
116                 w.Header().Set("X-Frame-Options", string(c.FrameOptionsPolicy))
117         }
118         if c.XSSProtection {
119                 v := "1"
120                 if c.XSSProtectionBlock {
121                         v += "; mode=block"
122                 }
123                 w.Header().Set("X-XSS-Protection", v)
124         }
125         next := c.Next
126         if next == nil {
127                 next = http.DefaultServeMux
128         }
129         next.ServeHTTP(w, r)
130 }
131
132 // Given that r is cleartext (not HTTPS), okloopback returns
133 // whether r is on a permitted loopback connection.
134 func (c *Config) okloopback(r *http.Request) bool {
135         return c.PermitClearLoopback && isLoopback(r)
136 }
137
138 func (c *Config) isHTTPS(r *http.Request) bool {
139         if c.HTTPSUseForwardedProto {
140                 return r.Header.Get("X-Forwarded-Proto") == "https"
141         }
142         return r.TLS != nil
143 }
144
145 // FramePolicy tells the browser under what circumstances to allow
146 // the response to be displayed inside an HTML frame. There are
147 // three options:
148 //
149 //   Deny            do not permit display in a frame
150 //   SameOrigin      permit display in a frame from the same origin
151 //   AllowFrom(url)  permit display in a frame from the given url
152 type FramePolicy string
153
154 const (
155         Deny       FramePolicy = "DENY"
156         SameOrigin FramePolicy = "SAMEORIGIN"
157 )
158
159 // AllowFrom returns a FramePolicy specifying that the requested
160 // resource should be included in a frame from only the given url.
161 func AllowFrom(url string) FramePolicy {
162         return FramePolicy("ALLOW-FROM: " + url)
163 }
164
165 // ShouldUseForwardedProto returns whether to trust the
166 // X-Forwarded-Proto header field.
167 // DefaultConfig.HTTPSUseForwardedProto is initialized to this
168 // value.
169 //
170 // This value depends on the particular environment where the
171 // package is built. It is currently true iff build constraint
172 // "heroku" is satisfied.
173 func ShouldUseForwardedProto() bool {
174         return defaultUseForwardedProto
175 }
176
177 func isLoopback(r *http.Request) bool {
178         a, err := net.ResolveTCPAddr("tcp", r.RemoteAddr)
179         return err == nil && a.IP.IsLoopback()
180 }