1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-02-26 21:49:08 +01:00
phorge-phorge/src/aphront/response/AphrontResponse.php

171 lines
4.7 KiB
PHP
Raw Normal View History

<?php
abstract class AphrontResponse {
private $request;
2011-01-27 11:35:04 -08:00
private $cacheable = false;
private $responseCode = 200;
private $lastModified = null;
protected $frameable;
public function setRequest($request) {
$this->request = $request;
return $this;
}
public function getRequest() {
return $this->request;
}
public function getHeaders() {
$headers = array();
if (!$this->frameable) {
$headers[] = array('X-Frame-Options', 'Deny');
}
Support HTTP Strict Transport Security Summary: Ref T4340. The attack this prevents is: - An adversary penetrates your network. They acquire one of two capabilities: - Your server is either configured to accept both HTTP and HTTPS, and they acquire the capability to observe HTTP traffic. - Or your server is configured to accept only HTTPS, and they acquire the capability to control DNS or routing. In this case, they start a proxy server to expose your secure service over HTTP. - They send you a link to `http://secure.service.com` (note HTTP, not HTTPS!) - You click it since everything looks fine and the domain is correct, not noticing that the "s" is missing. - They read your traffic. This is similar to attacks where `https://good.service.com` is proxied to `https://good.sorvace.com` (i.e., a similar looking domain), but can be more dangerous -- for example, the browser will send (non-SSL-only) cookies and the attacker can write cookies. This header instructs browsers that they can never access the site over HTTP and must always use HTTPS, defusing this class of attack. Test Plan: - Configured HTTPS locally. - Accessed site over HTTP (got application redirect) and HTTPS. - Enabled HSTS. - Accessed site over HTTPS (to set HSTS). - Tore down HTTPS part of the server and tried to load the site over HTTP. Browser refused to load "http://" and automatically tried to load "https://". In another browser which had not received the "HSTS" header, loading over HTTP worked fine. - Brought the HTTPS server back up, things worked fine. - Turned off the HSTS config setting. - Loaded a page (to set HSTS with expires 0, diabling it). - Tore down the HTTPS part of the server again. - Tried to load HTTP. - Now it worked. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T4340 Differential Revision: https://secure.phabricator.com/D11820
2015-02-19 10:33:48 -08:00
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;
}
2011-01-30 09:15:01 -08:00
2011-01-27 11:35:04 -08:00
public function setCacheDurationInSeconds($duration) {
$this->cacheable = $duration;
return $this;
}
2011-01-30 09:15:01 -08:00
public function setLastModified($epoch_timestamp) {
$this->lastModified = $epoch_timestamp;
return $this;
}
public function setHTTPResponseCode($code) {
$this->responseCode = $code;
return $this;
}
2011-01-30 09:15:01 -08:00
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);
OAuth - Phabricator OAuth server and Phabricator client for new Phabricator OAuth Server Summary: adds a Phabricator OAuth server, which has three big commands: - auth - allows $user to authorize a given client or application. if $user has already authorized, it hands an authoization code back to $redirect_uri - token - given a valid authorization code, this command returns an authorization token - whoami - Conduit.whoami, all nice and purdy relative to the oauth server. Also has a "test" handler, which I used to create some test data. T850 will delete this as it adds the ability to create this data in the Phabricator product. This diff also adds the corresponding client in Phabricator for the Phabricator OAuth Server. (Note that clients are known as "providers" in the Phabricator codebase but client makes more sense relative to the server nomenclature) Also, related to make this work well - clean up the diagnostics page by variabilizing the provider-specific information and extending the provider classes as appropriate. - augment Conduit.whoami for more full-featured OAuth support, at least where the Phabricator client is concerned What's missing here... See T844, T848, T849, T850, and T852. Test Plan: - created a dummy client via the test handler. setup development.conf to have have proper variables for this dummy client. went through authorization and de-authorization flows - viewed the diagnostics page for all known oauth providers and saw provider-specific debugging information Reviewers: epriestley CC: aran, epriestley Maniphest Tasks: T44, T797 Differential Revision: https://secure.phabricator.com/D1595
2012-02-03 16:21:40 -08:00
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 <script /> tag and then uses
// Object.prototype.__defineSetter__() or similar to read response data.
// This header causes the browser to loop infinitely instead of handing over
// sensitive data.
$shield = 'for (;;);';
OAuth - Phabricator OAuth server and Phabricator client for new Phabricator OAuth Server Summary: adds a Phabricator OAuth server, which has three big commands: - auth - allows $user to authorize a given client or application. if $user has already authorized, it hands an authoization code back to $redirect_uri - token - given a valid authorization code, this command returns an authorization token - whoami - Conduit.whoami, all nice and purdy relative to the oauth server. Also has a "test" handler, which I used to create some test data. T850 will delete this as it adds the ability to create this data in the Phabricator product. This diff also adds the corresponding client in Phabricator for the Phabricator OAuth Server. (Note that clients are known as "providers" in the Phabricator codebase but client makes more sense relative to the server nomenclature) Also, related to make this work well - clean up the diagnostics page by variabilizing the provider-specific information and extending the provider classes as appropriate. - augment Conduit.whoami for more full-featured OAuth support, at least where the Phabricator client is concerned What's missing here... See T844, T848, T849, T850, and T852. Test Plan: - created a dummy client via the test handler. setup development.conf to have have proper variables for this dummy client. went through authorization and de-authorization flows - viewed the diagnostics page for all known oauth providers and saw provider-specific debugging information Reviewers: epriestley CC: aran, epriestley Maniphest Tasks: T44, T797 Differential Revision: https://secure.phabricator.com/D1595
2012-02-03 16:21:40 -08:00
$response = $shield.$json_response;
return $response;
}
public function getCacheHeaders() {
$headers = array();
2011-01-27 11:35:04 -08:00
if ($this->cacheable) {
$headers[] = array(
'Expires',
$this->formatEpochTimestampForHTTPHeader(time() + $this->cacheable),
);
2011-01-27 11:35:04 -08:00
} else {
$headers[] = array(
'Cache-Control',
'private, no-cache, no-store, must-revalidate',
);
OAuthServer polish and random sauce Summary: This diff makes the OAuthServer more compliant with the spec by - making it return well-formatted error codes with error types from the spec. - making it respect the "state" variable, which is a transparent variable the client passes and the server passes back - making it be super, duper compliant with respect to redirect uris -- if specified in authorization step, check if its valid relative to the client registered URI and if so save it -- if specified in authorization step, check if its been specified in the access step and error if it doesn't match or doesn't exist -- note we don't make any use of it in the access step which seems strange but hey, that's what the spec says! This diff makes the OAuthServer suck less by - making the "cancel" button do something in the user authorization flow - making the client list view and client edit view be a bit more usable around client secrets - fixing a few bugs I managed to introduce along the way Test Plan: - create a test phabricator client, updated my conf, and then linked and unlinked phabricator to itself - wrote some tests for PhabricatorOAuthServer -- they pass! -- these validate the various validate URI checks - tried a few important authorization calls -- http://phabricator.dev/oauthserver/auth/?client_id=X&state=test&redirect_uri=http://www.evil.com --- verified error'd from mismatching redirect uri's --- verified state parameter in response --- verified did not redirect to client redirect uri -- http://phabricator.dev/oauthserver/auth/?client_id=X w/ existing authorization --- got redirected to proper client url with error that response_type not specified -- http://phabricator.dev/oauthserver/auth/?client_id=X&response_type=code w/ existing authorization --- got redirected to proper client url with pertinent code! - tried a few important access calls -- verified appropriate errors if missing any required parameters -- verified good access code with appropriate other variables resulted in an access token - verified that if redirect_uri set correctly in authorization required for access and errors if differs at all / only succeeds if exactly the same Reviewers: epriestley Reviewed By: epriestley CC: aran, epriestley, ajtrichards Maniphest Tasks: T889, T906, T897 Differential Revision: https://secure.phabricator.com/D1727
2012-03-01 14:46:18 -08:00
$headers[] = array(
'Pragma',
'no-cache',
);
$headers[] = array(
'Expires',
'Sat, 01 Jan 2000 00:00:00 GMT',
);
}
if ($this->lastModified) {
$headers[] = array(
'Last-Modified',
$this->formatEpochTimestampForHTTPHeader($this->lastModified),
);
2011-01-27 11:35:04 -08:00
}
// IE has a feature where it may override an explicit Content-Type
// declaration by inferring a content type. This can be a security risk
// and we always explicitly transmit the correct Content-Type header, so
// prevent IE from using inferred content types. This only offers protection
// on recent versions of IE; IE6/7 and Opera currently ignore this header.
$headers[] = array('X-Content-Type-Options', 'nosniff');
return $headers;
}
private function formatEpochTimestampForHTTPHeader($epoch_timestamp) {
return gmdate('D, d M Y H:i:s', $epoch_timestamp).' GMT';
}
abstract public function buildResponseString();
}