[go: up one dir, main page]

Source/WebCore/ChangeLog

 12016-11-29 John Wilander <wilander@apple.com>
 2
 3 Require preflight for non-standard CORS-safelisted request headers Accept, Accept-Language, and Content-Language
 4 https://bugs.webkit.org/show_bug.cgi?id=165178
 5 <rdar://problem/18792250>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 Test: http/tests/xmlhttprequest/cors-non-standard-safelisted-headers-should-trigger-preflight.html
 10
 11 * loader/CrossOriginAccessControl.cpp:
 12 (WebCore::isOnAccessControlSimpleRequestHeaderWhitelist):
 13 Now makes a call to WebCore:: isValidAcceptHeaderValue for Accept headers
 14 and WebCore:: isValidLanguageHeaderValue for Accept-Language and
 15 Content-Language headers.
 16 * platform/Language.cpp:
 17 (WebCore::isValidLanguageHeaderValue):
 18 Basic check that the characters are all ASCII alphanumeric, ' ', '*', '-', '.', ';', or '='.
 19 * platform/Language.h:
 20 * platform/network/HTTPParsers.cpp:
 21 (WebCore::isValidAcceptHeaderValue):
 22 Basic check that the characters are all ASCII alphanumeric, ' ', '*', '.', '/', ';', or '='.
 23 (WebCore::isSimpleHeader):
 24 Now makes a call to WebCore:: isValidAcceptHeaderValue for Accept headers
 25 and WebCore:: isValidLanguageHeaderValue for Accept-Language and
 26 Content-Language headers.
 27 (WebCore::isCrossOriginSafeRequestHeader):
 28 Now makes a call to WebCore:: isValidAcceptHeaderValue for Accept headers
 29 and WebCore:: isValidLanguageHeaderValue for Accept-Language and
 30 Content-Language headers.
 31 * platform/network/HTTPParsers.h:
 32
1332016-11-29 Brady Eidson <beidson@apple.com>
234
335 Followup to:
209107

Source/WebCore/loader/CrossOriginAccessControl.cpp

2929
3030#include "HTTPHeaderNames.h"
3131#include "HTTPParsers.h"
 32#include "Language.h"
3233#include "ResourceRequest.h"
3334#include "ResourceResponse.h"
3435#include "SchemeRegistry.h"

@@bool isOnAccessControlSimpleRequestHeade
4950{
5051 switch (name) {
5152 case HTTPHeaderName::Accept:
 53 return isValidAcceptHeaderValue(value);
5254 case HTTPHeaderName::AcceptLanguage:
5355 case HTTPHeaderName::ContentLanguage:
54  return true;
 56 return isValidLanguageHeaderValue(value);
5557 case HTTPHeaderName::ContentType: {
5658 // Preflight is required for MIME types that can not be sent via form submission.
5759 String mimeType = extractMIMETypeFromMediaType(value);
209004

Source/WebCore/platform/Language.cpp

2626#include "config.h"
2727#include "Language.h"
2828
 29#include <wtf/ASCIICType.h>
2930#include <wtf/HashMap.h>
3031#include <wtf/NeverDestroyed.h>
3132#include <wtf/PlatformUserPreferredLanguages.h>

@@String displayNameForLanguageLocale(cons
188189 return localeName;
189190}
190191
 192
 193bool isValidLanguageHeaderValue(const String& value)
 194{
 195 // Accept-Language https://tools.ietf.org/html/rfc7231#section-5.3.5
 196 // Content-Language https://tools.ietf.org/html/rfc7231#section-3.1.3.2
 197 // Language range https://tools.ietf.org/html/rfc4647#section-2.1
 198 // Language tags https://tools.ietf.org/html/rfc7231#section-3.1.3.1
 199 // Language tag syntax https://tools.ietf.org/html/bcp47#section-2.1
 200 UChar c;
 201 for (unsigned i = 0; i < value.length(); ++i) {
 202 c = value[i];
 203 if (!isASCIIAlphanumeric(c)
 204 && c != ' '
 205 && c != '*'
 206 && c != '-'
 207 && c != '.'
 208 && c != ';'
 209 && c != '=')
 210 return false;
 211 }
 212
 213 // FIXME: Validate further by splitting into language tags and optional quality
 214 // values (q=) and then check each language tag.
 215 return true;
 216}
 217
191218}
209004

Source/WebCore/platform/Language.h

@@WEBCORE_EXPORT void removeLanguageChange
4444
4545String displayNameForLanguageLocale(const String&);
4646
 47bool isValidLanguageHeaderValue(const String&);
 48
4749// Called from platform specific code when the user's preferred language(s) change.
4850WEBCORE_EXPORT void languageDidChange();
4951}
209004

Source/WebCore/platform/network/HTTPParsers.cpp

3434#include "HTTPParsers.h"
3535
3636#include "HTTPHeaderNames.h"
 37#include "Language.h"
3738#include <wtf/DateMath.h>
3839#include <wtf/NeverDestroyed.h>
3940#include <wtf/text/CString.h>

@@bool isValidHTTPHeaderValue(const String
126127 return true;
127128}
128129
 130// See RFC 7231, Section 5.3.2
 131bool isValidAcceptHeaderValue(const String& value)
 132{
 133 UChar c;
 134 for (unsigned i = 0; i < value.length(); ++i) {
 135 c = value[i];
 136 if (!isASCIIAlphanumeric(c)
 137 && c != ' '
 138 && c != '*'
 139 && c != '.'
 140 && c != '/'
 141 && c != ';'
 142 && c != '=')
 143 return false;
 144 }
 145
 146 return true;
 147}
 148
129149// See RFC 7230, Section 3.2.6.
130150bool isValidHTTPToken(const String& value)
131151{

@@void parseAccessControlExposeHeadersAllo
732752 }
733753}
734754
735 // Implememtnation of https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name
 755// Implementation of https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name
