fix cookie parsing problem

This commit is contained in:
Bill Burke 2014-08-18 18:36:36 -04:00
parent 2dc32fb8c9
commit faf18dfdfd
5 changed files with 300 additions and 347 deletions

View file

@ -173,7 +173,7 @@ public class AuthenticationManager {
public AuthResult authenticateIdentityCookie(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers, boolean checkActive) { public AuthResult authenticateIdentityCookie(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers, boolean checkActive) {
logger.info("authenticateIdentityCookie"); logger.info("authenticateIdentityCookie");
Cookie cookie = headers.getCookies().get(KEYCLOAK_IDENTITY_COOKIE); Cookie cookie = headers.getCookies().get(KEYCLOAK_IDENTITY_COOKIE);
if (cookie == null) { if (cookie == null || "".equals(cookie.getValue())) {
logger.infov("authenticateCookie could not find cookie: {0}", KEYCLOAK_IDENTITY_COOKIE); logger.infov("authenticateCookie could not find cookie: {0}", KEYCLOAK_IDENTITY_COOKIE);
return null; return null;
} }

View file

@ -96,7 +96,10 @@ public class OAuthFlows {
Cookie sessionCookie = request.getHttpHeaders().getCookies().get(AuthenticationManager.KEYCLOAK_SESSION_COOKIE); Cookie sessionCookie = request.getHttpHeaders().getCookies().get(AuthenticationManager.KEYCLOAK_SESSION_COOKIE);
if (sessionCookie != null) { if (sessionCookie != null) {
String oldSessionId = sessionCookie.getValue().split("/")[2];
String[] split = sessionCookie.getValue().split("/");
if (split.length >= 3) {
String oldSessionId = split[2];
if (!oldSessionId.equals(userSession.getId())) { if (!oldSessionId.equals(userSession.getId())) {
UserSessionModel oldSession = session.sessions().getUserSession(realm, oldSessionId); UserSessionModel oldSession = session.sessions().getUserSession(realm, oldSessionId);
if (oldSession != null) { if (oldSession != null) {
@ -105,6 +108,7 @@ public class OAuthFlows {
} }
} }
} }
}
// refresh the cookies! // refresh the cookies!
authManager.createLoginCookie(realm, accessCode.getUser(), userSession, uriInfo, clientConnection); authManager.createLoginCookie(realm, accessCode.getUser(), userSession, uriInfo, clientConnection);

View file

@ -29,7 +29,7 @@ public class CookieHelper {
* @param secure * @param secure
* @param httpOnly * @param httpOnly
*/ */
public static void addCookie2(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly) { public static void addCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly) {
HttpResponse response = ResteasyProviderFactory.getContextData(HttpResponse.class); HttpResponse response = ResteasyProviderFactory.getContextData(HttpResponse.class);
StringBuffer cookieBuf = new StringBuffer(); StringBuffer cookieBuf = new StringBuffer();
ServerCookie.appendCookieValue(cookieBuf, 1, name, value, path, domain, comment, maxAge, secure, httpOnly); ServerCookie.appendCookieValue(cookieBuf, 1, name, value, path, domain, comment, maxAge, secure, httpOnly);
@ -37,19 +37,5 @@ public class CookieHelper {
response.getOutputHeaders().add(HttpHeaders.SET_COOKIE, cookie); response.getOutputHeaders().add(HttpHeaders.SET_COOKIE, cookie);
} }
public static void addCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly) {
HttpServletResponse response = ResteasyProviderFactory.getContextData(HttpServletResponse.class);
Cookie cookie = new Cookie(name, value);
if (path != null) cookie.setPath(path);
if (domain != null) cookie.setDomain(domain);
if (comment != null) cookie.setComment(comment);
cookie.setMaxAge(maxAge);
cookie.setSecure(secure);
cookie.setHttpOnly(httpOnly);
response.addCookie(cookie);
}
} }

View file

@ -11,8 +11,7 @@ import java.util.TimeZone;
/** /**
* Server-side cookie representation. borrowed from Tomcat. * Server-side cookie representation. borrowed from Tomcat.
*/ */
public class ServerCookie implements Serializable public class ServerCookie implements Serializable {
{
private static final String tspecials = ",; "; private static final String tspecials = ",; ";
private static final String tspecials2 = "()<>@,;:\\\"/[]?={} \t"; private static final String tspecials2 = "()<>@,;:\\\"/[]?={} \t";
@ -25,13 +24,11 @@ public class ServerCookie implements Serializable
* @return <code>true</code> if the <code>String</code> is a reserved * @return <code>true</code> if the <code>String</code> is a reserved
* token; <code>false</code> if it is not * token; <code>false</code> if it is not
*/ */
public static boolean isToken(String value) public static boolean isToken(String value) {
{
if (value == null) return true; if (value == null) return true;
int len = value.length(); int len = value.length();
for (int i = 0; i < len; i++) for (int i = 0; i < len; i++) {
{
char c = value.charAt(i); char c = value.charAt(i);
if (tspecials.indexOf(c) != -1) if (tspecials.indexOf(c) != -1)
@ -40,15 +37,12 @@ public class ServerCookie implements Serializable
return true; return true;
} }
public static boolean containsCTL(String value, int version) public static boolean containsCTL(String value, int version) {
{
if (value == null) return false; if (value == null) return false;
int len = value.length(); int len = value.length();
for (int i = 0; i < len; i++) for (int i = 0; i < len; i++) {
{
char c = value.charAt(i); char c = value.charAt(i);
if (c < 0x20 || c >= 0x7f) if (c < 0x20 || c >= 0x7f) {
{
if (c == 0x09) if (c == 0x09)
continue; //allow horizontal tabs continue; //allow horizontal tabs
return true; return true;
@ -58,13 +52,11 @@ public class ServerCookie implements Serializable
} }
public static boolean isToken2(String value) public static boolean isToken2(String value) {
{
if (value == null) return true; if (value == null) return true;
int len = value.length(); int len = value.length();
for (int i = 0; i < len; i++) for (int i = 0; i < len; i++) {
{
char c = value.charAt(i); char c = value.charAt(i);
if (tspecials2.indexOf(c) != -1) if (tspecials2.indexOf(c) != -1)
return false; return false;
@ -75,8 +67,7 @@ public class ServerCookie implements Serializable
/** /**
* @deprecated - Not used * @deprecated - Not used
*/ */
public static boolean checkName(String name) public static boolean checkName(String name) {
{
if (!isToken(name) if (!isToken(name)
|| name.equalsIgnoreCase("Comment") // rfc2019 || name.equalsIgnoreCase("Comment") // rfc2019
|| name.equalsIgnoreCase("Discard") // rfc2965 || name.equalsIgnoreCase("Discard") // rfc2965
@ -87,8 +78,7 @@ public class ServerCookie implements Serializable
|| name.equalsIgnoreCase("Secure") // rfc2019 || name.equalsIgnoreCase("Secure") // rfc2019
|| name.equalsIgnoreCase("Version") // rfc2019 || name.equalsIgnoreCase("Version") // rfc2019
// TODO remaining RFC2965 attributes // TODO remaining RFC2965 attributes
) ) {
{
return false; return false;
} }
return true; return true;
@ -100,12 +90,10 @@ public class ServerCookie implements Serializable
/** /**
* Return the header name to set the cookie, based on cookie version. * Return the header name to set the cookie, based on cookie version.
*/ */
public static String getCookieHeaderName(int version) public static String getCookieHeaderName(int version) {
{
// TODO Re-enable logging when RFC2965 is implemented // TODO Re-enable logging when RFC2965 is implemented
// log( (version==1) ? "Set-Cookie2" : "Set-Cookie"); // log( (version==1) ? "Set-Cookie2" : "Set-Cookie");
if (version == 1) if (version == 1) {
{
// XXX RFC2965 not referenced in Servlet Spec // XXX RFC2965 not referenced in Servlet Spec
// Set-Cookie2 is not supported by Netscape 4, 6, IE 3, 5 // Set-Cookie2 is not supported by Netscape 4, 6, IE 3, 5
// Set-Cookie2 is supported by Lynx and Opera // Set-Cookie2 is supported by Lynx and Opera
@ -113,9 +101,7 @@ public class ServerCookie implements Serializable
// RFC2109 // RFC2109
return "Set-Cookie"; return "Set-Cookie";
// return "Set-Cookie2"; // return "Set-Cookie2";
} } else {
else
{
// Old Netscape // Old Netscape
return "Set-Cookie"; return "Set-Cookie";
} }
@ -138,21 +124,17 @@ public class ServerCookie implements Serializable
private final static DateFormat oldCookieFormat = new SimpleDateFormat(OLD_COOKIE_PATTERN, LOCALE_US); private final static DateFormat oldCookieFormat = new SimpleDateFormat(OLD_COOKIE_PATTERN, LOCALE_US);
public static String formatOldCookie(Date d) public static String formatOldCookie(Date d) {
{
String ocf = null; String ocf = null;
synchronized (oldCookieFormat) synchronized (oldCookieFormat) {
{
ocf = oldCookieFormat.format(d); ocf = oldCookieFormat.format(d);
} }
return ocf; return ocf;
} }
public static void formatOldCookie(Date d, StringBuffer sb, public static void formatOldCookie(Date d, StringBuffer sb,
FieldPosition fp) FieldPosition fp) {
{ synchronized (oldCookieFormat) {
synchronized (oldCookieFormat)
{
oldCookieFormat.format(d, sb, fp); oldCookieFormat.format(d, sb, fp);
} }
} }
@ -171,41 +153,40 @@ public class ServerCookie implements Serializable
String comment, String comment,
int maxAge, int maxAge,
boolean isSecure, boolean isSecure,
boolean httpOnly) boolean httpOnly) {
{
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
// Servlet implementation checks name // Servlet implementation checks name
buf.append(name); buf.append(name);
buf.append("="); buf.append("=");
// Servlet implementation does not check anything else // Servlet implementation does not check anything else
maybeQuote2(version, buf, value); // NOTE!!! BROWSERS REALLY DON'T LIKE QUOTING
//maybeQuote2(version, buf, value);
buf.append(value);
// Add version 1 specific information // Add version 1 specific information
if (version == 1) if (version == 1) {
{
// Version=1 ... required // Version=1 ... required
buf.append("; Version=1"); buf.append("; Version=1");
// Comment=comment // Comment=comment
if (comment != null) if (comment != null) {
{
buf.append("; Comment="); buf.append("; Comment=");
maybeQuote2(version, buf, comment); //maybeQuote2(version, buf, comment);
buf.append(comment);
} }
} }
// Add domain information, if present // Add domain information, if present
if (domain != null) if (domain != null) {
{
buf.append("; Domain="); buf.append("; Domain=");
maybeQuote2(version, buf, domain); //maybeQuote2(version, buf, domain);
buf.append(domain);
} }
// Max-Age=secs ... or use old "Expires" format // Max-Age=secs ... or use old "Expires" format
// TODO RFC2965 Discard // TODO RFC2965 Discard
if (maxAge >= 0) if (maxAge >= 0) {
{
// Wdy, DD-Mon-YY HH:MM:SS GMT ( Expires Netscape format ) // Wdy, DD-Mon-YY HH:MM:SS GMT ( Expires Netscape format )
buf.append("; Expires="); buf.append("; Expires=");
// To expire immediately we need to set the time in past // To expire immediately we need to set the time in past
@ -222,21 +203,18 @@ public class ServerCookie implements Serializable
} }
// Path=path // Path=path
if (path != null) if (path != null) {
{
buf.append("; Path="); buf.append("; Path=");
buf.append(path); buf.append(path);
} }
// Secure // Secure
if (isSecure) if (isSecure) {
{
buf.append("; Secure"); buf.append("; Secure");
} }
// HttpOnly // HttpOnly
if (httpOnly) if (httpOnly) {
{
buf.append("; HttpOnly"); buf.append("; HttpOnly");
} }
@ -247,23 +225,18 @@ public class ServerCookie implements Serializable
* @deprecated - Not used * @deprecated - Not used
*/ */
@Deprecated @Deprecated
public static void maybeQuote(int version, StringBuffer buf, String value) public static void maybeQuote(int version, StringBuffer buf, String value) {
{
// special case - a \n or \r shouldn't happen in any case // special case - a \n or \r shouldn't happen in any case
if (isToken(value)) if (isToken(value)) {
{
buf.append(value); buf.append(value);
} } else {
else
{
buf.append('"'); buf.append('"');
buf.append(escapeDoubleQuotes(value, 0, value.length())); buf.append(escapeDoubleQuotes(value, 0, value.length()));
buf.append('"'); buf.append('"');
} }
} }
public static boolean alreadyQuoted(String value) public static boolean alreadyQuoted(String value) {
{
if (value == null || value.length() == 0) return false; if (value == null || value.length() == 0) return false;
return (value.charAt(0) == '\"' && value.charAt(value.length() - 1) == '\"'); return (value.charAt(0) == '\"' && value.charAt(value.length() - 1) == '\"');
} }
@ -275,34 +248,24 @@ public class ServerCookie implements Serializable
* @param buf * @param buf
* @param value * @param value
*/ */
public static void maybeQuote2(int version, StringBuffer buf, String value) public static void maybeQuote2(int version, StringBuffer buf, String value) {
{ if (value == null || value.length() == 0) {
if (value == null || value.length() == 0)
{
buf.append("\"\""); buf.append("\"\"");
} } else if (containsCTL(value, version))
else if (containsCTL(value, version))
throw new IllegalArgumentException("Control character in cookie value, consider BASE64 encoding your value"); throw new IllegalArgumentException("Control character in cookie value, consider BASE64 encoding your value");
else if (alreadyQuoted(value)) else if (alreadyQuoted(value)) {
{
buf.append('"'); buf.append('"');
buf.append(escapeDoubleQuotes(value, 1, value.length() - 1)); buf.append(escapeDoubleQuotes(value, 1, value.length() - 1));
buf.append('"'); buf.append('"');
} } else if (version == 0 && !isToken(value)) {
else if (version == 0 && !isToken(value))
{
buf.append('"'); buf.append('"');
buf.append(escapeDoubleQuotes(value, 0, value.length())); buf.append(escapeDoubleQuotes(value, 0, value.length()));
buf.append('"'); buf.append('"');
} } else if (version == 1 && !isToken2(value)) {
else if (version == 1 && !isToken2(value))
{
buf.append('"'); buf.append('"');
buf.append(escapeDoubleQuotes(value, 0, value.length())); buf.append(escapeDoubleQuotes(value, 0, value.length()));
buf.append('"'); buf.append('"');
} } else {
else
{
buf.append(value); buf.append(value);
} }
} }
@ -316,26 +279,21 @@ public class ServerCookie implements Serializable
* @param endIndex exclusive * @param endIndex exclusive
* @return The (possibly) escaped string * @return The (possibly) escaped string
*/ */
private static String escapeDoubleQuotes(String s, int beginIndex, int endIndex) private static String escapeDoubleQuotes(String s, int beginIndex, int endIndex) {
{
if (s == null || s.length() == 0 || s.indexOf('"') == -1) if (s == null || s.length() == 0 || s.indexOf('"') == -1) {
{
return s; return s;
} }
StringBuffer b = new StringBuffer(); StringBuffer b = new StringBuffer();
for (int i = beginIndex; i < endIndex; i++) for (int i = beginIndex; i < endIndex; i++) {
{
char c = s.charAt(i); char c = s.charAt(i);
if (c == '\\') if (c == '\\') {
{
b.append(c); b.append(c);
//ignore the character after an escape, just append it //ignore the character after an escape, just append it
if (++i >= endIndex) throw new IllegalArgumentException("Invalid escape character in cookie value."); if (++i >= endIndex) throw new IllegalArgumentException("Invalid escape character in cookie value.");
b.append(s.charAt(i)); b.append(s.charAt(i));
} } else if (c == '"')
else if (c == '"')
b.append('\\').append('"'); b.append('\\').append('"');
else else
b.append(c); b.append(c);

View file

@ -180,6 +180,11 @@ public class AccessTokenPerfTest {
} }
Assert.assertEquals(302, response.getStatus()); Assert.assertEquals(302, response.getStatus());
uri = response.getLocation(); uri = response.getLocation();
for (String header : response.getHeaders().keySet()) {
for (Object value : response.getHeaders().get(header)) {
System.out.println(header + ": " + value);
}
}
response.close(); response.close();
Assert.assertNotNull(uri); Assert.assertNotNull(uri);