diff --git a/src/applications/config/check/PhabricatorSecuritySetupCheck.php b/src/applications/config/check/PhabricatorSecuritySetupCheck.php index b93b148ae3..654f0d5f44 100644 --- a/src/applications/config/check/PhabricatorSecuritySetupCheck.php +++ b/src/applications/config/check/PhabricatorSecuritySetupCheck.php @@ -45,5 +45,30 @@ final class PhabricatorSecuritySetupCheck extends PhabricatorSetupCheck { ->setMessage($message); } + $file_key = 'security.alternate-file-domain'; + $file_domain = PhabricatorEnv::getEnvConfig($file_key); + if (!$file_domain) { + $doc_href = PhabricatorEnv::getDocLink('Configuring a File Domain'); + + $this->newIssue('security.'.$file_key) + ->setName(pht('Alternate File Domain Not Configured')) + ->setSummary( + pht( + 'Increase security (and improve performance) by configuring '. + 'a CDN or alternate file domain.')) + ->setMessage( + pht( + 'Phabricator is currently configured to serve user uploads '. + 'directly from the same domain as other content. This is a '. + 'security risk.'. + "\n\n". + 'Configure a CDN (or alternate file domain) to eliminate this '. + 'risk. Using a CDN will also improve performance. See the '. + 'guide below for instructions.')) + ->addPhabricatorConfig($file_key) + ->addLink( + $doc_href, + pht('Configuration Guide: Configuring a File Domain')); + } } } diff --git a/src/applications/config/option/PhabricatorSecurityConfigOptions.php b/src/applications/config/option/PhabricatorSecurityConfigOptions.php index 7175b36ac1..6fbf2146de 100644 --- a/src/applications/config/option/PhabricatorSecurityConfigOptions.php +++ b/src/applications/config/option/PhabricatorSecurityConfigOptions.php @@ -18,31 +18,25 @@ final class PhabricatorSecurityConfigOptions public function getOptions() { $support_href = PhabricatorEnv::getDoclink('Give Feedback! Get Support!'); + $doc_href = PhabricatorEnv::getDoclink('Configuring a File Domain'); + $doc_name = pht('Configuration Guide: Configuring a File Domain'); + return array( $this->newOption('security.alternate-file-domain', 'string', null) ->setLocked(true) ->setSummary(pht('Alternate domain to serve files from.')) ->setDescription( pht( - "IMPORTANT: By default, Phabricator serves files from the same ". - "domain the application lives on. This is convenient but not ". - "secure: it creates a large class of vulnerabilities which can ". - "not be generally mitigated.\n\n". - - "To avoid this, you should configure a second domain in the same ". - "way you have the primary domain configured (i.e., point it at ". - "the same machine and set up the same vhost rules) and provide ". - "it here. For instance, if your primary install is on ". - "'http://www.phabricator-example.com/', you could configure ". - "'http://www.phabricator-files.com/' and specify the entire ". - "domain (with protocol) here. This will enforce that files are ". - "served only from the alternate domain. Ideally, you should use ". - "a completely separate domain name rather than just a different ". - "subdomain.\n\n". - - "It is **STRONGLY RECOMMENDED** that you configure this. Your ". - "install is **NOT SECURE** unless you do so.")) - ->addExample('http://www.phabricator-files.com/', pht('Valid Setting')), + 'By default, Phabricator serves files from the same domain '. + 'the application is served from. This is convenient, but '. + 'presents a security risk.'. + "\n\n". + 'You should configure a CDN or alternate file domain to mitigate '. + 'this risk. Configuring a CDN will also improve performance. See '. + '[[ %s | %s ]] for instructions.', + $doc_href, + $doc_name)) + ->addExample('https://files.phabcdn.net/', pht('Valid Setting')), $this->newOption( 'security.hmac-key', 'string', diff --git a/src/docs/user/configuration/configuration_guide.diviner b/src/docs/user/configuration/configuration_guide.diviner index 4a444935db..72f438cd3f 100644 --- a/src/docs/user/configuration/configuration_guide.diviner +++ b/src/docs/user/configuration/configuration_guide.diviner @@ -193,6 +193,8 @@ Continue by: @{article:Configuring Accounts and Registration}; or - understanding advanced configuration topics with @{article:Configuration User Guide: Advanced Configuration}; or + - configuring an alternate file domain with + @{article:Configuring a File Domain}; or - configuring a preamble script to set up the environment properly behind a load balancer, or adjust rate limiting with @{article:Configuring a Preamble Script}; or diff --git a/src/docs/user/configuration/configuring_file_domain.diviner b/src/docs/user/configuration/configuring_file_domain.diviner new file mode 100644 index 0000000000..5724bc3536 --- /dev/null +++ b/src/docs/user/configuration/configuring_file_domain.diviner @@ -0,0 +1,108 @@ +@title Configuring a File Domain +@group config + +Setup guide for an alternate file domain or CDN. + +Overview +======== + +Serving files that users upload from the same domain that Phabricator runs on +is a security risk. + +In general, doing this creates a risk that users who have permission to upload +files may be able to upload specially crafted files (like Flash or Java +applets) which can execute with domain permissions in some contexts (usually +because of security issues with Flash and Java, but both products have a rich +history of security issues). The attacker can then trick another user into +executing the file and gain access to their session. + +The best way to mitigate this threat is to serve files from a separate domain. +For example, if Phabricator is hosted at `https://phabricator.example.com/`, +you can serve files from `https://files.exampleusercontent.com/`. + +The alternate file domain should be a completely different domain from your +primary domain, not just a different subdomain. For example, Google uses +`googleusercontent.com`, //not// `usercontent.google.com`. + +You can also configure the alternate file domain to serve through a CDN, which +will improve performance. + +Approaches +========= + +Broadly, you can either choose a CDN service and configure that (which will +also defuse the security risks) or you can configure a second domain with the +same settings as your first domain. A CDN service may be easier to set up and +can improve performance. + +| Method | Setup Difficulty | Cost | Notes | +|---|---|---|---| +| AWS CloudFront | Very Easy | Cheap | Recommended | +| CloudFlare | Easy | Free/Cheap | Recommended | +| Self Hosted | Moderate | Free | No CDN unless you're an ops wizard. | + +Approach: AWS CloudFront +======== + +CloudFront is a CDN service that's part of Amazon Web Services. It makes +particular sense to use if you're hosting your install in AWS. + +To configure it, set up a new CloudFront distribution which is pointed at +your Phabricator install as an origin (make sure you point it at the primary +domain name of your install, not just a load balancer or instance). You do not +need to set up a new domain name, which makes setup a bit more straightforward. + +Once configured, accessing the distribution's domain name should return a +Phabricator error page indicating that Phabricator does not recognize the +domain. If you see this page, it means you've configured things correctly. + +Continue to "Configuring Phabricator", below. + +Approach: CloudFlare +======== + +[[ https://cloudflare.net | CloudFlare ]] is a general-purpose CDN service. + +To set up CloudFlare, you'll need to register a second domain and go through +their enrollment process to host the alternate domain on their servers. Use a +CNAME record to forward a subdomain to your Phabricator install. + +CloudFlare will automatically generate SSL certificates for hosted domains, +which can significantly reduce the cost and complexity of setup. + +Once configured, accessing the CNAME-forwarded subdomain should return a +Phabricator error page indicating that Phabricator does not recognize the +domain. If you see this page, it means you've configured things correctly. + +Continue to "Configuring Phabricator", below. + +Approach: Self Hosted +======== + +To do this, just set up a second domain exactly like your primary domain is +set up. When setup is complete, visiting the domain should return a Phabricator +error page indicating that Phabricator does not recognize the domain. This +means that you've configured things correctly. + +Note that if you use SSL (which you should), you'll also need to get a +certificate for this alternate domain and configure that, too. + +You can also configure a self-hosted domain to route through a caching server +to provide some of the performance benefits of a CDN, but this is advanced and +outside the scope of this documentation. + +Continue to "Configuring Phabricator", below. + +Configuring Phabricator +======== + +After you've set up a CDN or an alternate domain, configure Phabricator to +recognize the domain. Run this command, providing the domain you have +configured in place of the `` token. You should include the protocol, +so an example domain might be `https://cdn.phabcdn.net/`. + + phabricator/ $ ./bin/config set security.alternate-file-domain + +Phabricator should now serve CSS, JS, images, profile pictures, and user +content through the file domain. You can verify this with "View Source" or +by downloading a file and checking the URL.