Bug 159606

Summary: [GTK] Empty referer header after WebKit session state restoration trips Bad Behavior Wordpress plugin
Product: WebKit Reporter: Michael Catanzaro <mcatanzaro>
Component: WebCore Misc.Assignee: Michael Catanzaro <mcatanzaro>
Status: RESOLVED FIXED    
Severity: Normal CC: achristensen, ap, beidson, berto, bugs-noreply, buildbot, cdumez, cgarcia, commit-queue, dbates, gustavo, japhet, mcatanzaro, mrobinson, rniwa
Priority: P2    
Version: Other   
Hardware: PC   
OS: Linux   
Attachments:
Description Flags
Patch
none
Patch
none
Patch
none
Archive of layout-test-results from ews103 for mac-yosemite
none
Archive of layout-test-results from ews105 for mac-yosemite-wk2
none
Patch
none
Archive of layout-test-results from ews103 for mac-yosemite
none
Archive of layout-test-results from ews105 for mac-yosemite-wk2
none
Archive of layout-test-results from ews112 for mac-yosemite
none
Archive of layout-test-results from ews124 for ios-simulator-elcapitan-wk2
none
Patch none

Description Michael Catanzaro 2016-07-09 18:52:42 PDT
Reproducible in Epiphany on many Wordpress blogs:

 * Visit affected blog, e.g. https://blogs.gnome.org/mcatanzaro/
 * Close browser (triggering session state save)
 * Reopen browser (triggering session restoration)

It causes Wordpress to send an HTTP 400 error message, instead of properly reloading the page. Turns out we're tripping the Bad Behavior Wordpress plugin because we send an empty Referer header; Bad Behavior blocks the page load if the header is present but empty. I'm not sure why we are sending this empty header, but we should probably be saving the Referer in the session state. Alternatively we could just not send the Referer header, but I bet that would break other sites.

