Skip to content

Commit 8f3c6a1

Browse files
committed
Make Rails cookies RFC6265-compliant with domain: :all
Rails has incorrectly been adding leading dots to cookie domain values when the `domain: :all` option is present. This leading dot was required in cookies based on [RFC 2965][rfc2965] (October 2000), but [RFC 6265][rfc6265] (April 2011) changed that behaviour, making a leading dot strictly incorrect. Todays browsers aim to confirm to RFC6265 with repect to cookies. The new behaviour is that *any* cookie with an explicitly passed domain is sent to all matching subdomains[[ref][mdn]]. For a server to indicate that only the exact origin server should receive the cookie, it should instead pass *no* domain attribute. Despite the change in behaviour, browser devtools often display a cookie domain with a leading dot to indicate that it is valid for subdomains - this prefixed domain is *not* necessarily the raw value that was passed in the Set-Cookie header. This explains why it's a common belief among developers that the leading dot is required. RFC6265 standard gives UAs an algorithm to handle old-style cookie domain parameters (they can drop a leading dot if present), so it's unlikely that this error would ever have had any effect on web browsers. However, cookies generated this way can't be processed by Ruby's own CGI::Cookie class: > CGI::Cookie.new "domain" => ".foo.bar", "name" => "foo" ArgumentError: invalid domain: ".foo.bar" Newer versions of the Ruby CGI library accomodate the same fallback behaviour (dropping the extra dot) but this isn't a justification for it being the right way to set a cookie. [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#domain_attribute [rfc2965]: https://www.rfc-editor.org/rfc/rfc2965#section-3.2 [rfc6265]: https://www.rfc-editor.org/rfc/rfc6265#section-4.1.1
1 parent ea7ac15 commit 8f3c6a1

File tree

4 files changed

+22
-18
lines changed

4 files changed

+22
-18
lines changed

actionpack/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
* Remove leading dot from domains on cookies set with `domain: :all`, to meet RFC6265 requirements
2+
3+
*Gareth Adams*
4+
15
* Include source location in routes extended view.
26

