Add ! to allowed cookie value chars

Motivation:

! is missing from allowed cookie value chars, as per https://tools.ietf.org/html/rfc6265#section-4.1.1.
Issue was originally reported on Play!, see https://github.com/playframework/playframework/issues/4460#issuecomment-198177302.

Modifications:

Stick to RFC6265 ranges.

Result:

RFC6265 compliance, ! is supported
This commit is contained in:
Stephane Landelle 2016-03-18 09:23:24 +01:00 committed by Norman Maurer
parent 0320ccb59f
commit d747438366
2 changed files with 59 additions and 31 deletions

View File

@ -24,19 +24,25 @@ final class CookieUtil {
private static final BitSet VALID_COOKIE_VALUE_OCTETS = validCookieValueOctets(); private static final BitSet VALID_COOKIE_VALUE_OCTETS = validCookieValueOctets();
private static final BitSet VALID_COOKIE_NAME_OCTETS = validCookieNameOctets(VALID_COOKIE_VALUE_OCTETS); private static final BitSet VALID_COOKIE_NAME_OCTETS = validCookieNameOctets();
// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
// US-ASCII characters excluding CTLs, whitespace, DQUOTE, comma, semicolon, and backslash // US-ASCII characters excluding CTLs, whitespace, DQUOTE, comma, semicolon, and backslash
private static BitSet validCookieValueOctets() { private static BitSet validCookieValueOctets() {
BitSet bits = new BitSet(8); BitSet bits = new BitSet();
for (int i = 35; i < 127; i++) { bits.set(0x21);
// US-ASCII characters excluding CTLs (%x00-1F / %x7F) for (int i = 0x23; i <= 0x2B; i++) {
bits.set(i);
}
for (int i = 0x2D; i <= 0x3A; i++) {
bits.set(i);
}
for (int i = 0x3C; i <= 0x5B; i++) {
bits.set(i);
}
for (int i = 0x5D; i <= 0x7E; i++) {
bits.set(i); bits.set(i);
} }
bits.set('"', false); // exclude DQUOTE = %x22
bits.set(',', false); // exclude comma = %x2C
bits.set(';', false); // exclude semicolon = %x3B
bits.set('\\', false); // exclude backslash = %x5C
return bits; return bits;
} }
@ -45,24 +51,16 @@ final class CookieUtil {
// | "," | ";" | ":" | "\" | <"> // | "," | ";" | ":" | "\" | <">
// | "/" | "[" | "]" | "?" | "=" // | "/" | "[" | "]" | "?" | "="
// | "{" | "}" | SP | HT // | "{" | "}" | SP | HT
private static BitSet validCookieNameOctets(BitSet validCookieValueOctets) { private static BitSet validCookieNameOctets() {
BitSet bits = new BitSet(8); BitSet bits = new BitSet();
bits.or(validCookieValueOctets); for (int i = 32; i < 127; i++) {
bits.set('(', false); bits.set(i);
bits.set(')', false); }
bits.set('<', false); int[] separators = new int[]
bits.set('>', false); { '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t' };
bits.set('@', false); for (int separator : separators) {
bits.set(':', false); bits.set(separator, false);
bits.set('/', false); }
bits.set('[', false);
bits.set(']', false);
bits.set('?', false);
bits.set('=', false);
bits.set('{', false);
bits.set('}', false);
bits.set(' ', false);
bits.set('\t', false);
return bits; return bits;
} }

View File

@ -24,7 +24,9 @@ import io.netty.handler.codec.http.HttpHeaderDateFormat;
import java.text.ParseException; import java.text.ParseException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -76,6 +78,34 @@ public class ServerCookieEncoderTest {
assertEquals(result, encodedCookies); assertEquals(result, encodedCookies);
} }
@Test
public void illegalCharInCookieValueMakesStrictEncoderThrowsException() {
Set<Character> illegalChars = new HashSet<Character>();
// CTLs
for (int i = 0x00; i <= 0x1F; i++) {
illegalChars.add((char) i);
}
illegalChars.add((char) 0x7F);
// whitespace, DQUOTE, comma, semicolon, and backslash
for (char c : new char[] { ' ', '"', ',', ';', '\\' }) {
illegalChars.add(c);
}
int exceptions = 0;
for (char c : illegalChars) {
Cookie cookie = new DefaultCookie("name", "value" + c);
try {
ServerCookieEncoder.STRICT.encode(cookie);
} catch (IllegalArgumentException e) {
exceptions++;
}
}
assertEquals(illegalChars.size(), exceptions);
}
@Test @Test
public void testEncodingMultipleCookiesLax() { public void testEncodingMultipleCookiesLax() {
List<String> result = new ArrayList<String>(); List<String> result = new ArrayList<String>();