Treat comma as normal character in HTTP::Cookie.cookie_value_to_hash

As pointed out in CVE-2016-7401, treating comma in a Cookie header value
as separator may cause security problems.
This commit is contained in:
Akinori MUSHA 2016-09-30 19:20:39 +09:00
parent 1c4a7bbe4b
commit 7f94a9e5d9
2 changed files with 25 additions and 13 deletions

View file

@ -50,7 +50,7 @@ class HTTP::Cookie::Scanner < StringScanner
} }
end end
def scan_value def scan_value(comma_as_separator = false)
''.tap { |s| ''.tap { |s|
case case
when scan(/[^,;"]+/) when scan(/[^,;"]+/)
@ -59,7 +59,9 @@ class HTTP::Cookie::Scanner < StringScanner
# RFC 6265 2.2 # RFC 6265 2.2
# A cookie-value may be DQUOTE'd. # A cookie-value may be DQUOTE'd.
s << scan_dquoted s << scan_dquoted
when check(/;|#{RE_COOKIE_COMMA}/o) when check(/;/)
break
when comma_as_separator && check(RE_COOKIE_COMMA)
break break
else else
s << getch s << getch
@ -68,12 +70,12 @@ class HTTP::Cookie::Scanner < StringScanner
} }
end end
def scan_name_value def scan_name_value(comma_as_separator = false)
name = scan_name name = scan_name
if skip(/\=/) if skip(/\=/)
value = scan_value value = scan_value(comma_as_separator)
else else
scan_value scan_value(comma_as_separator)
value = nil value = nil
end end
[name, value] [name, value]
@ -159,7 +161,7 @@ class HTTP::Cookie::Scanner < StringScanner
skip_wsp skip_wsp
name, value = scan_name_value name, value = scan_name_value(true)
if value.nil? if value.nil?
@logger.warn("Cookie definition lacks a name-value pair.") if @logger @logger.warn("Cookie definition lacks a name-value pair.") if @logger
elsif name.empty? elsif name.empty?
@ -176,7 +178,7 @@ class HTTP::Cookie::Scanner < StringScanner
break break
when skip(/;/) when skip(/;/)
skip_wsp skip_wsp
aname, avalue = scan_name_value aname, avalue = scan_name_value(true)
next if aname.empty? || value.nil? next if aname.empty? || value.nil?
aname.downcase! aname.downcase!
case aname case aname
@ -218,13 +220,12 @@ class HTTP::Cookie::Scanner < StringScanner
until eos? until eos?
skip_wsp skip_wsp
name, value = scan_name_value # Do not treat comma in a Cookie header value as separator; see CVE-2016-7401
name, value = scan_name_value(false)
yield name, value if value yield name, value if value
# The comma is used as separator for concatenating multiple skip(/;/)
# values of a header.
skip(/[;,]/)
end end
end end
end end

View file

@ -441,17 +441,28 @@ class TestHTTPCookie < Test::Unit::TestCase
['Bar', 'value 2'], ['Bar', 'value 2'],
['Baz', 'value3'], ['Baz', 'value3'],
['Bar', 'value"4'], ['Bar', 'value"4'],
['Quux', 'x, value=5'],
] ]
cookie_value = HTTP::Cookie.cookie_value(pairs.map { |name, value| cookie_value = HTTP::Cookie.cookie_value(pairs.map { |name, value|
HTTP::Cookie.new(:name => name, :value => value) HTTP::Cookie.new(:name => name, :value => value)
}) })
assert_equal 'Foo=value1; Bar="value 2"; Baz=value3; Bar="value\\"4"', cookie_value assert_equal 'Foo=value1; Bar="value 2"; Baz=value3; Bar="value\\"4"; Quux="x, value=5"', cookie_value
hash = HTTP::Cookie.cookie_value_to_hash(cookie_value) hash = HTTP::Cookie.cookie_value_to_hash(cookie_value)
assert_equal 3, hash.size assert_equal pairs.map(&:first).uniq.size, hash.size
hash.each_pair { |name, value|
_, pvalue = pairs.assoc(name)
assert_equal pvalue, value
}
# Do not treat comma in a Cookie header value as separator; see CVE-2016-7401
hash = HTTP::Cookie.cookie_value_to_hash('Quux=x, value=5; Foo=value1; Bar="value 2"; Baz=value3; Bar="value\\"4"')
assert_equal pairs.map(&:first).uniq.size, hash.size
hash.each_pair { |name, value| hash.each_pair { |name, value|
_, pvalue = pairs.assoc(name) _, pvalue = pairs.assoc(name)