37
```bash

actionpack/lib/action_dispatch/middleware/cookies.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ def handle_options(options)
470470
end
471471

472472
options[:domain] = if cookie_domain.present?
473-
".#{cookie_domain}"
473+
cookie_domain
474474
end
475475
elsif options[:domain].is_a? Array
476476
# If host matches one of the supplied domains.

actionpack/test/dispatch/cookies_test.rb

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,63 +1122,63 @@ def test_cookie_with_hash_value_not_modified_by_rotation
11221122
def test_cookie_with_all_domain_option
11231123
get :set_cookie_with_domain
11241124
assert_response :success
1125-
assert_set_cookie_header "user_name=rizwanreza; domain=.nextangle.com; path=/; SameSite=Lax"
1125+
assert_set_cookie_header "user_name=rizwanreza; domain=nextangle.com; path=/; SameSite=Lax"
11261126
end
11271127

11281128
def test_cookie_with_all_domain_option_using_a_non_standard_tld
11291129
@request.host = "two.subdomains.nextangle.local"
11301130
get :set_cookie_with_domain
11311131
assert_response :success
1132-
assert_set_cookie_header "user_name=rizwanreza; domain=.nextangle.local; path=/; SameSite=Lax"
1132+
assert_set_cookie_header "user_name=rizwanreza; domain=nextangle.local; path=/; SameSite=Lax"
11331133
end
11341134

11351135
def test_cookie_with_all_domain_option_using_australian_style_tld
11361136
@request.host = "nextangle.com.au"
11371137
get :set_cookie_with_domain
11381138
assert_response :success
1139-
assert_set_cookie_header "user_name=rizwanreza; domain=.nextangle.com.au; path=/; SameSite=Lax"
1139+
assert_set_cookie_header "user_name=rizwanreza; domain=nextangle.com.au; path=/; SameSite=Lax"
11401140
end
11411141

11421142
def test_cookie_with_all_domain_option_using_australian_style_tld_and_two_subdomains
11431143
@request.host = "x.nextangle.com.au"
11441144
get :set_cookie_with_domain
11451145
assert_response :success
1146-
assert_set_cookie_header "user_name=rizwanreza; domain=.nextangle.com.au; path=/; SameSite=Lax"
1146+
assert_set_cookie_header "user_name=rizwanreza; domain=nextangle.com.au; path=/; SameSite=Lax"
11471147
end
11481148

11491149
def test_cookie_with_all_domain_option_using_uk_style_tld
11501150
@request.host = "nextangle.co.uk"
11511151
get :set_cookie_with_domain
11521152
assert_response :success
1153-
assert_set_cookie_header "user_name=rizwanreza; domain=.nextangle.co.uk; path=/; SameSite=Lax"
1153+
assert_set_cookie_header "user_name=rizwanreza; domain=nextangle.co.uk; path=/; SameSite=Lax"
11541154
end
11551155

11561156
def test_cookie_with_all_domain_option_using_two_letter_one_level_tld
11571157
@request.host = "hawth.ca"
11581158
get :set_cookie_with_domain
11591159
assert_response :success
1160-
assert_set_cookie_header "user_name=rizwanreza; domain=.hawth.ca; path=/; SameSite=Lax"
1160+
assert_set_cookie_header "user_name=rizwanreza; domain=hawth.ca; path=/; SameSite=Lax"
11611161
end
11621162

11631163
def test_cookie_with_all_domain_option_using_two_letter_one_level_tld_and_subdomain
11641164
@request.host = "x.hawth.ca"
11651165
get :set_cookie_with_domain
11661166
assert_response :success
1167-
assert_set_cookie_header "user_name=rizwanreza; domain=.hawth.ca; path=/; SameSite=Lax"
1167+
assert_set_cookie_header "user_name=rizwanreza; domain=hawth.ca; path=/; SameSite=Lax"
11681168
end
11691169

11701170
def test_cookie_with_all_domain_option_using_uk_style_tld_and_two_subdomains
11711171
@request.host = "x.nextangle.co.uk"
11721172
get :set_cookie_with_domain
11731173
assert_response :success
1174-
assert_set_cookie_header "user_name=rizwanreza; domain=.nextangle.co.uk; path=/; SameSite=Lax"
1174+
assert_set_cookie_header "user_name=rizwanreza; domain=nextangle.co.uk; path=/; SameSite=Lax"
11751175
end
11761176

11771177
def test_cookie_with_all_domain_option_using_host_with_port
11781178
@request.host = "nextangle.local:3000"
11791179
get :set_cookie_with_domain
11801180
assert_response :success
1181-
assert_set_cookie_header "user_name=rizwanreza; domain=.nextangle.local; path=/; SameSite=Lax"
1181+
assert_set_cookie_header "user_name=rizwanreza; domain=nextangle.local; path=/; SameSite=Lax"
11821182
end
11831183

11841184
def test_cookie_with_all_domain_option_using_localhost
@@ -1206,48 +1206,48 @@ def test_deleting_cookie_with_all_domain_option
12061206
request.cookies[:user_name] = "Joe"
12071207
get :delete_cookie_with_domain
12081208
assert_response :success
1209-
assert_set_cookie_header "user_name=; domain=.nextangle.com; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax"
1209+
assert_set_cookie_header "user_name=; domain=nextangle.com; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax"
12101210
end
12111211

12121212
def test_cookie_with_all_domain_option_and_tld_length
12131213
get :set_cookie_with_domain_and_tld
12141214
assert_response :success
1215-
assert_set_cookie_header "user_name=rizwanreza; domain=.nextangle.com; path=/; SameSite=Lax"
1215+
assert_set_cookie_header "user_name=rizwanreza; domain=nextangle.com; path=/; SameSite=Lax"
12161216
end
12171217

12181218
def test_cookie_with_all_domain_option_using_a_non_standard_tld_and_tld_length
12191219
@request.host = "two.subdomains.nextangle.local"
12201220
get :set_cookie_with_domain_and_tld
12211221
assert_response :success
1222-
assert_set_cookie_header "user_name=rizwanreza; domain=.nextangle.local; path=/; SameSite=Lax"
1222+
assert_set_cookie_header "user_name=rizwanreza; domain=nextangle.local; path=/; SameSite=Lax"
12231223
end
12241224

12251225
def test_cookie_with_all_domain_option_using_a_non_standard_2_letter_tld
12261226
@request.host = "admin.lvh.me"
12271227
get :set_cookie_with_domain_and_tld
12281228
assert_response :success
1229-
assert_set_cookie_header "user_name=rizwanreza; domain=.lvh.me; path=/; SameSite=Lax"
1229+
assert_set_cookie_header "user_name=rizwanreza; domain=lvh.me; path=/; SameSite=Lax"
12301230
end
12311231

12321232
def test_cookie_with_all_domain_option_using_host_with_port_and_tld_length
12331233
@request.host = "nextangle.local:3000"
12341234
get :set_cookie_with_domain_and_tld
12351235
assert_response :success
1236-
assert_set_cookie_header "user_name=rizwanreza; domain=.nextangle.local; path=/; SameSite=Lax"
1236+
assert_set_cookie_header "user_name=rizwanreza; domain=nextangle.local; path=/; SameSite=Lax"
12371237
end
12381238

12391239
def test_cookie_with_all_domain_option_using_longer_tld_length
12401240
@request.host = "x.y.z.t.com"
12411241
get :set_cookie_with_domain_and_longer_tld
12421242
assert_response :success
1243-
assert_set_cookie_header "user_name=rizwanreza; domain=.y.z.t.com; path=/; SameSite=Lax"
1243+
assert_set_cookie_header "user_name=rizwanreza; domain=y.z.t.com; path=/; SameSite=Lax"
12441244
end
12451245

12461246
def test_deleting_cookie_with_all_domain_option_and_tld_length
12471247
request.cookies[:user_name] = "Joe"
12481248
get :delete_cookie_with_domain_and_tld
12491249
assert_response :success
1250-
assert_set_cookie_header "user_name=; domain=.nextangle.com; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax"
1250+
assert_set_cookie_header "user_name=; domain=nextangle.com; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax"
12511251
end
12521252

12531253
def test_cookie_with_several_preset_domains_using_one_of_these_domains

actionpack/test/dispatch/session/cookie_store_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ def test_session_store_with_nil_domain
385385
def test_session_store_with_all_domains
386386
with_test_route_set(domain: :all) do
387387
get "/set_session_value"
388-
assert_match(/domain=\.example\.com/, headers["Set-Cookie"])
388+
assert_match(/domain=example\.com/, headers["Set-Cookie"])
389389
end
390390
end
391391

0 commit comments

Comments
 (0)