Invalid cookie value error in Kemal. How to fix?

Hello,

The code shown at the bottom of this post reads the cookie from a request. Normally, this works without any issues. However, if I run jupyter notebooks (which are related to python and run in the browser), they add this weird cookie to my browser: "2|1:0|10:1624367022|23:username-localhost-8889|44:ZTIxNDRmNzhkNjRlNGI1Yjk0MTdjY2FiYmUyMDg5YTM=|708662efba157615901904f79ba918ff49a07c23b7cd973872bde7ecbfe5174b" under the name username-localhost-8889.
When this cookie is present and I try to run my Kemal app on the localhost, I get the following error:

##### Error 500 <small>at GET /</small>

# Invalid cookie value

If I delete this cookie, things work well again. I suppose, I don’t have to worry about this problem in production because no client should have a weird cookie associated with my domain name, unless I put it there (am I right about this?). Nevertheless, I thought I will point this out in case the cookie parsing in Kemal can be fixed.

Thank you.

class AuthHandlerBrowserReq < Kemal::Handler
    ## add all routes here that require authentication and are accessed from
    ## browser (that is not using ajax)
    only ["/"]

    def call(env)
        if only_match?(env)
            puts "cookies: #{env.request.cookies}"
            authcookie = env.request.cookies["authcookie"]?.try {|cookie| cookie.value }
            puts "authcookie: #{authcookie}"
            ## more lines here...

Can you post the exact Cookie header sent by the browser, please?

Hi @straight-shoota,

Here is the output I get from puts #{env.request.headers}. Thank you for looking into this. The username-localhost-8888 cookie is the problematic one.

HTTP::Headers{"Host" => "localhost:3000", "Connection" => "keep-alive", "Upgrade-Insecure-Requests" => "1", "User-Agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36", "Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Sec-GPC" => "1", "Sec-Fetch-Site" => "none", "Sec-Fetch-Mode" => "navigate", "Sec-Fetch-User" => "?1", "Sec-Fetch-Dest" => "document", "Accept-Encoding" => "gzip, deflate, br", "Accept-Language" => "en-US,en;q=0.9", "Cookie" => "hicookie=iamatesthicookie; _xsrf=2|155b9748|adfc7cc3950b0750d20b414e8b4123f7|1624367005; authcookie=83d4fa5c0923329a236ff9803c239162409f6515a727c4a1310de0aa8d06c414; username-localhost-8888=\"2|1:0|10:1624652864|23:username-localhost-8888|44:MWE4YTIyM2JhNzMzNDIxMWI0Yzg3NmIzNzE2ZDZiYjg=|68467aba98ceb1f58ad04270427459bd23ebabb337efa3d2e13d0c2d06dae081\""}

The cookie value contains actual double quote characters. That’s invalid according to RFC 6265.
So the parser is actually working per specification. The data is just corrupt.

Question would be if it should be that strict or if we can offer options to be more lenient, either allow such values despite the specification, or just skip parsing such an invalid cookie value, yet allow the request to continue.
(I was wrong: Invalid cookie value error in Kemal. How to fix? - #8 by straight-shoota)

It would be particularly interesting to see how other HTTP frameworks handle this.

I feel that it would be better to skip parsing, rather than crashing the application … just ignore this cookie. Especially because it is not something that is in control of a developer. For any reason if a user has a cookie like this in their cookies, set by another application, the Crystal/Kemal application that has no errors should not crash.

Thanks for looking into it.

I tried this out with Symfony where the controller just does like return $request->cookies->all(); and apparently they’re ok with it.

curl --location --request GET 'localhost:8001/cookies' \
--header 'Cookie: username-localhost-8889="2|1:0|10:1624367022|23:username-localhost-8889|44:ZTIxNDRmNzhkNjRlNGI1Yjk0MTdjY2FiYmUyMDg5YTM=|708662efba157615901904f79ba918ff49a07c23b7cd973872bde7ecbfe5174b"'

Returns:

{
    "username-localhost-8889": "\"2|1:0|10:1624367022|23:username-localhost-8889|44:ZTIxNDRmNzhkNjRlNGI1Yjk0MTdjY2FiYmUyMDg5YTM=|708662efba157615901904f79ba918ff49a07c23b7cd973872bde7ecbfe5174b\""
}

No wait, I was totally wrong :man_facepalming: The cookie value is legit. It can be wrapped in double quotes.

HTTP::Cookies::Parser correctly recognizes the quoted value, but it doesn’t dequote when creating the HTTP::Cookie instance.

Fix in Fix `HTTP::Cookie` parse quoted cookie value by straight-shoota · Pull Request #10853 · crystal-lang/crystal · GitHub

1 Like

Great to see this being fixed so quickly. Amazing!! Thank you.

So actually, while white space is invalid in cookie values according to RFC 6265, some implementations seem to support that when the value is wrapped in double quotes (take Go for example: src/net/http/cookie_test.go - The Go Programming Language).
I think we might want to do the same. It’s harmless, easy to implement and improves compatibility with HTTP applications out in the wild.

2 Likes