302 Redirect Handling in IE and Firefox

bholt's picture

An apparent defect in how IE handled our OAuth consent flow at work turned out to be an interesting difference in how Firefox and IE (Firefox 6 and IE8, in this case) handle content loaded via a 302 HTTP response.

After one of our UI developers beautified our OAuth consent page (where the user authorizes a client application to use data on his or her behalf), one of our testers pointed out that the overall flow stopped working in Internet Explorer. It still worked in Firefox, though, and she told us that if she permanently authorized the client in Firefox, the flow still worked in Internet Explorer.

We allow OAuth clients to provide an image that will be displayed when we ask users for consent. However, it’s not required, and so occasionally on the consent page the IMG tag would have an empty SRC attribute: <img src="" alt="client Logo"/>.

screen capture of OAuth consent page with broken image

When the SRC attribute is empty, the browser loads the page URI instead. (So, it’s a self-reference, like the . entry on a filesystem.) This is where the difference between Firefox and IE comes in.

When a user accesses our site, he or she needs an OAuth access token for the site to work, and so Spring issues a 302 redirect from whatever URI was originally being accessed to the consent page. For example, if the user requests / (the home page), they’ll be redirected to /consentToUseOfData?oauth_token={tokenId}. (In the background, Spring obtains an OAuth request token from the OAuth provider, and replaces {tokenId} with the request token.)

As a result, the URI for the page had two wrinkles. The original URI for the page being loaded was /, but this was temporarily redirected to /consentToUseOfData?oauth_token={tokenId}. When the self-referring IMG tag is encountered, IE uses the original URI while Firefox uses the redirected URI.

While everyone loves to hate on Internet Explorer, initially I didn’t disagree with their approach here. The HTTP spec says, Since the redirection might be altered on occasion, the client SHOULD continue to use the Request-URI for future requests. I thought the request triggered by the IMG should be considered a “future request,” meaning Internet Explorer’s behavior would be correct.

However, I did a little more digging. The HTML4 spec for IMG (The choice of HTML4 was admittedly arbitrary, since we’re using the essentially spec-less HTML5.) says the SRC attribute is a URI: Relative URIs are resolved to full URIs using a base URI. … For more information about base URIs, please consult the section on base URIs in the chapter on links.

The relevant section states,

12.4.1 Resolving relative URIs

User agents must calculate the base URI for resolving relative URIs according to [RFC1808], section 3. The following describes how [RFC1808] applies specifically to HTML.

User agents must calculate the base URI according to the following precedences (highest priority to lowest):

  1. The base URI is set by the BASE element.
  2. The base URI is given by meta data discovered during a protocol interaction, such as an HTTP header (see [RFC2616]).
  3. By default, the base URI is that of the current document. Not all HTML documents have a base URI (e.g., a valid HTML document may appear in an email and may not be designated by a URI). Such HTML documents are considered erroneous if they contain relative URIs and rely on a default base URI.

We define no BASE element, nor does the response contain a Content-Location header (per RFC2616, this is how HTTP defines a Base URI), so the following section of RFC1808 applies:

3.3. Base URL from the Retrieval URL

If no base URL is embedded and the document is not encapsulated within some other entity (e.g., the top level of a composite entity), then, if a URL was used to retrieve the base document, that URL shall be considered the base URL. Note that if the retrieval was the result of a redirected request, the last URL used (i.e., that which resulted in he actual retrieval of the document) is the base URL.[emphasis added]

So, as usual, Firefox appears to be following the relevant spec and Internet Explorer is not. This one is pretty esoteric, though, so I’m tempted to forgive them.

Ultimately, we resolved the problem by removing the IMG tag when no image is provided. The fact that it was loading a broken image was a defect itself, of course.

Detailed Deconstruction

Firefox HTTP Trace

  1. GET https://serverName/ HTTP/1.1 Host: serverName User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip, deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Connection: keep-alive Cookie: <snip> HTTP/1.1 302 Moved Temporarily Date: Thu, 01 Sep 2011 07:30:59 GMT Server: Apache-Coyote/1.1 Location: https://serverName/consentToUseOfData?oauth_token=3760ff95-fd5c-4dcc-870f-a9d9c7717509 Content-Length: 0 Set-Cookie: JSESSIONID=1891371460FB24565FECFDC3C598E06A; Path=/ Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/plain

    This is the initial request for the homepage. A key point is the Accept header, which is clearly requesting HTML content.

  2. GET https://serverName/consentToUseOfData?oauth_token=3760ff95-fd5c-4dcc-870f-a9d9c7717509 HTTP/1.1 Host: serverName User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip, deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Connection: keep-alive Cookie: <snip> HTTP/1.1 200 OK Date: Thu, 01 Sep 2011 07:31:01 GMT Server: Apache-Coyote/1.1 Pragma: no-cache Cache-Control: no-cache Cache-Control: no-store Expires: Mon, 01 Jan 2001 10:00:00 GMT Content-Type: text/html;charset=UTF-8 Content-Language: en-US Keep-Alive: timeout=5, max=99 Connection: Keep-Alive Transfer-Encoding: chunked <snip>

    The redirected request. Again, the Accept header is clearly requesting HTML content. Also, note the lack of a Referrer header.

  3. GET https://serverName/consentToUseOfData?oauth_token=3760ff95-fd5c-4dcc-870f-a9d9c7717509 HTTP/1.1 Host: serverName User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0 Accept: image/png,image/*;q=0.8,*/*;q=0.5 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip, deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Connection: keep-alive Referer: https://serverName/consentToUseOfData?oauth_token=3760ff95-fd5c-4dcc-870f-a9d9c7717509 Cookie: <snip> HTTP/1.1 200 OK Date: Thu, 01 Sep 2011 07:31:03 GMT Server: Apache-Coyote/1.1 Pragma: no-cache Cache-Control: no-cache Cache-Control: no-store Expires: Mon, 01 Jan 2001 10:00:00 GMT Content-Type: text/html;charset=UTF-8 Content-Language: en-US Keep-Alive: timeout=5, max=96 Connection: Keep-Alive Transfer-Encoding: chunked

    The second request for /consentToUseOfData, notice that Firefox is requesting an image. This was the first indication (after about an hour of debugging, disabling scripts, pouring over logs, etc.) that the broken image might be the cause of the problem. The Referrer header is present here, too, and refers to the second request. Finally, note that the OAuth token in the request is the same as the original OAuth token.

