1 | <?php |
||
2 | /** |
||
3 | * @link http://www.yiiframework.com/ |
||
4 | * @copyright Copyright (c) 2008 Yii Software LLC |
||
5 | * @license http://www.yiiframework.com/license/ |
||
6 | */ |
||
7 | |||
8 | namespace yii\filters; |
||
9 | |||
10 | use Yii; |
||
11 | use yii\base\ActionFilter; |
||
12 | use yii\helpers\StringHelper; |
||
13 | use yii\web\NotFoundHttpException; |
||
14 | |||
15 | /** |
||
16 | * HostControl provides simple control over requested host name. |
||
17 | * |
||
18 | * This filter provides protection against ['host header' attacks](https://www.acunetix.com/vulnerabilities/web/host-header-attack), |
||
19 | * allowing action execution only for specified host names. |
||
20 | * |
||
21 | * Application configuration example: |
||
22 | * |
||
23 | * ```php |
||
24 | * return [ |
||
25 | * 'as hostControl' => [ |
||
26 | * 'class' => 'yii\filters\HostControl', |
||
27 | * 'allowedHosts' => [ |
||
28 | * 'example.com', |
||
29 | * '*.example.com', |
||
30 | * ], |
||
31 | * ], |
||
32 | * // ... |
||
33 | * ]; |
||
34 | * ``` |
||
35 | * |
||
36 | * Controller configuration example: |
||
37 | * |
||
38 | * ```php |
||
39 | * use yii\web\Controller; |
||
40 | * use yii\filters\HostControl; |
||
41 | * |
||
42 | * class SiteController extends Controller |
||
43 | * { |
||
44 | * public function behaviors() |
||
45 | * { |
||
46 | * return [ |
||
47 | * 'hostControl' => [ |
||
48 | * 'class' => HostControl::class, |
||
49 | * 'allowedHosts' => [ |
||
50 | * 'example.com', |
||
51 | * '*.example.com', |
||
52 | * ], |
||
53 | * ], |
||
54 | * ]; |
||
55 | * } |
||
56 | * |
||
57 | * // ... |
||
58 | * } |
||
59 | * ``` |
||
60 | * |
||
61 | * > Note: the best way to restrict allowed host names is usage of the web server 'virtual hosts' configuration. |
||
62 | * This filter should be used only if this configuration is not available or compromised. |
||
63 | * |
||
64 | * @author Paul Klimov <[email protected]> |
||
65 | * @since 2.0.11 |
||
66 | */ |
||
67 | class HostControl extends ActionFilter |
||
68 | { |
||
69 | /** |
||
70 | * @var array|\Closure|null list of host names, which are allowed. |
||
71 | * Each host can be specified as a wildcard pattern. For example: |
||
72 | * |
||
73 | * ```php |
||
74 | * [ |
||
75 | * 'example.com', |
||
76 | * '*.example.com', |
||
77 | * ] |
||
78 | * ``` |
||
79 | * |
||
80 | * This field can be specified as a PHP callback of following signature: |
||
81 | * |
||
82 | * ```php |
||
83 | * function (\yii\base\Action $action) { |
||
84 | * //return array of strings |
||
85 | * } |
||
86 | * ``` |
||
87 | * |
||
88 | * where `$action` is the current [[\yii\base\Action|action]] object. |
||
89 | * |
||
90 | * If this field is not set - no host name check will be performed. |
||
91 | */ |
||
92 | public $allowedHosts; |
||
93 | /** |
||
94 | * @var callable a callback that will be called if the current host does not match [[allowedHosts]]. |
||
95 | * If not set, [[denyAccess()]] will be called. |
||
96 | * |
||
97 | * The signature of the callback should be as follows: |
||
98 | * |
||
99 | * ```php |
||
100 | * function (\yii\base\Action $action) |
||
101 | * ``` |
||
102 | * |
||
103 | * where `$action` is the current [[\yii\base\Action|action]] object. |
||
104 | * |
||
105 | * > Note: while implementing your own host deny processing, make sure you avoid usage of the current requested |
||
106 | * host name, creation of absolute URL links, caching page parts and so on. |
||
107 | */ |
||
108 | public $denyCallback; |
||
109 | /** |
||
110 | * @var string|null fallback host info (e.g. `http://www.yiiframework.com`) used when [[\yii\web\Request::$hostInfo|Request::$hostInfo]] is invalid. |
||
111 | * This value will replace [[\yii\web\Request::$hostInfo|Request::$hostInfo]] before [[$denyCallback]] is called to make sure that |
||
112 | * an invalid host will not be used for further processing. You can set it to `null` to leave [[\yii\web\Request::$hostInfo|Request::$hostInfo]] untouched. |
||
113 | * Default value is empty string (this will result creating relative URLs instead of absolute). |
||
114 | * @see \yii\web\Request::getHostInfo() |
||
115 | */ |
||
116 | public $fallbackHostInfo = ''; |
||
117 | |||
118 | |||
119 | /** |
||
120 | * {@inheritdoc} |
||
121 | */ |
||
122 | 11 | public function beforeAction($action) |
|
123 | { |
||
124 | 11 | $allowedHosts = $this->allowedHosts; |
|
125 | 11 | if ($allowedHosts instanceof \Closure) { |
|
126 | 2 | $allowedHosts = call_user_func($allowedHosts, $action); |
|
127 | } |
||
128 | 11 | if ($allowedHosts === null) { |
|
129 | 1 | return true; |
|
130 | } |
||
131 | |||
132 | 10 | if (!is_array($allowedHosts) && !$allowedHosts instanceof \Traversable) { |
|
133 | 1 | $allowedHosts = (array) $allowedHosts; |
|
134 | } |
||
135 | |||
136 | 10 | $currentHost = Yii::$app->getRequest()->getHostName(); |
|
137 | |||
138 | 10 | foreach ($allowedHosts as $allowedHost) { |
|
139 | 10 | if (StringHelper::matchWildcard($allowedHost, $currentHost)) { |
|
140 | 10 | return true; |
|
141 | } |
||
142 | } |
||
143 | |||
144 | // replace invalid host info to prevent using it in further processing |
||
145 | 6 | if ($this->fallbackHostInfo !== null) { |
|
146 | 6 | Yii::$app->getRequest()->setHostInfo($this->fallbackHostInfo); |
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||
147 | } |
||
148 | |||
149 | 6 | if ($this->denyCallback !== null) { |
|
150 | 2 | call_user_func($this->denyCallback, $action); |
|
151 | } else { |
||
152 | 4 | $this->denyAccess($action); |
|
153 | } |
||
154 | |||
155 | 2 | return false; |
|
156 | } |
||
157 | |||
158 | /** |
||
159 | * Denies the access. |
||
160 | * The default implementation will display 404 page right away, terminating the program execution. |
||
161 | * You may override this method, creating your own deny access handler. While doing so, make sure you |
||
162 | * avoid usage of the current requested host name, creation of absolute URL links, caching page parts and so on. |
||
163 | * @param \yii\base\Action $action the action to be executed. |
||
164 | * @throws NotFoundHttpException |
||
165 | */ |
||
166 | 4 | protected function denyAccess($action) |
|
167 | { |
||
168 | 4 | $exception = new NotFoundHttpException(Yii::t('yii', 'Page not found.')); |
|
169 | |||
170 | // use regular error handling if $this->fallbackHostInfo was set |
||
171 | 4 | if (!empty(Yii::$app->getRequest()->hostName)) { |
|
0 ignored issues
–
show
The property
hostName does not exist on yii\console\Request . Since you implemented __get , consider adding a @property annotation.
Loading history...
|
|||
172 | 1 | throw $exception; |
|
173 | } |
||
174 | |||
175 | 3 | $response = Yii::$app->getResponse(); |
|
176 | 3 | $errorHandler = Yii::$app->getErrorHandler(); |
|
177 | |||
178 | 3 | $response->setStatusCode($exception->statusCode, $exception->getMessage()); |
|
179 | 3 | $response->data = $errorHandler->renderFile($errorHandler->errorView, ['exception' => $exception]); |
|
180 | 3 | $response->send(); |
|
181 | |||
182 | 3 | Yii::$app->end(); |
|
183 | } |
||
184 | } |
||
185 |