Re: [http-state] Is this an omission in the parser rules of draft-ietf-httpstate-cookie-21?

"Remy Lebeau" <remy@lebeausoftware.org> Sat, 05 February 2011 07:18 UTC

Return-Path: <remy@lebeausoftware.org>
X-Original-To: http-state@core3.amsl.com
Delivered-To: http-state@core3.amsl.com
Received: from localhost (localhost [127.0.0.1]) by core3.amsl.com (Postfix) with ESMTP id 596023A6808 for <http-state@core3.amsl.com>; Fri, 4 Feb 2011 23:18:25 -0800 (PST)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -1.428
X-Spam-Level:
X-Spam-Status: No, score=-1.428 tagged_above=-999 required=5 tests=[AWL=1.019, BAYES_00=-2.599, SARE_SUB_ENC_UTF8=0.152]
Received: from mail.ietf.org ([64.170.98.32]) by localhost (core3.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id WYnFVLHlu68s for <http-state@core3.amsl.com>; Fri, 4 Feb 2011 23:18:24 -0800 (PST)
Received: from smtpoutwbe11.prod.mesa1.secureserver.net (smtpoutwbe11.prod.mesa1.secureserver.net [208.109.78.27]) by core3.amsl.com (Postfix) with SMTP id 10EDE3A67A2 for <http-state@ietf.org>; Fri, 4 Feb 2011 23:18:23 -0800 (PST)
Received: (qmail 20185 invoked from network); 5 Feb 2011 07:21:49 -0000
Received: from unknown (HELO gem-wbe27.prod.mesa1.secureserver.net) (64.202.189.161) by smtpoutwbe11.prod.mesa1.secureserver.net with SMTP; 5 Feb 2011 07:21:49 -0000
Received: (qmail 21535 invoked by uid 99); 5 Feb 2011 07:21:49 -0000
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset="utf-8"
X-Originating-IP: 76.93.119.83
User-Agent: Web-Based Email 5.3.08
Message-Id: <20110205002149.f00013ceab8fb1928885c5c172fbfd4a.147756da58.wbe@email00.secureserver.net>
From: "Remy Lebeau" <remy@lebeausoftware.org>
To: "Adam Barth" <ietf@adambarth.com>
Date: Sat, 05 Feb 2011 00:21:49 -0700
Mime-Version: 1.0
Cc: http-state@ietf.org
Subject: Re: [http-state] =?utf-8?q?Is_this_an_omission_in_the_parser_rules_of?= =?utf-8?q?_draft-ietf-httpstate-cookie-21=3F?=
X-BeenThere: http-state@ietf.org
X-Mailman-Version: 2.1.9
Precedence: list
List-Id: Discuss HTTP State Management Mechanism <http-state.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/listinfo/http-state>, <mailto:http-state-request@ietf.org?subject=unsubscribe>
List-Archive: <http://www.ietf.org/mail-archive/web/http-state>
List-Post: <mailto:http-state@ietf.org>
List-Help: <mailto:http-state-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/http-state>, <mailto:http-state-request@ietf.org?subject=subscribe>
X-List-Received-Date: Sat, 05 Feb 2011 07:18:25 -0000

-------- Original Message --------
Subject: Re: [http-state] Is this an omission in the parser rules of
draft-ietf-httpstate-cookie-21?
From: Adam Barth <ietf@adambarth.com>;
Date: Fri, February 04, 2011 4:27 pm
To: Remy Lebeau <remy@lebeausoftware.org>;
Cc: http-state@ietf.org

> Section 5 does not use token at all, much less exclusively.

It does, however, outline the rules for extracting the individual string
values of the various components, and then how to process their contents
once all the grammar has been stripped away.  The only parsing rule that
explicitally allows for quoted-string values is Date handling, because a
quotation mark is included in the list of defined leading/trailing
delimiters.  No other rule allows for the presence of quotation marks.

> Can you provide an example of a Set-Cookie header that
> you believe is processed incorrectly by Section 5?

The examples in RFCs 2109 and 2965, for starters.  Let's look at the
first example:

  Set-Cookie: Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"

Which is issued by a request to "http://<host>/acme/login".

Here is a step-by-step breakdown of how the above cookie gets processed
by Section 5.2 (I will use parenthesis around values so you can see
where whitespace charcters exist).  I believe a failure occurs in the
processing of the "Path" attribute by Section 5.2.4:

--- begin ---
A user agent MUST use an algorithm equivalent to the following algorithm
to parse a "set-cookie-string":

1.  If the set-cookie-string contains a %x3B (";") character:

      The name-value-pair string consists of the characters up to, but
not including, the first %x3B (";"), and the unparsed-attributes consist
of the remainder of the set-cookie-string (including the %x3B (";") in
question).

    Otherwise:

      The name-value-pair string consists of all the characters
contained in the set-cookie-string, and the unparsed-attributes is the
empty string.

  name-value-pair = (Customer="WILE_E_COYOTE")
  unparsed-attributes = (; Version="1"; Path="/acme")

2.  If the name-value-pair string lacks a %x3D ("=") character, ignore
the set-cookie-string entirely.

3.  The (possibly empty) name string consists of the characters up to,
but not including, the first %x3D ("=") character, and the (possibly
empty) value string consists of the characters after the first %x3D
("=") character.

  name = (Customer)
  value = ("WILE_E_COYOTE")

4.  Remove any leading or trailing WSP characters from the name string
and the value string.

  name = (Customer)
  value = ("WILE_E_COYOTE")

5.  If the name string is empty, ignore the set-cookie-string entirely.

6.  The cookie-name is the name string, and the cookie-value is the
value string.

  cookie-name = (Customer)
  cookie-value = ("WILE_E_COYOTE")

The user agent MUST use an algorithm equivalent to the following
algorithm to parse the unparsed-attributes:

1.  If the unparsed-attributes string is empty, skip the rest of these
steps.

2.  Discard the first character of the unparsed-attributes (which will
be a %x3B (";") character).

  unparsed-attributes = ( Version="1"; Path="/acme")

3.  If the remaining unparsed-attributes contains a %x3B (";")
character:

      Consume the characters of the unparsed-attributes up to, but not
including, the first %x3B (";") character.

    Otherwise:

      Consume the remainder of the unparsed-attributes.
     
    Let the cookie-av string be the characters consumed in this step.

  unparsed-attributes = (; Path="/acme")
  cookie-av = ( Version="1")

4.  If the cookie-av string contains a %x3D ("=") character:

      The (possibly empty) attribute-name string consists of the
characters up to, but not including, the first %x3D ("=") character, and
the (possibly empty) attribute-value string consists of the characters
after the first %x3D ("=") character.

  attribute-name = ( Version)
  attribute-value = ("1")

5.  Remove any leading or trailing WSP characters from the
attribute-name string and the attribute-value string.

  attribute-name = (Version)
  attribute-value = ("1")

6.  Process the attribute-name and attribute-value according to the
requirements in the following subsections.  (Notice that attributes with
unrecognized attribute-names are ignored.)

7.  Return to Step 1 of this algorithm.

1.  If the unparsed-attributes string is empty, skip the rest of these
steps.

2.  Discard the first character of the unparsed-attributes (which will
be a %x3B (";") character).

  unparsed-attributes = ( Path="/acme")

3.  If the remaining unparsed-attributes contains a %x3B (";")
character:

      Consume the characters of the unparsed-attributes up to, but not
including, the first %x3B (";") character.

    Otherwise:

      Consume the remainder of the unparsed-attributes.

    Let the cookie-av string be the characters consumed in this step.

  unparsed-attributes = ()
  cookie-av = ( Path="/acme")

4.  If the cookie-av string contains a %x3D ("=") character:

      The (possibly empty) attribute-name string consists of the
characters up to, but not including, the first %x3D ("=") character, and
the (possibly empty) attribute-value string consists of the characters
after the first %x3D ("=") character.

    Otherwise:

       The attribute-name string consists of the entire cookie-av
string, and the attribute-value string is empty.

  attribute-name = ( Path)
  attribute-value = ("/acme")

5.  Remove any leading or trailing WSP characters from the
attribute-name string and the attribute-value string.

  attribute-name = (Path)
  attribute-value = ("/acme")

6.  Process the attribute-name and attribute-value according to the
requirements in the following subsections.  (Notice that attributes with
unrecognized attribute-names are ignored.)

    If the attribute-name case-insensitively matches the string "Path",
the user agent MUST process the cookie-av as follows.

    If the attribute-value is empty or if the first character of the
attribute-value is not %x2F ("/"):

      Let cookie-path be the default-path.

      The user agent MUST use an algorithm equivalent to the following
algorithm to compute the default-path of a cookie:

      1.  Let uri-path be the path portion of the request-uri if such a
portion exists (and empty otherwise).  For example, if the request-uri
contains just a path (and optional query string), then the uri-path is
that path (without the %x3F ("?") character or query string), and if the
request-uri contains a full absoluteURI, the uri-path is the path
component of that URI.

        uri-path = (/acme/login)

      2.  If the uri-path is empty or if the first character of the
uri-path is not a %x2F ("/") character, output %x2F ("/") and skip the
remaining steps.

      3.  If the uri-path contains only a single %x2F ("/") character,
output %x2F ("/") and skip the remaining steps.

      4.  Output the characters of the uri-path from the first character
up to, but not including, the right-most %x2F ("/").

        default-path = (/acme)

    Otherwise:

      Let cookie-path be the attribute-value.

    Append an attribute to the cookie-attribute-list with an
attribute-name of Path and an attribute-value of cookie-path.

  cookie-path = default-path
  cookie-attribute-list = (Path=default-path)

7.  Return to Step 1 of this algorithm.

1.  If the unparsed-attributes string is empty, skip the rest of these
steps.
-- end ---

I believe the cookie-path should have been set to attribute-value
instead of default-path, but quotation marks were not stripped out
during grammar parsing, so the condition "if the first character of the
attribute-value is not %x2F" is satisfied when it should not be.

In this example, the default-path of the request-uri happens to be
"/acme", which is the same value as the cookie's original "Path"
attribute, so the cookie's behavior would be OK.  But what would happen
if a cookie specifies a path on a different level of the request-uri's
folder hierarchy?  Section 5.2.4 would store the wrong path in the
cookie storage due to this shortcoming in Section 5.2's parsing rules.

A more fatal failure exists in Section 5.2.3, and it is caused by the
same parsing logic error in Section 5.2.

Had the cookie specified a quoted "Domain" attribute value, eg:

  Set-Cookie: Customer="WILE_E_COYOTE"; Version="1"; Domain="<host>";
Path="/acme"

The "If the first character of the attribute-value string is %x2E"
condition of Section 5.2.3 would not be satisfied as the first character
would be %x22 ("), so the cookie-domain would end up being set to the
complete quoted attribute value.  Future domain matching for that cookie
would then always fail, as the quotes would falsify every test condition
in Section 5.1.3, so the cookie would never be able to be included in
future "Cookie" request headers.

RFCs 2109 and 2965 allow quoted-string in all value fields, and as such
should be handled at the Section 5.2 parsing layer, not the Section
5.2.x processing layers.  This would further benefit interoperability
with existing implementations.  It would also possibly eliminate the
need to explicitally include base64-string in the allowed grammar.  Any
server that outputs a base64 encoded cookie value that uses "=" padding
character(s) is likely to quote the value to conform with RFC 2109, as
quoted-string allows characters that are normally reserved by the
grammer.  base64 cookie values that do not use any padding do not
require quoting, but can still be quoted if the server wishes.

Does this make the issue clearer for you?