736756bool isForbiddenHeaderName(const String& name)
737757{
738758 HTTPHeaderName headerName;

@@bool isSimpleHeader(const String& name,
778798 return false;
779799 switch (headerName) {
780800 case HTTPHeaderName::Accept:
 801 return isValidAcceptHeaderValue(value);
781802 case HTTPHeaderName::AcceptLanguage:
782803 case HTTPHeaderName::ContentLanguage:
783  return true;
 804 return isValidLanguageHeaderValue(value);
784805 case HTTPHeaderName::ContentType: {
785806 String mimeType = extractMIMETypeFromMediaType(value);
786807 return equalLettersIgnoringASCIICase(mimeType, "application/x-www-form-urlencoded") || equalLettersIgnoringASCIICase(mimeType, "multipart/form-data") || equalLettersIgnoringASCIICase(mimeType, "text/plain");

@@bool isCrossOriginSafeRequestHeader(HTTP
824845{
825846 switch (name) {
826847 case HTTPHeaderName::Accept:
 848 return isValidAcceptHeaderValue(value);
827849 case HTTPHeaderName::AcceptLanguage:
828850 case HTTPHeaderName::ContentLanguage:
829  return true;
 851 return isValidLanguageHeaderValue(value);
830852 case HTTPHeaderName::ContentType: {
831853 String mimeType = extractMIMETypeFromMediaType(value);
832854 return equalLettersIgnoringASCIICase(mimeType, "application/x-www-form-urlencoded") || equalLettersIgnoringASCIICase(mimeType, "multipart/form-data") || equalLettersIgnoringASCIICase(mimeType, "text/plain");
209004

Source/WebCore/platform/network/HTTPParsers.h

@@enum XFrameOptionsDisposition {
6969
7070bool isValidReasonPhrase(const String&);
7171bool isValidHTTPHeaderValue(const String&);
 72bool isValidAcceptHeaderValue(const String&);
7273bool isValidHTTPToken(const String&);
7374bool parseHTTPRefresh(const String& refresh, double& delay, String& url);
7475std::optional<std::chrono::system_clock::time_point> parseHTTPDate(const String&);
209004

LayoutTests/ChangeLog

 12016-11-29 John Wilander <wilander@apple.com>
 2
 3 Require preflight for non-standard CORS-safelisted request headers Accept, Accept-Language, and Content-Language
 4 https://bugs.webkit.org/show_bug.cgi?id=165178
 5 <rdar://problem/18792250>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 * http/tests/xmlhttprequest/cors-non-standard-safelisted-headers-should-trigger-preflight-expected.txt: Added.
 10 * http/tests/xmlhttprequest/cors-non-standard-safelisted-headers-should-trigger-preflight.html: Added.
 11 Tests that:
 12 Normal Accept, Accept-Language, and Content-Language headers don't trigger a preflight.
 13 Abnormal Accept, Accept-Language, and Content-Language headers do trigger a preflight.
 14 Abnormal Accept, Accept-Language, and Content-Language headers are accepted if the server whitelists them.
 15 * http/tests/xmlhttprequest/resources/cors-preflight-safelisted-headers-responder.php: Added.
 16
1172016-11-28 Antti Koivisto <antti@apple.com>
218
319 Remove FIRST_LINE_INHERITED fake pseudo style
209004

LayoutTests/http/tests/xmlhttprequest/cors-non-standard-safelisted-headers-should-trigger-preflight-expected.txt

 1CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/xmlhttprequest/resources/cors-preflight-safelisted-headers-responder.php. Request header field Accept is not allowed by Access-Control-Allow-Headers.
 2CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/xmlhttprequest/resources/cors-preflight-safelisted-headers-responder.php. Request header field Accept-Language is not allowed by Access-Control-Allow-Headers.
 3CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/xmlhttprequest/resources/cors-preflight-safelisted-headers-responder.php. Request header field Content-Language is not allowed by Access-Control-Allow-Headers.
 4CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/xmlhttprequest/resources/cors-preflight-safelisted-headers-responder.php. Request header field Content-Language is not allowed by Access-Control-Allow-Headers.
 5CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/xmlhttprequest/resources/cors-preflight-safelisted-headers-responder.php. Request header field Accept is not allowed by Access-Control-Allow-Headers.
 6PASS Accept header with normal value SHOULD NOT cause a preflight
 7PASS Accept-Language header with normal value SHOULD NOT cause a preflight
 8PASS Content-Language header with normal value SHOULD NOT cause a preflight
 9PASS Accept header with abnormal value SHOULD cause a preflight
 10PASS Accept-Language header with abnormal value SHOULD cause a preflight
 11PASS Content-Language header with abnormal value SHOULD cause a preflight
 12PASS Accept header with normal value, Accept-Language header with normal value, and Content-Language header with abnormal value SHOULD cause a preflight
 13PASS Accept header with normal value and then another Accept header with abnormal value SHOULD cause a preflight
 14PASS Accept header with abnormal value and explicitly allowed headers SHOULD be allowed
 15PASS Content-Language header with abnormal value and explicitly allowed headers SHOULD be allowed
 16PASS Accept header with normal value, Accept-Language header with normal value, Content-Language header with abnormal value, and explicitly allowed headers SHOULD be allowed
 17PASS Accept header with normal value, then another Accept header with abnormal value, and explicitly allowed headers SHOULD be allowed
 18
nonexistent

LayoutTests/http/tests/xmlhttprequest/cors-non-standard-safelisted-headers-should-trigger-preflight.html

 1<!DOCTYPE html>
 2<html lang="en">
 3<head>
 4 <meta charset="UTF-8">
 5 <title>Non-Standard Safelisted Headers SHOULD Trigger a Preflight</title>
 6 <script src="../resources/js-test-pre.js"></script>
 7</head>
 8<body>
 9<!-- https://fetch.spec.whatwg.org/#cors-safelisted-request-header -->
 10<script>
 11 if (window.testRunner) {
 12 testRunner.dumpAsText();
 13 testRunner.waitUntilDone();
 14 }
 15
 16 var invocation;
 17 var url = 'http://localhost:8000/xmlhttprequest/resources/cors-preflight-safelisted-headers-responder.php';
 18
 19 function createReadyStateHandler (description, testNumber) {
 20 return function handler (e) {
 21 if (invocation.readyState === XMLHttpRequest.DONE) {
 22 testPassed(description);
 23 if (testNumber === (testCases.length - 1)) {
 24 if (window.testRunner) {
 25 testRunner.notifyDone();
 26 }
 27 } else {
 28 runTestCase(testNumber + 1);
 29 }
 30 }
 31 }
 32 }
 33
 34 function createOnErrorHandler (description, testNumber) {
 35 return function handler (e) {
 36 e.preventDefault();
 37 testPassed(description);
 38 if (testNumber === (testCases.length - 1)) {
 39 if (window.testRunner) {
 40 testRunner.notifyDone();
 41 }
 42 } else {
 43 runTestCase(testNumber + 1);
 44 }
 45 }
 46 }
 47
 48 var abnormalSimpleCorsHeaderValue = "() { :;};"
 49 var testCases = [
 50 // Positive test cases with normal headers
 51 {
 52 headersToAdd: [{ name : "Accept", value: "text/*" }],
 53 explicitlyAllowHeaders: false,
 54 shouldCausePreflight: false,
 55 description: "Accept header with normal value SHOULD NOT cause a preflight"
 56 }
 57 ,{
 58 headersToAdd: [{ name : "Accept-Language", value: "en" }],
 59 explicitlyAllowHeaders: false,
 60 shouldCausePreflight: false,
 61 description: "Accept-Language header with normal value SHOULD NOT cause a preflight"
 62 }
 63 ,{
 64 headersToAdd: [{ name : "Content-Language", value: "en" }],
 65 explicitlyAllowHeaders: false,
 66 shouldCausePreflight: false,
 67 description: "Content-Language header with normal value SHOULD NOT cause a preflight"
 68 }
 69 // Negative test cases with abnormal headers
 70 ,{
 71 headersToAdd: [{ name : "Accept", value: abnormalSimpleCorsHeaderValue }],
 72 explicitlyAllowHeaders: false,
 73 shouldCausePreflight: true,
 74 description: "Accept header with abnormal value SHOULD cause a preflight"
 75 }
 76 ,{
 77 headersToAdd: [{ name : "Accept-Language", value: abnormalSimpleCorsHeaderValue }],
 78 explicitlyAllowHeaders: false,
 79 shouldCausePreflight: true,
 80 description: "Accept-Language header with abnormal value SHOULD cause a preflight"
 81 }
 82 ,{
 83 headersToAdd: [{ name : "Content-Language", value: abnormalSimpleCorsHeaderValue }],
 84 explicitlyAllowHeaders: false,
 85 shouldCausePreflight: true,
 86 description: "Content-Language header with abnormal value SHOULD cause a preflight"
 87 }
 88 ,{
 89 headersToAdd: [{ name : "Accept", value: "text/*" }, { name : "Accept-Language", value: "en" }, { name : "Content-Language", value: abnormalSimpleCorsHeaderValue }],
 90 explicitlyAllowHeaders: false,
 91 shouldCausePreflight: true,
 92 description: "Accept header with normal value, Accept-Language header with normal value, and Content-Language header with abnormal value SHOULD cause a preflight"
 93 }
 94 ,{
 95 headersToAdd: [{ name : "Accept", value: "text/*" }, { name : "Accept", value: abnormalSimpleCorsHeaderValue }],
 96 explicitlyAllowHeaders: false,
 97 shouldCausePreflight: true,
 98 description: "Accept header with normal value and then another Accept header with abnormal value SHOULD cause a preflight"
 99 }
 100 // Positive test cases with abnormal headers
 101 ,{
 102 headersToAdd: [{ name : "Accept", value: abnormalSimpleCorsHeaderValue }],
 103 explicitlyAllowHeaders: true,
 104 shouldCausePreflight: true,
 105 description: "Accept header with abnormal value and explicitly allowed headers SHOULD be allowed"
 106 }
 107 ,{
 108 headersToAdd: [{ name : "Content-Language", value: abnormalSimpleCorsHeaderValue }],
 109 explicitlyAllowHeaders: true,
 110 shouldCausePreflight: true,
 111 description: "Content-Language header with abnormal value and explicitly allowed headers SHOULD be allowed"
 112 }
 113 ,{
 114 headersToAdd: [{ name : "Accept", value: "text/*" }, { name : "Accept-Language", value: "en" }, { name : "Content-Language", value: abnormalSimpleCorsHeaderValue }],
 115 explicitlyAllowHeaders: true,
 116 shouldCausePreflight: true,
 117 description: "Accept header with normal value, Accept-Language header with normal value, Content-Language header with abnormal value, and explicitly allowed headers SHOULD be allowed"
 118 }
 119 ,{
 120 headersToAdd: [{ name : "Accept", value: "text/*" }, { name : "Accept", value: abnormalSimpleCorsHeaderValue }],
 121 explicitlyAllowHeaders: true,
 122 shouldCausePreflight: true,
 123 description: "Accept header with normal value, then another Accept header with abnormal value, and explicitly allowed headers SHOULD be allowed"
 124 }
 125 ];
 126
 127 function runTestCase(testNumber) {
 128 var testCase = testCases[testNumber];
 129 invocation = new XMLHttpRequest();
 130 if(invocation) {
 131 invocation.open('GET', url + (testCase.explicitlyAllowHeaders ? "/?explicitlyAllowHeaders=true" : ""), true);
 132 for (var i = 0; i < testCase.headersToAdd.length; i++) {
 133 invocation.setRequestHeader(testCase.headersToAdd[i].name, testCase.headersToAdd[i].value);
 134 }
 135 if (testCase.shouldCausePreflight && !testCase.explicitlyAllowHeaders) {
 136 invocation. testNumber);
 137 } else {
 138 invocation. testNumber);
 139 }
 140 invocation.send();
 141 }
 142 }
 143
 144 runTestCase(0);
 145</script>
 146</body>
 147</html>
nonexistent

LayoutTests/http/tests/xmlhttprequest/resources/cors-preflight-safelisted-headers-responder.php

 1<?php
 2header('Access-Control-Allow-Origin: http://127.0.0.1:8000');
 3
 4if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS' && isset($_GET['explicitlyAllowHeaders'])) {
 5 header('Access-Control-Allow-Methods: GET, OPTIONS');
 6 header('Access-Control-Allow-Headers: Accept, Accept-Language, Content-Language');
 7}
 8?>
nonexistent