Skip to content

Added the documentation for the trusted_hosts option #3876

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions reference/configuration/framework.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Configuration
* `http_method_override`_
* `ide`_
* `test`_
* `trusted_hosts`_
* `trusted_proxies`_
* `form`_
* enabled
Expand Down Expand Up @@ -114,6 +115,69 @@ services related to testing your application (e.g. ``test.client``) are loaded.
This setting should be present in your ``test`` environment (usually via
``app/config/config_test.yml``). For more information, see :doc:`/book/testing`.

.. _reference-framework-trusted-hosts:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did you add this? Did you want to add a link to this section, but you forgot to commit that link?


trusted_hosts
~~~~~~~~~~~~~

**type**: ``array``

The value of the ``$_SERVER['HOST']`` parameter cannot be safely trusted because
users can manipulate it. This option whitelists the hosts that your Symfony
application can respond to.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by "can respond to"? It sounds like the app will not work unless you put your domain name here. I don't think this is what we mean - I actually don't know what this feature does tbh :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To understand the feature, it's better to see the code:

1. When booting, FrameworkBundle sets the trusted hosts of the Request:

class FrameworkBundle extends Bundle
{
    public function boot()
    {
        // ...

        if ($trustedHosts = $this->container->getParameter('kernel.trusted_hosts')) {
            Request::setTrustedHosts($trustedHosts);
        }
    }

    // ...
}

2. The Request uses this configuration value to define the trusted hosts patterns:

    public static function setTrustedHosts(array $hostPatterns)
    {
        self::$trustedHostPatterns = array_map(function ($hostPattern) {
            return sprintf('{%s}i', str_replace('}', '\\}', $hostPattern));
        }, $hostPatterns);
        // we need to reset trusted hosts on trusted host patterns change
        self::$trustedHosts = array();
    }

3. When dealing with the Request, one of the called methods is prepareRequestUri():

    protected function prepareRequestUri()
    {
        $requestUri = '';

        if ($this->headers->has('X_ORIGINAL_URL')) {
            // ...
        } elseif ($this->headers->has('X_REWRITE_URL')) {
            // ...
        } elseif ($this->server->get('IIS_WasUrlRewritten') == '1' && $this->server->get('UNENCODED_URL') != '') {
            // ...
        } elseif ($this->server->has('REQUEST_URI')) {
            $requestUri = $this->server->get('REQUEST_URI');
            // HTTP proxy reqs setup request URI with scheme and host
            // [and port] + the URL path, only use URL path
            $schemeAndHttpHost = $this->getSchemeAndHttpHost();
            if (strpos($requestUri, $schemeAndHttpHost) === 0) {
                $requestUri = substr($requestUri, strlen($schemeAndHttpHost));
            }
        } elseif ($this->server->has('ORIG_PATH_INFO')) {
            // ...
        }

        // normalize the request URI to ease creating sub-requests from this request
        $this->server->set('REQUEST_URI', $requestUri);

        return $requestUri;
    }

4. Indirectly, the getSchemeAndHttpHost() method executes the getHost() method, which will throw an exception if the host of the user request doesn't match the trusted host patterns:

    public function getHost()
    {
        // ...

        // as the host can come from the user (HTTP_HOST and depending on the
        // configuration, SERVER_NAME too can come from the user)
        // check that it does not contain forbidden characters (see RFC 952 and RFC 2181)
        if ($host && !preg_match('/^\[?(?:[a-zA-Z0-9-:\]_]+\.?)+$/', $host)) {
            throw new \UnexpectedValueException(sprintf('Invalid Host "%s"', $host));
        }

        if (count(self::$trustedHostPatterns) > 0) {
            // to avoid host header injection attacks, you should provide a
            // list of trusted host patterns

            if (in_array($host, self::$trustedHosts)) {
                return $host;
            }

            foreach (self::$trustedHostPatterns as $pattern) {
                if (preg_match($pattern, $host)) {
                    self::$trustedHosts[] = $host;

                    return $host;
                }
            }

            throw new \UnexpectedValueException(sprintf('Untrusted Host "%s"', $host));
        }

        return $host;
    }

So, if you leave this option blank, everything works as previously did. If you set any value, the application will thro an exception if the host provided by the user request doesn't match the configured regular expressions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, so the purpose of the feature is so that I can "restrict my application to respond to only a sub-set of hosts". Is that accurate? What exactly is the security risk? Or said differently, why would a developer care about this?

I would like to re-word the first few sentences to address the use-case. This is totally invented (since I don't know the real use-case), but for example:

If you use the HTTP host for security (e.g., you can only access certain URLs from a specific host), then you should explicitly make sure your application only works for the host names that you're expecting.

That's weak wording... but you get the idea. I'm purposefully being "dumb" (and not looking up more detailson) so that if I understand this feature, then we're good :).

Thanks!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


.. configuration-block::

.. code-block:: yaml

framework:
trusted_hosts: ['acme.com', 'acme.org']

.. code-block:: xml

<framework:config trusted-hosts="acme.com, acme.org">
<!-- ... -->
</framework>

.. code-block:: php

$container->loadFromExtension('framework', array(
'trusted_hosts' => array('acme.com', 'acme.org'),
));

Hosts can also be configured using regular expressions, which make it easier to
respond to any subdomain:

.. configuration-block::

.. code-block:: yaml

framework:
trusted_hosts: ['.*\.?acme.com$', '.*\.?acme.org$']

.. code-block:: xml

<framework:config trusted-hosts=".*\.?acme.com$, .*\.?acme.org$">
<!-- ... -->
</framework>

.. code-block:: php

$container->loadFromExtension('framework', array(
'trusted_hosts' => array('.*\.?acme.com$', '.*\.?acme.org$'),
));

In addition, you can also set the trusted hosts in the front controller using
the ``Request::setTrustedHosts()`` method:

.. code-block:: php
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use the :: shortcut instead


// web/app.php
Request::setTrustedHosts(array('.*\.?acme.com$', '.*\.?acme.org$'));

The default value for this option is an empty array, meaning that the application
can respond to any given host.

.. _reference-framework-trusted-proxies:

trusted_proxies
Expand Down