diff --git a/src/aphront/response/AphrontResponse.php b/src/aphront/response/AphrontResponse.php index e0f0730081..27c603b091 100644 --- a/src/aphront/response/AphrontResponse.php +++ b/src/aphront/response/AphrontResponse.php @@ -1,152 +1,170 @@ request = $request; return $this; } public function getRequest() { return $this->request; } public function getHeaders() { $headers = array(); if (!$this->frameable) { $headers[] = array('X-Frame-Options', 'Deny'); } + if ($this->getRequest() && $this->getRequest()->isHTTPS()) { + $hsts_key = 'security.strict-transport-security'; + $use_hsts = PhabricatorEnv::getEnvConfig($hsts_key); + if ($use_hsts) { + $duration = phutil_units('365 days in seconds'); + } else { + // If HSTS has been disabled, tell browsers to turn it off. This may + // not be effective because we can only disable it over a valid HTTPS + // connection, but it best represents the configured intent. + $duration = 0; + } + + $headers[] = array( + 'Strict-Transport-Security', + "max-age={$duration}; includeSubdomains; preload", + ); + } + return $headers; } public function setCacheDurationInSeconds($duration) { $this->cacheable = $duration; return $this; } public function setLastModified($epoch_timestamp) { $this->lastModified = $epoch_timestamp; return $this; } public function setHTTPResponseCode($code) { $this->responseCode = $code; return $this; } public function getHTTPResponseCode() { return $this->responseCode; } public function getHTTPResponseMessage() { return ''; } public function setFrameable($frameable) { $this->frameable = $frameable; return $this; } public static function processValueForJSONEncoding(&$value, $key) { if ($value instanceof PhutilSafeHTMLProducerInterface) { // This renders the producer down to PhutilSafeHTML, which will then // be simplified into a string below. $value = hsprintf('%s', $value); } if ($value instanceof PhutilSafeHTML) { // TODO: Javelin supports implicity conversion of '__html' objects to // JX.HTML, but only for Ajax responses, not behaviors. Just leave things // as they are for now (where behaviors treat responses as HTML or plain // text at their discretion). $value = $value->getHTMLContent(); } } public static function encodeJSONForHTTPResponse(array $object) { array_walk_recursive( $object, array('AphrontResponse', 'processValueForJSONEncoding')); $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); return $response; } protected function addJSONShield($json_response) { // Add a shield to prevent "JSON Hijacking" attacks where an attacker // requests a JSON response using a normal