yii2: host header attack problem

Hi, I cache some menu links in my application (datacaching using filecache), sometimes hostname part of these links change to a random website (usually chinese websites) and I have to flush the cache to make it work again. after some research and studying webserver logs it turned out to be “Host Header Attack”. turning off caching would solve the problem but that’s not the solution for me as I have very high traffic websites. reading some articles ( https://www.acunetix.com/vulnerabilities/web/host-header-attack ) suggest not using $_SERVER['HTTP_HOST'] when creating links and using $_SERVER['SERVER_NAME'] instead. I tried to simulate the situation using burp suite application and the suggested solution acutally works. but the code responsible for getting host info is located in core framework and is in \yii\web\Request::getHostInfo method and the above change should be applied on that method. although I don’t know if it has any other side effect rather than ruined links when caching is enabled.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 5
  • Comments: 104 (104 by maintainers)

Commits related to this issue

Most upvoted comments

$_SERVER['SERVER_NAME'] is somethign configured at server side. It can contain random string. As well as it can contain something like *.example.com, which is invalid domain name either.

Indeed in correctly configured web server it will contain the host name, but in correctly configured web server you will be unable to access application from invalid host.

I’d prefer to add this as a property to the Request class which can be configured like it is done by symfony and django. something like trustedHosts property.

@cebe More or less. I will open PR with my proposal.

The decision on this one is not easy and we had long discussion about it. As it is mainly a server configuration issue, putting too much code about it into already complex Request class does not seem right. Both sides have good arguments but we came to the conclusion that education in the guide + a behavior which is independent of Request class is good way to deal with this. Thanks to all for providing your input on this!

no matter what point u look at it, its a server configuration fault, wich shouldnt be solved in YII, as proposed above we can have a filter wich does an additional check, Imo thats the best we can do, i mean there can be many more problems when the server isnt configured right.

We should emphasize that server config is the right place to fix it in the guide and apidoc of the filter and add the filter as a PHP solution if server config does not work.

I’m for adding this to the core. I think we should not completely rely on server config and limit prevention of HTTP Host header attacks to configuring server.

For example Django’s approach is separate setting ALLOWED_HOSTS.

This is a security measure to prevent HTTP Host header attacks, which are possible even under many seemingly-safe web server configurations.

This related section also can be useful - Host Header Validation.

Warning

Previous versions of this document recommended configuring your web server to ensure it validates incoming HTTP Host headers. While this is still recommended, in many common web servers a configuration that seems to validate the Host header may not in fact do so. For instance, even if Apache is configured such that your Django site is served from a non-default virtual host with the ServerName set, it is still possible for an HTTP request to match this virtual host and supply a fake Host header. Thus, Django now requires that you set ALLOWED_HOSTS explicitly rather than relying on web server configuration.

$ php -S 0.0.0.0:8000 -r 'echo $_SERVER["HTTP_HOST"];'  &
$ curl -v -H "Host: fake.ru" http://127.0.0.1:8000/
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 8000 (#0)
> GET / HTTP/1.1
> Host: fake.ru
> User-Agent: curl/7.49.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Host: fake.ru
< Connection: close
< X-Powered-By: PHP/7.0.12
< Content-type: text/html; charset=UTF-8
<
fake.ru
* Closing connection 0

I would prefer if the framework’s defaults are robust against such improper configuration. The h1 on yiiframework.com says “The Fast, Secure and Professional PHP Framework”. If there is an opportunity to mitigate this risk, we should take it.

  • Request::getHostInfo() uses Host by default
  • BaseUrl::to(), base() and home() use Request::getHostInfo()
  • the UrlManager and UrlRule use Request::getHostInfo() and ::to()
  • Enh #2646: Added support for specifying hostinfo in the pattern of a URL rule (qiangxue)

While Request::getHostInfo() is quite well documented, it could additionally warn that Host header cannot be trusted, using it to generate links is unnecessary, and it’s wise to configure the value instead.

But I would prefer if Yii would not use the Host header unless the user configures it, and document this configuration with the warning.

I’ll think about how this might be achieved and return to this discussion later.

You can override default error handler by custom action, and then you can (accidentally) use Request::$hostInfo (which is super easy, since lots of code use UrlManager which use Request::$hostInfo)

That is why HostControll::denyCallback() uses own implementation for 404 page without using native error handler mechanism.

@samdark You could write your own denyCallback which use Request::$hostInfo.

@cebe I don’t think it is enough. Request::$hostInfo is used implicit in many places, it is easy to use it without even know about that. For example, redirecting to correct domain is probably valid solution and some people will prefer this instead of showing ugly 404 error. But using Yii::$app->getResponse()->redirect(['/home']) is insecure, since it will use corrupted host info to create URL for redirection, and this is not obvious.

In the other hand introducing property like HostControl::$defaultHostInfo which will replace invalid host info, will simplify everything. After setting this property you could safely use redirection and even show regular error page.

In console app you already have empty host info and I never seen such URLs, so I guess that this problem doesn’t even exist.

In console Application you may still need to create a URLs. For example, while sending some notification emails. However, developer expects having problem, while generating URLs in console application and will definitely verify the results. However, having trusted hosts configured at application config, you can easily forget about in case project domain name changing. Thus you will have invalid URLs created.

Same will happen, if you cache main menu and get substituted ‘host header’ just in time this cache created.

But I can create alternative PR with Symfony-way solution, just give me few days.

Do not ask me. I have make my decision on this matter. Symfony has another style of application flow and data verification. Thus I can not accept some solution, just because Symfony does this in this way. I would prefer to close this issue already. Thus your efforts may be in vain at the end. Unless other team members are willing to wait, there is no need to proceed.

We could add event for that case? Like onInvalidHostInfo?

How it will be different from current state?

I can’t really imagine usecase when you get empty hostInfo from valid request.

At the present state following condition:

if (Yii::$app->request->getHostInfo() === null) 

indicates there is no host information available at all.

Such thing may happen while running console application. Alhough such condition is unlikely to be used for some business logic - it may still appear.

Also I was thinking about setting Request::$_hostInfo to empty string or false if it was invalid.

Returning empty host info will produce invalid absolute URLs in silent mode. This may confuse the developer at some stage, while his project is moving from one domain name to another one.

If ‘host name’ check fails - it should produce an explicit error - otherwise it makes no sense.

Request::getHostInfo() will return null if host info is invalid.

I do not like such behavior. For me cases host is invalid and there is no host at all are different. I do not like to mix them up. For example: request, even with invalid Host Name, may still need to be logged for the error processing. Just tell if host is invalid means there is no host at all does not feel to be right.

Setup filter at Application::beforeRequest() level is exactly the earliest as possible. What is the problem?

The problem is that Request::getHostInfo() will return unsafe value. So I’m proposing to validate host info before it is returned by Request::getHostInfo() (in beforeRequest), you’re proposing doing this after this. So my proposal is the earliest of the earliest as possible. 😃

It can not. In case host name is used at least once while rendering error page - you will see 500 error instead of 404, as it will throw new NotFoundHttpException exception, while alreay processing exception.

No? Request::getHostInfo() will return null if host info is invalid. This should not brake anything.

and still in some way i think this issue isn’t at all at Yii, but at the server configuration. So why build in fail saves for something that is not the responsibility of Yii.

Your implementation makes sense. I forgot about the behavior implications. Should’ve looked more carefully at the PR. 👍

I don’t like seeing it solved via action filter. Why not simply modify a property

See https://github.com/yiisoft/yii2/issues/13050#issuecomment-262253751 :

If it is an app with many sub-domains then it may make sense to configure it per action.

hey allow to set host regexes instead of simple strings

HostControl at #13063 allows specification of hots name as wildcard, e.g. *.example.com.

@arogachev thanks for references. These are useful for docs.

PR with filter implementation: #13063