Internet Explorer HTTP Trace

  1. GET https://serverName/ HTTP/1.1 Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* Accept-Language: en-US User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E) Accept-Encoding: gzip, deflate Host: serverName Connection: Keep-Alive Cookie: <snip> HTTP/1.1 302 Moved Temporarily Date: Thu, 01 Sep 2011 06:05:48 GMT Server: Apache-Coyote/1.1 Location: https://serverName/consentToUseOfData?oauth_token=af1f9e74-2a6e-4aa5-9fcf-cc2d385cc3a3 Content-Length: 0 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/plain

    The initial request for the homepage. While Internet Explorer is not as requesting HTML as clearly as Firefox did, the Accept header here is typical of an IE page load.

  2. GET https://serverName/consentToUseOfData?oauth_token=af1f9e74-2a6e-4aa5-9fcf-cc2d385cc3a3 HTTP/1.1 Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* Accept-Language: en-US User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E) Accept-Encoding: gzip, deflate Host: serverName Connection: Keep-Alive Cookie: <snip> HTTP/1.1 200 OK Date: Thu, 01 Sep 2011 06:05:48 GMT Server: Apache-Coyote/1.1 Pragma: no-cache Cache-Control: no-cache Cache-Control: no-store Expires: Mon, 01 Jan 2001 10:00:00 GMT Content-Type: text/html;charset=UTF-8 Content-Language: en-US Keep-Alive: timeout=5, max=99 Connection: Keep-Alive Transfer-Encoding: chunked fd8 <snip>
  3. GET https://serverName/ HTTP/1.1 Accept: */* Referer: https://serverName/consentToUseOfData?oauth_token=af1f9e74-2a6e-4aa5-9fcf-cc2d385cc3a3 Accept-Language: en-US User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E) Accept-Encoding: gzip, deflate Host: serverName Connection: Keep-Alive Cookie: <snip> HTTP/1.1 302 Moved Temporarily Date: Thu, 01 Sep 2011 06:05:49 GMT Server: Apache-Coyote/1.1 Location: https://serverName/consentToUseOfData?oauth_token=e725bf13-9153-4f75-a412-b995de8aed99 Content-Length: 0 Keep-Alive: timeout=5, max=98 Connection: Keep-Alive Content-Type: text/plain

    The second request, but Internet Explorer requests the original URI, not the redirected URI. The Accept header isn’t as specific as it was before, an indication that this request came from an IMG tag. Notice too that the OAuth token in the response is different from the OAuth token in the request’s Referrer.

    Because this request didn’t include an OAuth token parameter, Spring’s OAuth client obtained a new request token from the OAuth provider and stored that new token in the user’s session. That means that when the user submits their approval using the HTML form from the first /consentToUseOfData, there is a mismatch between the submitted token value and the value in the user’s session.

  4. GET https://serverName/consentToUseOfData?oauth_token=e725bf13-9153-4f75-a412-b995de8aed99 HTTP/1.1 Accept: */* Referer: https://serverName/consentToUseOfData?oauth_token=af1f9e74-2a6e-4aa5-9fcf-cc2d385cc3a3 Accept-Language: en-US User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E) Accept-Encoding: gzip, deflate Host: serverName Connection: Keep-Alive Cookie: <snip> HTTP/1.1 200 OK Date: Thu, 01 Sep 2011 06:05:49 GMT Server: Apache-Coyote/1.1 Pragma: no-cache Cache-Control: no-cache Cache-Control: no-store Expires: Mon, 01 Jan 2001 10:00:00 GMT Content-Type: text/html;charset=UTF-8 Content-Language: en-US Keep-Alive: timeout=5, max=97 Connection: Keep-Alive Content-Length: 8683 <snip>

    The second request for /consentToUseOfData, this time with a different OAuth token.

Comments

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Allowed HTML tags: <a> <abbr> <acronym> <em> <blockquote> <hr /> <img /> <strike> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <p> <div> <span> <sup> <sub> <h3> <h4> <h5> <h6>
  • Lines and paragraphs break automatically.
  • You may link to Gallery2 items on this site using a special syntax.
  • Image links with 'rel="lightbox"' in the <a> tag will appear in a Lightbox when clicked on.
  • You may quote other posts using [quote] tags.
  • You may insert videos with [video:URL]
  • Web page addresses and e-mail addresses turn into links automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.