From c8b4bfdcd1cc2f5aab54ee41eb43039b8ca650d7 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 14 Feb 2012 14:51:51 -0800 Subject: [PATCH] Encode "<" and ">" in JSON/Ajax responses to prevent content-sniffing attacks Summary: Some browsers will still sniff content types even with "Content-Type" and "X-Content-Type-Options: nosniff". Encode "<" and ">" to prevent them from sniffing the content as HTML. See T865. Also unified some of the code on this pathway. Test Plan: Verified Opera no longer sniffs the Conduit response into HTML for the test case in T865. Unit tests pass. Reviewers: cbg, btrahan Reviewed By: cbg CC: aran, epriestley Maniphest Tasks: T139, T865 Differential Revision: https://secure.phabricator.com/D1606 --- scripts/conduit/api.php | 2 +- src/__phutil_library_map__.php | 2 + .../response/ajax/AphrontAjaxResponse.php | 10 ++-- src/aphront/response/base/AphrontResponse.php | 34 +++++++++++++- .../response/json/AphrontJSONResponse.php | 46 +++++++++++++++++++ src/aphront/response/json/__init__.php | 12 +++++ src/aphront/sink/base/AphrontHTTPSink.php | 17 +++++++ .../__tests__/AphrontHTTPSinkTestCase.php | 16 +++++++ src/aphront/sink/base/__tests__/__init__.php | 3 ++ .../api/PhabricatorConduitAPIController.php | 5 +- .../conduit/controller/api/__init__.php | 2 +- .../protocol/response/ConduitAPIResponse.php | 4 -- .../CelerityStaticResourceResponse.php | 4 +- webroot/index.php | 2 + 14 files changed, 143 insertions(+), 16 deletions(-) create mode 100644 src/aphront/response/json/AphrontJSONResponse.php create mode 100644 src/aphront/response/json/__init__.php diff --git a/scripts/conduit/api.php b/scripts/conduit/api.php index 2844c2f393..e2da82ce6e 100644 --- a/scripts/conduit/api.php +++ b/scripts/conduit/api.php @@ -85,7 +85,7 @@ $response = id(new ConduitAPIResponse()) ->setResult($result) ->setErrorCode($error_code) ->setErrorInfo($error_info); -echo $response->toJSON(), "\n"; +echo json_encode($response->toDictionary()), "\n"; // TODO -- how get $connection_id from SSH? $connection_id = null; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index fb58757510..77037eed33 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -55,6 +55,7 @@ phutil_register_library_map(array( 'AphrontIsolatedDatabaseConnection' => 'storage/connection/isolated', 'AphrontIsolatedDatabaseConnectionTestCase' => 'storage/connection/isolated/__tests__', 'AphrontIsolatedHTTPSink' => 'aphront/sink/test', + 'AphrontJSONResponse' => 'aphront/response/json', 'AphrontJavelinView' => 'view/javelin-view', 'AphrontKeyboardShortcutsAvailableView' => 'view/widget/keyboardshortcuts', 'AphrontListFilterView' => 'view/layout/listfilter', @@ -880,6 +881,7 @@ phutil_register_library_map(array( 'AphrontIsolatedDatabaseConnection' => 'AphrontDatabaseConnection', 'AphrontIsolatedDatabaseConnectionTestCase' => 'PhabricatorTestCase', 'AphrontIsolatedHTTPSink' => 'AphrontHTTPSink', + 'AphrontJSONResponse' => 'AphrontResponse', 'AphrontJavelinView' => 'AphrontView', 'AphrontKeyboardShortcutsAvailableView' => 'AphrontView', 'AphrontListFilterView' => 'AphrontView', diff --git a/src/aphront/response/ajax/AphrontAjaxResponse.php b/src/aphront/response/ajax/AphrontAjaxResponse.php index b64d053f17..a22cbc6eeb 100644 --- a/src/aphront/response/ajax/AphrontAjaxResponse.php +++ b/src/aphront/response/ajax/AphrontAjaxResponse.php @@ -1,7 +1,7 @@ renderAjaxResponse( + $object = $response->buildAjaxResponse( $this->content, $this->error); + + return $this->encodeJSONForHTTPResponse( + $object, + $use_javelin_shield = true); } public function getHeaders() { diff --git a/src/aphront/response/base/AphrontResponse.php b/src/aphront/response/base/AphrontResponse.php index c4b4ce8b31..6c32b7f744 100644 --- a/src/aphront/response/base/AphrontResponse.php +++ b/src/aphront/response/base/AphrontResponse.php @@ -70,6 +70,37 @@ abstract class AphrontResponse { return $this; } + protected function encodeJSONForHTTPResponse( + array $object, + $use_javelin_shield) { + + $response = json_encode($object); + + // Prevent content sniffing attacks by encoding "<" and ">", so browsers + // won't try to execute the document as HTML even if they ignore + // Content-Type and X-Content-Type-Options. See T865. + $response = str_replace( + array('<', '>'), + array('\u003c', '\u003e'), + $response); + + // Add a shield to prevent "JSON Hijacking" attacks where an attacker + // requests a JSON response using a normal