Skip to content

Commit 8b2c77b

Browse files
committed
Fix #1523 by adding SameSite mode for CSRF settings
1 parent 3e8a797 commit 8b2c77b

File tree

2 files changed

+53
-6
lines changed

2 files changed

+53
-6
lines changed

middleware/csrf.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ type (
5757
// Indicates if CSRF cookie is HTTP only.
5858
// Optional. Default value false.
5959
CookieHTTPOnly bool `yaml:"cookie_http_only"`
60+
61+
// Indicates SameSite mode of the CSRF cookie.
62+
// Optional. Default value SameSiteDefaultMode.
63+
CookieSameSite http.SameSite `yaml:"cookie_same_site"`
6064
}
6165

6266
// csrfTokenExtractor defines a function that takes `echo.Context` and returns
@@ -67,12 +71,13 @@ type (
6771
var (
6872
// DefaultCSRFConfig is the default CSRF middleware config.
6973
DefaultCSRFConfig = CSRFConfig{
70-
Skipper: DefaultSkipper,
71-
TokenLength: 32,
72-
TokenLookup: "header:" + echo.HeaderXCSRFToken,
73-
ContextKey: "csrf",
74-
CookieName: "_csrf",
75-
CookieMaxAge: 86400,
74+
Skipper: DefaultSkipper,
75+
TokenLength: 32,
76+
TokenLookup: "header:" + echo.HeaderXCSRFToken,
77+
ContextKey: "csrf",
78+
CookieName: "_csrf",
79+
CookieMaxAge: 86400,
80+
CookieSameSite: http.SameSiteDefaultMode,
7681
}
7782
)
7883

@@ -105,6 +110,9 @@ func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc {
105110
if config.CookieMaxAge == 0 {
106111
config.CookieMaxAge = DefaultCSRFConfig.CookieMaxAge
107112
}
113+
if config.CookieSameSite == 0 {
114+
config.CookieSameSite = http.SameSiteDefaultMode
115+
}
108116

109117
// Initialize
110118
parts := strings.Split(config.TokenLookup, ":")
@@ -157,6 +165,9 @@ func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc {
157165
if config.CookieDomain != "" {
158166
cookie.Domain = config.CookieDomain
159167
}
168+
if config.CookieSameSite != http.SameSiteDefaultMode {
169+
cookie.SameSite = config.CookieSameSite
170+
}
160171
cookie.Expires = time.Now().Add(time.Duration(config.CookieMaxAge) * time.Second)
161172
cookie.Secure = config.CookieSecure
162173
cookie.HttpOnly = config.CookieHTTPOnly

middleware/csrf_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,39 @@ func TestCSRFTokenFromQuery(t *testing.T) {
8181
assert.Error(t, err)
8282
csrfTokenFromQuery("csrf")
8383
}
84+
85+
func TestCSRFSetSameSiteMode(t *testing.T) {
86+
e := echo.New()
87+
req := httptest.NewRequest(http.MethodGet, "/", nil)
88+
rec := httptest.NewRecorder()
89+
c := e.NewContext(req, rec)
90+
91+
csrf := CSRFWithConfig(CSRFConfig{
92+
CookieSameSite: http.SameSiteStrictMode,
93+
})
94+
95+
h := csrf(func(c echo.Context) error {
96+
return c.String(http.StatusOK, "test")
97+
})
98+
99+
r := h(c)
100+
assert.NoError(t, r)
101+
assert.Regexp(t, "SameSite=Strict", rec.Header()["Set-Cookie"])
102+
}
103+
104+
func TestCSRFWithoutSameSiteMode(t *testing.T) {
105+
e := echo.New()
106+
req := httptest.NewRequest(http.MethodGet, "/", nil)
107+
rec := httptest.NewRecorder()
108+
c := e.NewContext(req, rec)
109+
110+
csrf := CSRFWithConfig(CSRFConfig{})
111+
112+
h := csrf(func(c echo.Context) error {
113+
return c.String(http.StatusOK, "test")
114+
})
115+
116+
r := h(c)
117+
assert.NoError(t, r)
118+
assert.NotRegexp(t, "SameSite=", rec.Header()["Set-Cookie"])
119+
}

0 commit comments

Comments
 (0)