summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWill Holley <willholley@gmail.com>2020-01-13 19:57:19 +0000
committerWill Holley <willholley@gmail.com>2020-01-14 17:48:26 +0000
commit6e03a48413055a292fe5627ce89106e47b8415e5 (patch)
tree0ead752863beb13d81c4cdeb9d02c7123ff87612
parent283c564e7a612a3855c1072e52e2cb9380bc0a7a (diff)
downloadcouchdb-samesite_cookie.tar.gz
Add SameSite support to auth cookiesamesite_cookie
Adds a new configuration field, `couch_httpd_auth.same_site` which sets the `SameSite` attribute of the CouchDB auth cookie. If no value is set (the default), no `SameSite` attribute is added. Refs #2221
-rw-r--r--.gitignore1
-rw-r--r--rel/overlay/etc/default.ini2
-rw-r--r--src/couch/src/couch_httpd_auth.erl16
-rw-r--r--src/couch/test/exunit/same_site_cookie_tests.exs44
4 files changed, 62 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index 5eec70f3e..3fa860c59 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@
*~
.venv
.DS_Store
+.vscode
.rebar/
.eunit/
cover/
diff --git a/rel/overlay/etc/default.ini b/rel/overlay/etc/default.ini
index 5fc8e0761..a301987b2 100644
--- a/rel/overlay/etc/default.ini
+++ b/rel/overlay/etc/default.ini
@@ -245,6 +245,8 @@ iterations = 10 ; iterations for password hashing
; secret =
; users_db_public = false
; cookie_domain = example.com
+; Set the SameSite cookie property for the auth cookie. If empty, the SameSite property is not set.
+; same_site =
; CSP (Content Security Policy) Support for _utils
[csp]
diff --git a/src/couch/src/couch_httpd_auth.erl b/src/couch/src/couch_httpd_auth.erl
index 515ce6132..96de5bf3b 100644
--- a/src/couch/src/couch_httpd_auth.erl
+++ b/src/couch/src/couch_httpd_auth.erl
@@ -273,7 +273,7 @@ cookie_auth_cookie(Req, User, Secret, TimeStamp) ->
Hash = crypto:hmac(sha, Secret, SessionData),
mochiweb_cookies:cookie("AuthSession",
couch_util:encodeBase64Url(SessionData ++ ":" ++ ?b2l(Hash)),
- [{path, "/"}] ++ cookie_scheme(Req) ++ max_age() ++ cookie_domain()).
+ [{path, "/"}] ++ cookie_scheme(Req) ++ max_age() ++ cookie_domain() ++ same_site()).
ensure_cookie_auth_secret() ->
case config:get("couch_httpd_auth", "secret", undefined) of
@@ -457,6 +457,20 @@ cookie_domain() ->
_ -> [{domain, Domain}]
end.
+
+same_site() ->
+ SameSite = config:get("couch_httpd_auth", "same_site", ""),
+ case string:to_lower(SameSite) of
+ "" -> [];
+ "none" -> [{same_site, none}];
+ "lax" -> [{same_site, lax}];
+ "strict" -> [{same_site, strict}];
+ _ ->
+ couch_log:error("invalid config value couch_httpd_auth.same_site: ~p ",[SameSite]),
+ []
+ end.
+
+
reject_if_totp(User) ->
case get_totp_config(User) of
undefined ->
diff --git a/src/couch/test/exunit/same_site_cookie_tests.exs b/src/couch/test/exunit/same_site_cookie_tests.exs
new file mode 100644
index 000000000..bad32ada4
--- /dev/null
+++ b/src/couch/test/exunit/same_site_cookie_tests.exs
@@ -0,0 +1,44 @@
+defmodule SameSiteCookieTests do
+ use CouchTestCase
+
+ @moduletag :authentication
+
+ def get_cookie(user, pass) do
+ resp = Couch.post("/_session", body: %{:username => user, :password => pass})
+
+ true = resp.body["ok"]
+ resp.headers[:"set-cookie"]
+ end
+
+ @tag config: [{"admins", "jan", "apple"}, {"couch_httpd_auth", "same_site", "None"}]
+ test "Set same_site None" do
+ cookie = get_cookie("jan", "apple")
+ assert cookie =~ "; SameSite=None"
+ end
+
+ @tag config: [{"admins", "jan", "apple"}, {"couch_httpd_auth", "same_site", ""}]
+ test "same_site not set" do
+ cookie = get_cookie("jan", "apple")
+ assert cookie
+ refute cookie =~ "; SameSite="
+ end
+
+ @tag config: [{"admins", "jan", "apple"}, {"couch_httpd_auth", "same_site", "Strict"}]
+ test "Set same_site Strict" do
+ cookie = get_cookie("jan", "apple")
+ assert cookie =~ "; SameSite=Strict"
+ end
+
+ @tag config: [{"admins", "jan", "apple"}, {"couch_httpd_auth", "same_site", "Lax"}]
+ test "Set same_site Lax" do
+ cookie = get_cookie("jan", "apple")
+ assert cookie =~ "; SameSite=Lax"
+ end
+
+ @tag config: [{"admins", "jan", "apple"}, {"couch_httpd_auth", "same_site", "Invalid"}]
+ test "Set same_site invalid" do
+ cookie = get_cookie("jan", "apple")
+ assert cookie
+ refute cookie =~ "; SameSite="
+ end
+end