tsweb.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. // Package tsweb contains code used in various Tailscale webservers.
  4. package tsweb
  5. import (
  6. "bufio"
  7. "bytes"
  8. "cmp"
  9. "context"
  10. "errors"
  11. "expvar"
  12. "fmt"
  13. "net"
  14. "net/http"
  15. _ "net/http/pprof"
  16. "net/netip"
  17. "net/url"
  18. "os"
  19. "path/filepath"
  20. "regexp"
  21. "strconv"
  22. "strings"
  23. "sync"
  24. "time"
  25. "go4.org/mem"
  26. "tailscale.com/envknob"
  27. "tailscale.com/metrics"
  28. "tailscale.com/net/tsaddr"
  29. "tailscale.com/tsweb/varz"
  30. "tailscale.com/types/logger"
  31. "tailscale.com/util/vizerror"
  32. )
  33. // DevMode controls whether extra output in shown, for when the binary is being run in dev mode.
  34. var DevMode bool
  35. func DefaultCertDir(leafDir string) string {
  36. cacheDir, err := os.UserCacheDir()
  37. if err == nil {
  38. return filepath.Join(cacheDir, "tailscale", leafDir)
  39. }
  40. return ""
  41. }
  42. // IsProd443 reports whether addr is a Go listen address for port 443.
  43. func IsProd443(addr string) bool {
  44. _, port, _ := net.SplitHostPort(addr)
  45. return port == "443" || port == "https"
  46. }
  47. // AllowDebugAccess reports whether r should be permitted to access
  48. // various debug endpoints.
  49. func AllowDebugAccess(r *http.Request) bool {
  50. if allowDebugAccessWithKey(r) {
  51. return true
  52. }
  53. if r.Header.Get("X-Forwarded-For") != "" {
  54. // TODO if/when needed. For now, conservative:
  55. return false
  56. }
  57. ipStr, _, err := net.SplitHostPort(r.RemoteAddr)
  58. if err != nil {
  59. return false
  60. }
  61. ip, err := netip.ParseAddr(ipStr)
  62. if err != nil {
  63. return false
  64. }
  65. if tsaddr.IsTailscaleIP(ip) || ip.IsLoopback() || ipStr == envknob.String("TS_ALLOW_DEBUG_IP") {
  66. return true
  67. }
  68. return false
  69. }
  70. func allowDebugAccessWithKey(r *http.Request) bool {
  71. if r.Method != "GET" {
  72. return false
  73. }
  74. urlKey := r.FormValue("debugkey")
  75. keyPath := envknob.String("TS_DEBUG_KEY_PATH")
  76. if urlKey != "" && keyPath != "" {
  77. slurp, err := os.ReadFile(keyPath)
  78. if err == nil && string(bytes.TrimSpace(slurp)) == urlKey {
  79. return true
  80. }
  81. }
  82. return false
  83. }
  84. // AcceptsEncoding reports whether r accepts the named encoding
  85. // ("gzip", "br", etc).
  86. func AcceptsEncoding(r *http.Request, enc string) bool {
  87. h := r.Header.Get("Accept-Encoding")
  88. if h == "" {
  89. return false
  90. }
  91. if !strings.Contains(h, enc) && !mem.ContainsFold(mem.S(h), mem.S(enc)) {
  92. return false
  93. }
  94. remain := h
  95. for len(remain) > 0 {
  96. var part string
  97. part, remain, _ = strings.Cut(remain, ",")
  98. part = strings.TrimSpace(part)
  99. part, _, _ = strings.Cut(part, ";")
  100. if part == enc {
  101. return true
  102. }
  103. }
  104. return false
  105. }
  106. // Protected wraps a provided debug handler, h, returning a Handler
  107. // that enforces AllowDebugAccess and returns forbidden replies for
  108. // unauthorized requests.
  109. func Protected(h http.Handler) http.Handler {
  110. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  111. if !AllowDebugAccess(r) {
  112. msg := "debug access denied"
  113. if DevMode {
  114. ipStr, _, _ := net.SplitHostPort(r.RemoteAddr)
  115. msg += fmt.Sprintf("; to permit access, set TS_ALLOW_DEBUG_IP=%v", ipStr)
  116. }
  117. http.Error(w, msg, http.StatusForbidden)
  118. return
  119. }
  120. h.ServeHTTP(w, r)
  121. })
  122. }
  123. // Port80Handler is the handler to be given to
  124. // autocert.Manager.HTTPHandler. The inner handler is the mux
  125. // returned by NewMux containing registered /debug handlers.
  126. type Port80Handler struct {
  127. Main http.Handler
  128. // FQDN is used to redirect incoming requests to https://<FQDN>.
  129. // If it is not set, the hostname is calculated from the incoming
  130. // request.
  131. FQDN string
  132. }
  133. func (h Port80Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  134. path := r.RequestURI
  135. if path == "/debug" || strings.HasPrefix(path, "/debug") {
  136. h.Main.ServeHTTP(w, r)
  137. return
  138. }
  139. if r.Method != "GET" && r.Method != "HEAD" {
  140. http.Error(w, "Use HTTPS", http.StatusBadRequest)
  141. return
  142. }
  143. if path == "/" && AllowDebugAccess(r) {
  144. // Redirect authorized user to the debug handler.
  145. path = "/debug/"
  146. }
  147. host := cmp.Or(h.FQDN, r.Host)
  148. target := "https://" + host + path
  149. http.Redirect(w, r, target, http.StatusFound)
  150. }
  151. // ReturnHandler is like net/http.Handler, but the handler can return an
  152. // error instead of writing to its ResponseWriter.
  153. type ReturnHandler interface {
  154. // ServeHTTPReturn is like http.Handler.ServeHTTP, except that
  155. // it can choose to return an error instead of writing to its
  156. // http.ResponseWriter.
  157. //
  158. // If ServeHTTPReturn returns an error, it caller should handle
  159. // an error by serving an HTTP 500 response to the user. The
  160. // error details should not be sent to the client, as they may
  161. // contain sensitive information. If the error is an
  162. // HTTPError, though, callers should use the HTTP response
  163. // code and message as the response to the client.
  164. ServeHTTPReturn(http.ResponseWriter, *http.Request) error
  165. }
  166. // BucketedStatsOptions describes tsweb handler options surrounding
  167. // the generation of metrics, grouped into buckets.
  168. type BucketedStatsOptions struct {
  169. // Bucket returns which bucket the given request is in.
  170. // If nil, [NormalizedPath] is used to compute the bucket.
  171. Bucket func(req *http.Request) string
  172. // If non-nil, Started maintains a counter of all requests which
  173. // have begun processing.
  174. Started *metrics.LabelMap
  175. // If non-nil, Finished maintains a counter of all requests which
  176. // have finished processing with success (that is, the HTTP handler has
  177. // returned).
  178. Finished *metrics.LabelMap
  179. }
  180. // normalizePathRegex matches components in a HTTP request path
  181. // that should be replaced.
  182. //
  183. // See: https://regex101.com/r/WIfpaR/3 for the explainer and test cases.
  184. var normalizePathRegex = regexp.MustCompile("([a-fA-F0-9]{9,}|([^\\/])+\\.([^\\/]){2,}|((n|k|u|L|t|S)[a-zA-Z0-9]{5,}(CNTRL|Djz1H|LV5CY|mxgaY|jNy1b))|(([^\\/])+\\@passkey))")
  185. // NormalizedPath returns the given path with the following modifications:
  186. //
  187. // - any query parameters are removed
  188. // - any path component with a hex string of 9 or more characters is
  189. // replaced by an ellipsis
  190. // - any path component containing a period with at least two characters
  191. // after the period (i.e. an email or domain)
  192. // - any path component consisting of a common Tailscale Stable ID
  193. // - any path segment *@passkey.
  194. func NormalizedPath(p string) string {
  195. // Fastpath: No hex sequences in there we might have to trim.
  196. // Avoids allocating.
  197. if normalizePathRegex.FindStringIndex(p) == nil {
  198. b, _, _ := strings.Cut(p, "?")
  199. return b
  200. }
  201. // If we got here, there's at least one hex sequences we need to
  202. // replace with an ellipsis.
  203. replaced := normalizePathRegex.ReplaceAllString(p, "…")
  204. b, _, _ := strings.Cut(replaced, "?")
  205. return b
  206. }
  207. func (o *BucketedStatsOptions) bucketForRequest(r *http.Request) string {
  208. if o.Bucket != nil {
  209. return o.Bucket(r)
  210. }
  211. return NormalizedPath(r.URL.Path)
  212. }
  213. type HandlerOptions struct {
  214. QuietLoggingIfSuccessful bool // if set, do not log successfully handled HTTP requests (200 and 304 status codes)
  215. Logf logger.Logf
  216. Now func() time.Time // if nil, defaults to time.Now
  217. // If non-nil, StatusCodeCounters maintains counters
  218. // of status codes for handled responses.
  219. // The keys are "1xx", "2xx", "3xx", "4xx", and "5xx".
  220. StatusCodeCounters *expvar.Map
  221. // If non-nil, StatusCodeCountersFull maintains counters of status
  222. // codes for handled responses.
  223. // The keys are HTTP numeric response codes e.g. 200, 404, ...
  224. StatusCodeCountersFull *expvar.Map
  225. // If non-nil, BucketedStats computes and exposes statistics
  226. // for each bucket based on the contained parameters.
  227. BucketedStats *BucketedStatsOptions
  228. // OnStart is called inline before ServeHTTP is called. Optional.
  229. OnStart OnStartFunc
  230. // OnError is called if the handler returned a HTTPError. This
  231. // is intended to be used to present pretty error pages if
  232. // the user agent is determined to be a browser.
  233. OnError ErrorHandlerFunc
  234. // OnCompletion is called inline when ServeHTTP is finished and gets
  235. // useful data that the implementor can use for metrics. Optional.
  236. OnCompletion OnCompletionFunc
  237. }
  238. // ErrorHandlerFunc is called to present a error response.
  239. type ErrorHandlerFunc func(http.ResponseWriter, *http.Request, HTTPError)
  240. // OnStartFunc is called before ServeHTTP is called.
  241. type OnStartFunc func(*http.Request, AccessLogRecord)
  242. // OnCompletionFunc is called when ServeHTTP is finished and gets
  243. // useful data that the implementor can use for metrics.
  244. type OnCompletionFunc func(*http.Request, AccessLogRecord)
  245. // ReturnHandlerFunc is an adapter to allow the use of ordinary
  246. // functions as ReturnHandlers. If f is a function with the
  247. // appropriate signature, ReturnHandlerFunc(f) is a ReturnHandler that
  248. // calls f.
  249. type ReturnHandlerFunc func(http.ResponseWriter, *http.Request) error
  250. // A Middleware is a function that wraps an http.Handler to extend or modify
  251. // its behaviour.
  252. //
  253. // The implementation of the wrapper is responsible for delegating its input
  254. // request to the underlying handler, if appropriate.
  255. type Middleware func(h http.Handler) http.Handler
  256. // ServeHTTPReturn calls f(w, r).
  257. func (f ReturnHandlerFunc) ServeHTTPReturn(w http.ResponseWriter, r *http.Request) error {
  258. return f(w, r)
  259. }
  260. // StdHandler converts a ReturnHandler into a standard http.Handler.
  261. // Handled requests are logged using opts.Logf, as are any errors.
  262. // Errors are handled as specified by the Handler interface.
  263. func StdHandler(h ReturnHandler, opts HandlerOptions) http.Handler {
  264. if opts.Now == nil {
  265. opts.Now = time.Now
  266. }
  267. if opts.Logf == nil {
  268. opts.Logf = logger.Discard
  269. }
  270. return retHandler{h, opts}
  271. }
  272. // retHandler is an http.Handler that wraps a Handler and handles errors.
  273. type retHandler struct {
  274. rh ReturnHandler
  275. opts HandlerOptions
  276. }
  277. // ServeHTTP implements the http.Handler interface.
  278. func (h retHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  279. msg := AccessLogRecord{
  280. Time: h.opts.Now(),
  281. RemoteAddr: r.RemoteAddr,
  282. Proto: r.Proto,
  283. TLS: r.TLS != nil,
  284. Host: r.Host,
  285. Method: r.Method,
  286. RequestURI: r.URL.RequestURI(),
  287. UserAgent: r.UserAgent(),
  288. Referer: r.Referer(),
  289. RequestID: RequestIDFromContext(r.Context()),
  290. }
  291. var bucket string
  292. var startRecorded bool
  293. if bs := h.opts.BucketedStats; bs != nil {
  294. bucket = bs.bucketForRequest(r)
  295. if bs.Started != nil {
  296. switch v := bs.Started.Map.Get(bucket).(type) {
  297. case *expvar.Int:
  298. // If we've already seen this bucket for, count it immediately.
  299. // Otherwise, for newly seen paths, only count retroactively
  300. // (so started-finished doesn't go negative) so we don't fill
  301. // this LabelMap up with internet scanning spam.
  302. v.Add(1)
  303. startRecorded = true
  304. }
  305. }
  306. }
  307. if fn := h.opts.OnStart; fn != nil {
  308. fn(r, msg)
  309. }
  310. lw := &loggingResponseWriter{ResponseWriter: w, logf: h.opts.Logf}
  311. // In case the handler panics, we want to recover and continue logging the
  312. // error before raising the panic again for the server to handle.
  313. var (
  314. didPanic bool
  315. panicRes any
  316. )
  317. defer func() {
  318. if didPanic {
  319. panic(panicRes)
  320. }
  321. }()
  322. runWithPanicProtection := func() (err error) {
  323. defer func() {
  324. if r := recover(); r != nil {
  325. didPanic = true
  326. panicRes = r
  327. // Even if r is an error, do not wrap it as an error here as
  328. // that would allow things like panic(vizerror.New("foo")) which
  329. // is really hard to define the behavior of.
  330. err = fmt.Errorf("panic: %v", r)
  331. }
  332. }()
  333. return h.rh.ServeHTTPReturn(lw, r)
  334. }
  335. err := runWithPanicProtection()
  336. var hErr HTTPError
  337. var hErrOK bool
  338. if errors.As(err, &hErr) {
  339. hErrOK = true
  340. } else if vizErr, ok := vizerror.As(err); ok {
  341. hErrOK = true
  342. hErr = HTTPError{Msg: vizErr.Error()}
  343. }
  344. if lw.code == 0 && err == nil && !lw.hijacked {
  345. // If the handler didn't write and didn't send a header, that still means 200.
  346. // (See https://play.golang.org/p/4P7nx_Tap7p)
  347. lw.code = 200
  348. }
  349. msg.Seconds = h.opts.Now().Sub(msg.Time).Seconds()
  350. msg.Code = lw.code
  351. msg.Bytes = lw.bytes
  352. switch {
  353. case lw.hijacked:
  354. // Connection no longer belongs to us, just log that we
  355. // switched protocols away from HTTP.
  356. if msg.Code == 0 {
  357. msg.Code = http.StatusSwitchingProtocols
  358. }
  359. case err != nil && r.Context().Err() == context.Canceled:
  360. msg.Code = 499 // nginx convention: Client Closed Request
  361. msg.Err = context.Canceled.Error()
  362. case hErrOK:
  363. // Handler asked us to send an error. Do so, if we haven't
  364. // already sent a response.
  365. msg.Err = hErr.Msg
  366. if hErr.Err != nil {
  367. if msg.Err == "" {
  368. msg.Err = hErr.Err.Error()
  369. } else {
  370. msg.Err = msg.Err + ": " + hErr.Err.Error()
  371. }
  372. }
  373. if lw.code != 0 {
  374. h.opts.Logf("[unexpected] handler returned HTTPError %v, but already sent a response with code %d", hErr, lw.code)
  375. break
  376. }
  377. msg.Code = hErr.Code
  378. if msg.Code == 0 {
  379. h.opts.Logf("[unexpected] HTTPError %v did not contain an HTTP status code, sending internal server error", hErr)
  380. msg.Code = http.StatusInternalServerError
  381. }
  382. if h.opts.OnError != nil {
  383. h.opts.OnError(lw, r, hErr)
  384. } else {
  385. // Default headers set by http.Error.
  386. lw.Header().Set("Content-Type", "text/plain; charset=utf-8")
  387. lw.Header().Set("X-Content-Type-Options", "nosniff")
  388. for k, vs := range hErr.Header {
  389. lw.Header()[k] = vs
  390. }
  391. lw.WriteHeader(msg.Code)
  392. fmt.Fprintln(lw, hErr.Msg)
  393. if msg.RequestID != "" {
  394. fmt.Fprintln(lw, msg.RequestID)
  395. }
  396. }
  397. case err != nil:
  398. const internalServerError = "internal server error"
  399. errorMessage := internalServerError
  400. if msg.RequestID != "" {
  401. errorMessage += "\n" + string(msg.RequestID)
  402. }
  403. // Handler returned a generic error. Serve an internal server
  404. // error, if necessary.
  405. msg.Err = err.Error()
  406. if lw.code == 0 {
  407. msg.Code = http.StatusInternalServerError
  408. http.Error(lw, errorMessage, msg.Code)
  409. }
  410. }
  411. if h.opts.OnCompletion != nil {
  412. h.opts.OnCompletion(r, msg)
  413. }
  414. if bs := h.opts.BucketedStats; bs != nil && bs.Finished != nil {
  415. // Only increment metrics for buckets that result in good HTTP statuses
  416. // or when we know the start was already counted.
  417. // Otherwise they get full of internet scanning noise. Only filtering 404
  418. // gets most of the way there but there are also plenty of URLs that are
  419. // almost right but result in 400s too. Seem easier to just only ignore
  420. // all 4xx and 5xx.
  421. if startRecorded {
  422. bs.Finished.Add(bucket, 1)
  423. } else if msg.Code < 400 {
  424. // This is the first non-error request for this bucket,
  425. // so count it now retroactively.
  426. bs.Started.Add(bucket, 1)
  427. bs.Finished.Add(bucket, 1)
  428. }
  429. }
  430. if !h.opts.QuietLoggingIfSuccessful || (msg.Code != http.StatusOK && msg.Code != http.StatusNotModified) {
  431. h.opts.Logf("%s", msg)
  432. }
  433. if h.opts.StatusCodeCounters != nil {
  434. h.opts.StatusCodeCounters.Add(responseCodeString(msg.Code/100), 1)
  435. }
  436. if h.opts.StatusCodeCountersFull != nil {
  437. h.opts.StatusCodeCountersFull.Add(responseCodeString(msg.Code), 1)
  438. }
  439. }
  440. func responseCodeString(code int) string {
  441. if v, ok := responseCodeCache.Load(code); ok {
  442. return v.(string)
  443. }
  444. var ret string
  445. if code < 10 {
  446. ret = fmt.Sprintf("%dxx", code)
  447. } else {
  448. ret = strconv.Itoa(code)
  449. }
  450. responseCodeCache.Store(code, ret)
  451. return ret
  452. }
  453. // responseCodeCache memoizes the string form of HTTP response codes,
  454. // so that the hot request-handling codepath doesn't have to allocate
  455. // in strconv/fmt for every request.
  456. //
  457. // Keys are either full HTTP response code ints (200, 404) or "family"
  458. // ints representing entire families (e.g. 2 for 2xx codes). Values
  459. // are the string form of that code/family.
  460. var responseCodeCache sync.Map
  461. // loggingResponseWriter wraps a ResponseWriter and record the HTTP
  462. // response code that gets sent, if any.
  463. type loggingResponseWriter struct {
  464. http.ResponseWriter
  465. code int
  466. bytes int
  467. hijacked bool
  468. logf logger.Logf
  469. }
  470. // WriteHeader implements http.Handler.
  471. func (l *loggingResponseWriter) WriteHeader(statusCode int) {
  472. if l.code != 0 {
  473. l.logf("[unexpected] HTTP handler set statusCode twice (%d and %d)", l.code, statusCode)
  474. return
  475. }
  476. l.code = statusCode
  477. l.ResponseWriter.WriteHeader(statusCode)
  478. }
  479. // Write implements http.Handler.
  480. func (l *loggingResponseWriter) Write(bs []byte) (int, error) {
  481. if l.code == 0 {
  482. l.code = 200
  483. }
  484. n, err := l.ResponseWriter.Write(bs)
  485. l.bytes += n
  486. return n, err
  487. }
  488. // Hijack implements http.Hijacker. Note that hijacking can still fail
  489. // because the wrapped ResponseWriter is not required to implement
  490. // Hijacker, as this breaks HTTP/2.
  491. func (l *loggingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
  492. h, ok := l.ResponseWriter.(http.Hijacker)
  493. if !ok {
  494. return nil, nil, errors.New("ResponseWriter is not a Hijacker")
  495. }
  496. conn, buf, err := h.Hijack()
  497. if err == nil {
  498. l.hijacked = true
  499. }
  500. return conn, buf, err
  501. }
  502. func (l loggingResponseWriter) Flush() {
  503. f, _ := l.ResponseWriter.(http.Flusher)
  504. if f == nil {
  505. l.logf("[unexpected] tried to Flush a ResponseWriter that can't flush")
  506. return
  507. }
  508. f.Flush()
  509. }
  510. // HTTPError is an error with embedded HTTP response information.
  511. //
  512. // It is the error type to be (optionally) used by Handler.ServeHTTPReturn.
  513. type HTTPError struct {
  514. Code int // HTTP response code to send to client; 0 means 500
  515. Msg string // Response body to send to client
  516. Err error // Detailed error to log on the server
  517. Header http.Header // Optional set of HTTP headers to set in the response
  518. }
  519. // Error implements the error interface.
  520. func (e HTTPError) Error() string { return fmt.Sprintf("httperror{%d, %q, %v}", e.Code, e.Msg, e.Err) }
  521. func (e HTTPError) Unwrap() error { return e.Err }
  522. // Error returns an HTTPError containing the given information.
  523. func Error(code int, msg string, err error) HTTPError {
  524. return HTTPError{Code: code, Msg: msg, Err: err}
  525. }
  526. // VarzHandler writes expvar values as Prometheus metrics.
  527. // TODO: migrate all users to varz.Handler or promvarz.Handler and remove this.
  528. func VarzHandler(w http.ResponseWriter, r *http.Request) {
  529. varz.Handler(w, r)
  530. }
  531. // CleanRedirectURL ensures that urlStr is a valid redirect URL to the
  532. // current server, or one of allowedHosts. Returns the cleaned URL or
  533. // a validation error.
  534. func CleanRedirectURL(urlStr string, allowedHosts []string) (*url.URL, error) {
  535. if urlStr == "" {
  536. return &url.URL{}, nil
  537. }
  538. // In some places, we unfortunately query-escape the redirect URL
  539. // too many times, and end up needing to redirect to a URL that's
  540. // still escaped by one level. Try to unescape the input.
  541. unescaped, err := url.QueryUnescape(urlStr)
  542. if err == nil && unescaped != urlStr {
  543. urlStr = unescaped
  544. }
  545. // Go's URL parser and browser URL parsers disagree on the meaning
  546. // of malformed HTTP URLs. Given the input https:/evil.com, Go
  547. // parses it as hostname="", path="/evil.com". Browsers parse it
  548. // as hostname="evil.com", path="". This means that, using
  549. // malformed URLs, an attacker could trick us into approving of a
  550. // "local" redirect that in fact sends people elsewhere.
  551. //
  552. // This very blunt check enforces that we'll only process
  553. // redirects that are definitely well-formed URLs.
  554. //
  555. // Note that the check for just / also allows URLs of the form
  556. // "//foo.com/bar", which are scheme-relative redirects. These
  557. // must be handled with care below when determining whether a
  558. // redirect is relative to the current host. Notably,
  559. // url.URL.IsAbs reports // URLs as relative, whereas we want to
  560. // treat them as absolute redirects and verify the target host.
  561. if !hasSafeRedirectPrefix(urlStr) {
  562. return nil, fmt.Errorf("invalid redirect URL %q", urlStr)
  563. }
  564. url, err := url.Parse(urlStr)
  565. if err != nil {
  566. return nil, fmt.Errorf("invalid redirect URL %q: %w", urlStr, err)
  567. }
  568. // Redirects to self are always allowed. A self redirect must
  569. // start with url.Path, all prior URL sections must be empty.
  570. isSelfRedirect := url.Scheme == "" && url.Opaque == "" && url.User == nil && url.Host == ""
  571. if isSelfRedirect {
  572. return url, nil
  573. }
  574. for _, allowed := range allowedHosts {
  575. if strings.EqualFold(allowed, url.Hostname()) {
  576. return url, nil
  577. }
  578. }
  579. return nil, fmt.Errorf("disallowed target host %q in redirect URL %q", url.Hostname(), urlStr)
  580. }
  581. // hasSafeRedirectPrefix reports whether url starts with a slash, or
  582. // one of the case-insensitive strings "http://" or "https://".
  583. func hasSafeRedirectPrefix(url string) bool {
  584. if len(url) >= 1 && url[0] == '/' {
  585. return true
  586. }
  587. const http = "http://"
  588. if len(url) >= len(http) && strings.EqualFold(url[:len(http)], http) {
  589. return true
  590. }
  591. const https = "https://"
  592. if len(url) >= len(https) && strings.EqualFold(url[:len(https)], https) {
  593. return true
  594. }
  595. return false
  596. }
  597. // AddBrowserHeaders sets various HTTP security headers for browser-facing endpoints.
  598. //
  599. // The specific headers:
  600. // - require HTTPS access (HSTS)
  601. // - disallow iframe embedding
  602. // - mitigate MIME confusion attacks
  603. //
  604. // These headers are based on
  605. // https://infosec.mozilla.org/guidelines/web_security
  606. func AddBrowserHeaders(w http.ResponseWriter) {
  607. w.Header().Set("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
  608. w.Header().Set("Content-Security-Policy", "default-src 'self'; frame-ancestors 'none'; form-action 'self'; base-uri 'self'; block-all-mixed-content; object-src 'none'")
  609. w.Header().Set("X-Frame-Options", "DENY")
  610. w.Header().Set("X-Content-Type-Options", "nosniff")
  611. }
  612. // BrowserHeaderHandler wraps the provided http.Handler with a call to
  613. // AddBrowserHeaders.
  614. func BrowserHeaderHandler(h http.Handler) http.Handler {
  615. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  616. AddBrowserHeaders(w)
  617. h.ServeHTTP(w, r)
  618. })
  619. }
  620. // BrowserHeaderHandlerFunc wraps the provided http.HandlerFunc with a call to
  621. // AddBrowserHeaders.
  622. func BrowserHeaderHandlerFunc(h http.HandlerFunc) http.HandlerFunc {
  623. return func(w http.ResponseWriter, r *http.Request) {
  624. AddBrowserHeaders(w)
  625. h.ServeHTTP(w, r)
  626. }
  627. }