This is probably not reproducible in Safari because Cocoa ports do not reload pages after session restoration.
Comment 1 Chris Dumez 2016-07-10 09:56:41 PDT
Indeed, I don't see any issue in Safari with this URL.
Comment 2 Michael Catanzaro 2016-07-10 10:06:42 PDT
(In reply to comment #0)
> I'm not sure why we are sending
> this empty header, but we should probably be saving the Referer in the
> session state. Alternatively we could just not send the Referer header, but
> I bet that would break other sites.

It is actually saved in the session state, just not sent.
Comment 3 Michael Catanzaro 2016-07-10 10:52:50 PDT
Similar issue on vox.com. When http://www.vox.com/ is restored from session state, we get an almost-blank page that just says "Forbidden". It may or may not be the same issue.
Comment 4 Michael Catanzaro 2016-09-10 13:12:56 PDT
(In reply to comment #2) 
> It is actually saved in the session state, just not sent.

It actually is sent if there was a referrer originally; the problem occurs only on pages that didn't originally have a referrer before being saved to session state. In that case, we need to not add an empty referer header.
Comment 5 Michael Catanzaro 2016-09-10 13:24:19 PDT
I can fix this by modifying the GTK+ session state decoding.
Comment 6 Michael Catanzaro 2016-09-10 13:42:54 PDT
Created attachment 288494 [details]
Patch
Comment 7 Michael Catanzaro 2016-09-10 13:43:16 PDT
(Probably ought to write a test for it.)
Comment 8 WebKit Commit Bot 2016-09-10 13:45:06 PDT
Thanks for the patch. If this patch contains new public API please make sure it follows the guidelines for new WebKit2 GTK+ API. See http://trac.webkit.org/wiki/WebKitGTK/AddingNewWebKit2API
Comment 9 Michael Catanzaro 2016-09-10 17:02:11 PDT
Created attachment 288510 [details]
Patch
Comment 10 Michael Catanzaro 2016-09-10 17:03:20 PDT
So the first patch here was to WebKitWebViewSessionState.cpp, but I think I prefer the patch to ResourceRequestBase.cpp, which is less fragile.
Comment 11 Carlos Garcia Campos 2016-09-11 23:46:57 PDT
Comment on attachment 288510 [details]
Patch

If we do this, we should rename the bug as it's not GTK+ specific. You should also check all the callers, because there are several already checking isEmpty(), and others checking isNull(). CachedResource clears the header in case of the referrer is empty.
Comment 12 Michael Catanzaro 2016-09-12 10:08:28 PDT
Created attachment 288575 [details]
Patch
Comment 13 Michael Catanzaro 2016-09-12 10:11:12 PDT
Here's a more comprehensive patch. I'm not sure about it, though. I think the callers checking with isNull() really wanted to check isEmpty(); in that case, this patch is correct. But if any  were checking with the intention of not changing a preexisting referrer, which seems unlikely but is possible, then this patch would be wrong. It's probably fine if it passes layout tests.
Comment 14 Build Bot 2016-09-12 11:02:45 PDT
Comment on attachment 288575 [details]
Patch

Attachment 288575 [details] did not pass mac-ews (mac):
Output: http://webkit-queues.webkit.org/results/2060194

New failing tests:
imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight-referrer-worker.html
Comment 15 Build Bot 2016-09-12 11:02:49 PDT
Created attachment 288586 [details]
Archive of layout-test-results from ews103 for mac-yosemite

The attached test failures were seen while running run-webkit-tests on the mac-ews.
Bot: ews103  Port: mac-yosemite  Platform: Mac OS X 10.10.5
Comment 16 Build Bot 2016-09-12 11:06:40 PDT
Comment on attachment 288575 [details]
Patch

Attachment 288575 [details] did not pass mac-wk2-ews (mac-wk2):
Output: http://webkit-queues.webkit.org/results/2060200

New failing tests:
imported/w3c/web-platform-tests/fetch/api/cors/cors-preflight-referrer-worker.html
Comment 17 Build Bot 2016-09-12 11:06:45 PDT
Created attachment 288588 [details]
Archive of layout-test-results from ews105 for mac-yosemite-wk2

The attached test failures were seen while running run-webkit-tests on the mac-wk2-ews.
Bot: ews105  Port: mac-yosemite-wk2  Platform: Mac OS X 10.10.5
Comment 18 Michael Catanzaro 2016-09-12 11:14:10 PDT
(In reply to comment #13)
> I think the callers checking with isNull() really wanted to check isEmpty()

Evidently not so for the CORS code! It's safer to not change any of the isNull() checks, and only remove the isEmpty() checks.
Comment 19 Michael Catanzaro 2016-09-12 20:26:28 PDT
Created attachment 288664 [details]
Patch
Comment 20 Build Bot 2016-09-12 21:23:21 PDT
Comment on attachment 288664 [details]
Patch

Attachment 288664 [details] did not pass mac-ews (mac):
Output: http://webkit-queues.webkit.org/results/2063443

New failing tests:
imported/w3c/web-platform-tests/fetch/api/basic/referrer.html
imported/w3c/web-platform-tests/fetch/api/basic/request-referrer.html
Comment 21 Build Bot 2016-09-12 21:23:25 PDT
Created attachment 288668 [details]
Archive of layout-test-results from ews103 for mac-yosemite

The attached test failures were seen while running run-webkit-tests on the mac-ews.
Bot: ews103  Port: mac-yosemite  Platform: Mac OS X 10.10.5
Comment 22 Build Bot 2016-09-12 21:24:41 PDT
Comment on attachment 288664 [details]
Patch

Attachment 288664 [details] did not pass mac-wk2-ews (mac-wk2):
Output: http://webkit-queues.webkit.org/results/2063453

New failing tests:
imported/w3c/web-platform-tests/fetch/api/basic/referrer.html
imported/w3c/web-platform-tests/fetch/api/basic/request-referrer.html
Comment 23 Build Bot 2016-09-12 21:24:45 PDT
Created attachment 288669 [details]
Archive of layout-test-results from ews105 for mac-yosemite-wk2

The attached test failures were seen while running run-webkit-tests on the mac-wk2-ews.
Bot: ews105  Port: mac-yosemite-wk2  Platform: Mac OS X 10.10.5
Comment 24 Build Bot 2016-09-12 21:28:11 PDT
Comment on attachment 288664 [details]
Patch

Attachment 288664 [details] did not pass mac-debug-ews (mac):
Output: http://webkit-queues.webkit.org/results/2063449

New failing tests:
imported/w3c/web-platform-tests/fetch/api/basic/referrer.html
imported/w3c/web-platform-tests/fetch/api/basic/request-referrer.html
Comment 25 Build Bot 2016-09-12 21:28:15 PDT
Created attachment 288670 [details]
Archive of layout-test-results from ews112 for mac-yosemite

The attached test failures were seen while running run-webkit-tests on the mac-debug-ews.
Bot: ews112  Port: mac-yosemite  Platform: Mac OS X 10.10.5
Comment 26 Build Bot 2016-09-12 21:37:32 PDT
Comment on attachment 288664 [details]
Patch

Attachment 288664 [details] did not pass ios-sim-ews (ios-simulator-wk2):
Output: http://webkit-queues.webkit.org/results/2063463

New failing tests:
imported/w3c/web-platform-tests/fetch/api/basic/referrer.html
imported/w3c/web-platform-tests/fetch/api/basic/request-referrer.html
Comment 27 Build Bot 2016-09-12 21:37:36 PDT
Created attachment 288672 [details]
Archive of layout-test-results from ews124 for ios-simulator-elcapitan-wk2

The attached test failures were seen while running run-webkit-tests on the ios-sim-ews.
Bot: ews124  Port: ios-simulator-elcapitan-wk2  Platform: Mac OS X 10.11.5
Comment 28 Michael Catanzaro 2016-09-13 09:26:25 PDT
(In reply to comment #11)
> Comment on attachment 288510 [details]
> Patch
> 
> If we do this, we should rename the bug as it's not GTK+ specific. You
> should also check all the callers, because there are several already
> checking isEmpty(), and others checking isNull(). CachedResource clears the
> header in case of the referrer is empty.

Turns out callers really expect different things here and I am lousy at predicting them. Let's make the minimal correct change, which is to set the referrer to null when an empty string is passed.
Comment 29 Michael Catanzaro 2016-09-13 09:33:07 PDT
Created attachment 288691 [details]
Patch
Comment 30 Alex Christensen 2016-09-14 11:25:11 PDT
Comment on attachment 288691 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=288691&action=review

> Source/WebCore/ChangeLog:11
> +        No new tests because it's hard to think of how to test it.

There ought to be a way to test this.  We have php scripts that echo all headers.
Comment 31 Alexey Proskuryakov 2016-09-14 11:25:55 PDT
Comment on attachment 288691 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=288691&action=review

> Source/WebCore/ChangeLog:11
> +        No new tests because it's hard to think of how to test it.

There should be ways to trigger code that's so low in the stack, even if that's not for state restoration.

> Source/WebCore/platform/network/ResourceRequestBase.cpp:293
> +    // Many websites are intolerant of empty referrers, so never send one.

Do browsers actually never send one, not even when requested so in XMLHttpRequest? It's not obvious if this is the right level to make the fix.
Comment 32 youenn fablet 2016-09-14 11:36:23 PDT
Comment on attachment 288691 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=288691&action=review

>> Source/WebCore/platform/network/ResourceRequestBase.cpp:293
>> +    // Many websites are intolerant of empty referrers, so never send one.
> 
> Do browsers actually never send one, not even when requested so in XMLHttpRequest? It's not obvious if this is the right level to make the fix.

Ideally, we would like to have something like ASSERT(!httpReferrer.isEmpty()).
There are cases where no referrer header is sent (no-referrer policy). A fetch call may lead to such request without referrer.
Comment 33 Michael Catanzaro 2016-09-18 07:24:52 PDT
Comment on attachment 288494 [details]
Patch

Let's just go back to my original patch to WebKitWebViewSessionState. AP is right, modifying ResourceRequestBase is a bad idea; there's no good reason to prevent XHR from setting whatever value it wants for the header.

(In reply to comment #32)
> Ideally, we would like to have something like
> ASSERT(!httpReferrer.isEmpty()).
> There are cases where no referrer header is sent (no-referrer policy). A
> fetch call may lead to such request without referrer.

The assert would have to be (httpReferrer.isNull() || !httpReferrer.isEmpty()), because we of course want to allow sending requests with no referrer header. But anyway, not going to do that....
Comment 34 Carlos Garcia Campos 2016-09-18 23:33:09 PDT
Comment on attachment 288494 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=288494&action=review

> Source/WebKit2/UIProcess/API/gtk/WebKitWebViewSessionState.cpp:299
> +    // frameState.referrer must not be an empty string since we never want to
> +    // send an empty Referer header. Bug #159606.
> +    if (strlen(referrer))
> +        frameState.referrer = String::fromUTF8(referrer);

I think this is actually a workaround, and probably good enough for the stable branch. In my opinion the bug is not the in the decoder, but in the encoder, because we are not correctly handling optional strings (null strings). So, the optional fields that are strings should be encoded as "ms", instead of "s". We should review all other strings and change the encode/decode format, but we can do that in a separate bug, because that requires to add a new format version.
Comment 35 WebKit Commit Bot 2016-09-18 23:53:35 PDT
Comment on attachment 288494 [details]
Patch

Clearing flags on attachment: 288494

Committed r206086: <http://trac.webkit.org/changeset/206086>
Comment 36 WebKit Commit Bot 2016-09-18 23:53:42 PDT
All reviewed patches have been landed.  Closing bug.