gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. // Copyright 2013 Martini Authors
  2. // Copyright 2014 The Macaron Authors
  3. // Copyright 2021 The Gitea Authors
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License"): you may
  6. // not use this file except in compliance with the License. You may obtain
  7. // a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  13. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  14. // License for the specific language governing permissions and limitations
  15. // under the License.
  16. // SPDX-License-Identifier: Apache-2.0
  17. // a middleware that generates and validates CSRF tokens.
  18. package context
  19. import (
  20. "html/template"
  21. "net/http"
  22. "strconv"
  23. "time"
  24. "code.gitea.io/gitea/modules/log"
  25. "code.gitea.io/gitea/modules/util"
  26. )
  27. const (
  28. CsrfHeaderName = "X-Csrf-Token"
  29. CsrfFormName = "_csrf"
  30. )
  31. // CSRFProtector represents a CSRF protector and is used to get the current token and validate the token.
  32. type CSRFProtector interface {
  33. // PrepareForSessionUser prepares the csrf protector for the current session user.
  34. PrepareForSessionUser(ctx *Context)
  35. // Validate validates the csrf token in http context.
  36. Validate(ctx *Context)
  37. // DeleteCookie deletes the csrf cookie
  38. DeleteCookie(ctx *Context)
  39. }
  40. type csrfProtector struct {
  41. opt CsrfOptions
  42. // id must be unique per user.
  43. id string
  44. // token is the valid one which will be used by end user and passed via header, cookie, or hidden form value.
  45. token string
  46. }
  47. // CsrfOptions maintains options to manage behavior of Generate.
  48. type CsrfOptions struct {
  49. // The global secret value used to generate Tokens.
  50. Secret string
  51. // Cookie value used to set and get token.
  52. Cookie string
  53. // Cookie domain.
  54. CookieDomain string
  55. // Cookie path.
  56. CookiePath string
  57. CookieHTTPOnly bool
  58. // SameSite set the cookie SameSite type
  59. SameSite http.SameSite
  60. // Set the Secure flag to true on the cookie.
  61. Secure bool
  62. // sessionKey is the key used for getting the unique ID per user.
  63. sessionKey string
  64. // oldSessionKey saves old value corresponding to sessionKey.
  65. oldSessionKey string
  66. }
  67. func newCsrfCookie(opt *CsrfOptions, value string) *http.Cookie {
  68. return &http.Cookie{
  69. Name: opt.Cookie,
  70. Value: value,
  71. Path: opt.CookiePath,
  72. Domain: opt.CookieDomain,
  73. MaxAge: int(CsrfTokenTimeout.Seconds()),
  74. Secure: opt.Secure,
  75. HttpOnly: opt.CookieHTTPOnly,
  76. SameSite: opt.SameSite,
  77. }
  78. }
  79. func NewCSRFProtector(opt CsrfOptions) CSRFProtector {
  80. if opt.Secret == "" {
  81. panic("CSRF secret is empty but it must be set") // it shouldn't happen because it is always set in code
  82. }
  83. opt.Cookie = util.IfZero(opt.Cookie, "_csrf")
  84. opt.CookiePath = util.IfZero(opt.CookiePath, "/")
  85. opt.sessionKey = "uid"
  86. opt.oldSessionKey = "_old_" + opt.sessionKey
  87. return &csrfProtector{opt: opt}
  88. }
  89. func (c *csrfProtector) PrepareForSessionUser(ctx *Context) {
  90. c.id = "0"
  91. if uidAny := ctx.Session.Get(c.opt.sessionKey); uidAny != nil {
  92. switch uidVal := uidAny.(type) {
  93. case string:
  94. c.id = uidVal
  95. case int64:
  96. c.id = strconv.FormatInt(uidVal, 10)
  97. default:
  98. log.Error("invalid uid type in session: %T", uidAny)
  99. }
  100. }
  101. oldUID := ctx.Session.Get(c.opt.oldSessionKey)
  102. uidChanged := oldUID == nil || oldUID.(string) != c.id
  103. cookieToken := ctx.GetSiteCookie(c.opt.Cookie)
  104. needsNew := true
  105. if uidChanged {
  106. _ = ctx.Session.Set(c.opt.oldSessionKey, c.id)
  107. } else if cookieToken != "" {
  108. // If cookie token presents, re-use existing unexpired token, else generate a new one.
  109. if issueTime, ok := ParseCsrfToken(cookieToken); ok {
  110. dur := time.Since(issueTime) // issueTime is not a monotonic-clock, the server time may change a lot to an early time.
  111. if dur >= -CsrfTokenRegenerationInterval && dur <= CsrfTokenRegenerationInterval {
  112. c.token = cookieToken
  113. needsNew = false
  114. }
  115. }
  116. }
  117. if needsNew {
  118. c.token = GenerateCsrfToken(c.opt.Secret, c.id, "POST", time.Now())
  119. ctx.Resp.Header().Add("Set-Cookie", newCsrfCookie(&c.opt, c.token).String())
  120. }
  121. ctx.Data["CsrfToken"] = c.token
  122. ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + template.HTMLEscapeString(c.token) + `">`)
  123. }
  124. func (c *csrfProtector) validateToken(ctx *Context, token string) {
  125. if !ValidCsrfToken(token, c.opt.Secret, c.id, "POST", time.Now()) {
  126. c.DeleteCookie(ctx)
  127. // currently, there should be no access to the APIPath with CSRF token. because templates shouldn't use the `/api/` endpoints.
  128. // FIXME: distinguish what the response is for: HTML (web page) or JSON (fetch)
  129. http.Error(ctx.Resp, "Invalid CSRF token.", http.StatusBadRequest)
  130. }
  131. }
  132. // Validate should be used as a per route middleware. It attempts to get a token from an "X-Csrf-Token"
  133. // HTTP header and then a "_csrf" form value. If one of these is found, the token will be validated.
  134. // If this validation fails, http.StatusBadRequest is sent.
  135. func (c *csrfProtector) Validate(ctx *Context) {
  136. if token := ctx.Req.Header.Get(CsrfHeaderName); token != "" {
  137. c.validateToken(ctx, token)
  138. return
  139. }
  140. if token := ctx.Req.FormValue(CsrfFormName); token != "" {
  141. c.validateToken(ctx, token)
  142. return
  143. }
  144. c.validateToken(ctx, "") // no csrf token, use an empty token to respond error
  145. }
  146. func (c *csrfProtector) DeleteCookie(ctx *Context) {
  147. cookie := newCsrfCookie(&c.opt, "")
  148. cookie.MaxAge = -1
  149. ctx.Resp.Header().Add("Set-Cookie", cookie.String())
  150. }