This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * @link https://github.com/vuongxuongminh/yii2-mfa |
||
4 | * @copyright Copyright (c) 2019 Vuong Xuong Minh |
||
5 | * @license [New BSD License](http://www.opensource.org/licenses/bsd-license.php) |
||
6 | */ |
||
7 | |||
8 | namespace vxm\mfa; |
||
9 | |||
10 | use Yii; |
||
11 | |||
12 | use yii\base\Behavior as BaseBehavior; |
||
13 | use yii\base\InvalidValueException; |
||
14 | use yii\di\Instance; |
||
15 | use yii\web\User; |
||
16 | use yii\web\UserEvent; |
||
17 | use yii\web\ForbiddenHttpException; |
||
18 | |||
19 | |||
20 | /** |
||
21 | * Class MfaBehavior automatically redirect to verify mfa url when identity enabled it and verify digits given. |
||
22 | * |
||
23 | * To use MfaBehavior, configure the [[User::$identityClass]] property which should specify class implemented [[\vxm\mfa\IdentityInterface]]. |
||
24 | * |
||
25 | * For example, |
||
26 | * |
||
27 | * ```php |
||
28 | * |
||
29 | * use yii\db\ActiveRecord; |
||
30 | * |
||
31 | * use vxm\mfa\IdentityInterface; |
||
32 | * |
||
33 | * class Identity extends ActiveRecord implements IdentityInterface { |
||
34 | * |
||
35 | * public function getMfaSecretKey() |
||
36 | * { |
||
37 | * return $this->mfa_secret; |
||
38 | * } |
||
39 | * |
||
40 | * } |
||
41 | * ``` |
||
42 | * |
||
43 | * And attach this behavior to the [[User]] component of an application config. |
||
44 | * |
||
45 | * For example, |
||
46 | * |
||
47 | * ```php |
||
48 | * |
||
49 | * 'user' => [ |
||
50 | * 'as mfa' => [ |
||
51 | * 'class' => 'vxm\mfa\MfaBehavior', |
||
52 | * 'verifyUrl' => 'site/mfa-verify', |
||
53 | * ] |
||
54 | * |
||
55 | * ] |
||
56 | * ``` |
||
57 | * |
||
58 | * Note: it only work when an owner [[User::$enableSession]] is true. |
||
59 | * |
||
60 | * @property Otp $otp use to validate, generating an otp digits. |
||
61 | * |
||
62 | * @author Vuong Minh <[email protected]> |
||
63 | * @since 1.0.0 |
||
64 | */ |
||
65 | class Behavior extends BaseBehavior |
||
66 | { |
||
67 | |||
68 | /** |
||
69 | * @var User |
||
70 | */ |
||
71 | public $owner; |
||
72 | |||
73 | /** |
||
74 | * @var callable|bool weather enabling this behavior. This property use in special case you need to disable it in runtime environment. |
||
75 | * When it is callable this object instance will be parse to first parameter. |
||
76 | * |
||
77 | * Example: |
||
78 | * ```php |
||
79 | * function(\vxm\mfa\Behavior $behavior) { |
||
80 | * |
||
81 | * |
||
82 | * } |
||
83 | * ``` |
||
84 | */ |
||
85 | public $enable = true; |
||
86 | |||
87 | /** |
||
88 | * @var string|array the URL for login when [[verifyRequired()]] is called. |
||
89 | * If an array is given, [[\yii\web\UrlManager::createUrl()]] will be called to create the corresponding URL. |
||
90 | * The first element of the array should be the route to the verify action, and the rest of |
||
91 | * the name-value pairs are GET parameters used to construct the verify URL. For example, |
||
92 | * |
||
93 | * ```php |
||
94 | * 'site/mfa-verify' |
||
95 | * ``` |
||
96 | * |
||
97 | * If this property is `null`, a 403 HTTP exception will be raised when [[verifyRequired()]] is called. |
||
98 | */ |
||
99 | public $verifyUrl; |
||
100 | |||
101 | /** |
||
102 | * @var string the session variable name used to store values of an identity logged in. |
||
103 | */ |
||
104 | public $mfaParam = '__mfa'; |
||
105 | |||
106 | /** |
||
107 | * @inheritDoc |
||
108 | */ |
||
109 | 13 | public function init() |
|
110 | { |
||
111 | 13 | if (is_callable($this->enable)) { |
|
112 | $this->enable = call_user_func($this->enable, $this); |
||
113 | } |
||
114 | |||
115 | 13 | parent::init(); |
|
116 | 13 | } |
|
117 | |||
118 | /** |
||
119 | * @inheritDoc |
||
120 | */ |
||
121 | 13 | public function events() |
|
122 | { |
||
123 | 13 | if ($this->enable) { |
|
124 | return [ |
||
125 | 13 | User::EVENT_BEFORE_LOGIN => 'beforeLogin' |
|
126 | ]; |
||
127 | } else { |
||
128 | return []; |
||
129 | } |
||
130 | } |
||
131 | |||
132 | /** |
||
133 | * Event trigger when before user log in to system. It will be require an user verify otp digits except when user logged in via cookie base. |
||
134 | * |
||
135 | * @param UserEvent $event an event triggered |
||
136 | * @throws ForbiddenHttpException |
||
137 | */ |
||
138 | 11 | public function beforeLogin(UserEvent $event) |
|
139 | { |
||
140 | 11 | if (!$event->isValid) { |
|
141 | return; |
||
142 | } |
||
143 | |||
144 | 11 | if (!$event->identity instanceof IdentityInterface) { |
|
145 | throw new InvalidValueException("{$this->owner->identityClass}::findIdentity() must return an object implementing \\vxm\\mfa\\IdentityInterface."); |
||
146 | } |
||
147 | |||
148 | 11 | $secretKey = $event->identity->getMfaSecretKey(); |
|
149 | |||
150 | 11 | if (!empty($secretKey) && $this->owner->enableSession && !$event->cookieBased) { |
|
151 | 11 | $event->isValid = false; |
|
152 | 11 | $this->saveIdentityLoggedIn($event->identity, $event->duration); |
|
153 | 11 | $this->verifyRequired(); |
|
154 | } |
||
155 | 11 | } |
|
156 | |||
157 | /** |
||
158 | * Switches to a logged in identity for the current user. |
||
159 | * |
||
160 | * @see \yii\web\User::switchIdentity() |
||
161 | */ |
||
162 | 5 | public function switchIdentityLoggedIn() |
|
163 | { |
||
164 | 5 | $data = $this->getIdentityLoggedIn(); |
|
165 | |||
166 | 5 | if ($data === null) { |
|
167 | return; |
||
168 | } |
||
169 | |||
170 | 5 | list($identity, $duration) = $data; |
|
171 | 5 | $this->owner->switchIdentity($identity, $duration); |
|
172 | 5 | } |
|
173 | |||
174 | /** |
||
175 | * Save the user identity logged in object when an identity need to verify. |
||
176 | * |
||
177 | * @param IdentityInterface|null $identity the identity object associated with the currently logged user. |
||
178 | * @param int $duration number of seconds that the user can remain in logged-in status. |
||
179 | */ |
||
180 | 11 | public function saveIdentityLoggedIn(IdentityInterface $identity, int $duration) |
|
181 | { |
||
182 | 11 | Yii::$app->getSession()->set($this->mfaParam, [$identity->getId(), $duration]); |
|
0 ignored issues
–
show
|
|||
183 | 11 | } |
|
184 | |||
185 | /** |
||
186 | * Get an identity logged in. |
||
187 | * |
||
188 | * @return array|null Returns an array of 'identity' and 'duration' if valid, otherwise null. |
||
189 | * @see saveIdentityLoggedIn() |
||
190 | */ |
||
191 | 12 | public function getIdentityLoggedIn() |
|
192 | { |
||
193 | 12 | $data = Yii::$app->getSession()->get($this->mfaParam); |
|
0 ignored issues
–
show
The method
getSession does only exist in yii\web\Application , but not in yii\console\Application .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
![]() |
|||
194 | |||
195 | 12 | if ($data === null) { |
|
196 | 2 | return null; |
|
197 | } |
||
198 | |||
199 | 11 | if (is_array($data) && count($data) == 2) { |
|
200 | 11 | list($id, $duration) = $data; |
|
201 | /* @var $class IdentityInterface */ |
||
202 | 11 | $class = $this->owner->identityClass; |
|
203 | 11 | $identity = $class::findIdentity($id); |
|
204 | |||
205 | 11 | if ($identity !== null) { |
|
206 | 11 | if (!$identity instanceof IdentityInterface) { |
|
207 | throw new InvalidValueException("$class::findIdentity() must return an object implementing \\vxm\\mfa\\IdentityInterface."); |
||
208 | } else { |
||
209 | 11 | return [$identity, $duration]; |
|
210 | } |
||
211 | } |
||
212 | } |
||
213 | |||
214 | $this->removeIdentityLoggedIn(); |
||
215 | |||
216 | return null; |
||
217 | } |
||
218 | |||
219 | /** |
||
220 | * Removes the identity logged in. |
||
221 | */ |
||
222 | 2 | public function removeIdentityLoggedIn() |
|
223 | { |
||
224 | 2 | Yii::$app->getSession()->remove($this->mfaParam); |
|
0 ignored issues
–
show
The method
getSession does only exist in yii\web\Application , but not in yii\console\Application .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
![]() |
|||
225 | 2 | } |
|
226 | |||
227 | /** |
||
228 | * @var Otp|null an otp instance use to generate and validate otp. |
||
229 | */ |
||
230 | private $_otp; |
||
231 | |||
232 | /** |
||
233 | * Get an otp instance. |
||
234 | * |
||
235 | * @return Otp|null an otp instance use to generate and validate otp. |
||
236 | * @throws \yii\base\InvalidConfigException |
||
237 | */ |
||
238 | 8 | public function getOtp() |
|
239 | { |
||
240 | 8 | if ($this->_otp === null) { |
|
241 | 7 | $this->setOtp(Otp::class); |
|
242 | } |
||
243 | |||
244 | 8 | return $this->_otp; |
|
245 | } |
||
246 | |||
247 | /** |
||
248 | * Set an otp instance use to generate and validate otp. |
||
249 | * |
||
250 | * @param array|string|Otp $otp object instance |
||
251 | * @throws \yii\base\InvalidConfigException |
||
252 | */ |
||
253 | 8 | public function setOtp($otp) |
|
254 | { |
||
255 | 8 | if (is_array($otp) && !isset($otp['class'])) { |
|
256 | 1 | $otp['class'] = Otp::class; |
|
257 | } |
||
258 | |||
259 | 8 | $this->_otp = Instance::ensure($otp, Otp::class); |
|
260 | 8 | } |
|
261 | |||
262 | /** |
||
263 | * Generate an otp by current user logged in |
||
264 | * |
||
265 | * @return string|null an otp of current user logged in. |
||
266 | * @throws \yii\base\InvalidConfigException |
||
267 | * @throws \yii\base\NotSupportedException |
||
268 | */ |
||
269 | 4 | View Code Duplication | public function generateOtpByIdentityLoggedIn() |
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
270 | { |
||
271 | 4 | $data = $this->getIdentityLoggedIn(); |
|
272 | |||
273 | 4 | if (is_array($data)) { |
|
274 | |||
275 | /** @var IdentityInterface $identity */ |
||
276 | 4 | $identity = $data[0]; |
|
277 | 4 | $secretKey = $identity->getMfaSecretKey(); |
|
278 | |||
279 | 4 | if (!empty($secretKey)) { |
|
280 | 4 | return $this->getOtp()->generate($secretKey); |
|
281 | } |
||
282 | } |
||
283 | |||
284 | return null; |
||
285 | } |
||
286 | |||
287 | /** |
||
288 | * Validate an otp by current user logged in |
||
289 | * |
||
290 | * @param string $otp need to be validate |
||
291 | * @return bool weather an otp given is valid with identity logged in |
||
292 | * @throws \yii\base\InvalidConfigException |
||
293 | * @throws \yii\base\NotSupportedException |
||
294 | */ |
||
295 | 3 | View Code Duplication | public function validateOtpByIdentityLoggedIn(string $otp) |
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
296 | { |
||
297 | 3 | $data = $this->getIdentityLoggedIn(); |
|
298 | |||
299 | 3 | if (is_array($data)) { |
|
300 | /** @var IdentityInterface $identity */ |
||
301 | 3 | $identity = $data[0]; |
|
302 | 3 | $secretKey = $identity->getMfaSecretKey(); |
|
303 | |||
304 | 3 | if (!empty($secretKey)) { |
|
305 | 3 | return $this->getOtp()->validate($secretKey, $otp); |
|
306 | } |
||
307 | } |
||
308 | |||
309 | return false; |
||
310 | } |
||
311 | |||
312 | /** |
||
313 | * Return a qr code uri of current user |
||
314 | * |
||
315 | * @param array $params list of information use to show on an authenticator app. |
||
316 | * |
||
317 | * Example: |
||
318 | * ```php |
||
319 | * ['issuer' => 'VXM', 'label' => '[email protected]', 'image' => 'https://google.com'] |
||
320 | * ``` |
||
321 | * |
||
322 | * @return string|null qr code uri. If `null`, it means the user is a guest or not enable mfa. |
||
323 | * @throws \Throwable |
||
324 | */ |
||
325 | 4 | public function getQrCodeUri(array $params) |
|
326 | { |
||
327 | 4 | if ($identity = $this->owner->getIdentity()) { |
|
328 | 3 | if (!$identity instanceof IdentityInterface) { |
|
329 | throw new InvalidValueException("{$this->owner->identityClass}::findIdentity() must return an object implementing \\vxm\\mfa\\IdentityInterface."); |
||
330 | } else { |
||
331 | 3 | $secretKey = $identity->getMfaSecretKey(); |
|
332 | |||
333 | 3 | if (!empty($secretKey)) { |
|
334 | 3 | return $this->getOtp()->getQrCodeUri($secretKey, $params); |
|
335 | } |
||
336 | } |
||
337 | } |
||
338 | |||
339 | 1 | return null; |
|
340 | } |
||
341 | |||
342 | /** |
||
343 | * Redirects the user browser to the mfa verify page.. |
||
344 | * |
||
345 | * Make sure you set [[verifyUrl]] so that the user browser can be redirected to the specified verify URL after |
||
346 | * calling this method. |
||
347 | * |
||
348 | * Note that when [[verifyUrl]] is set, calling this method will NOT terminate the application execution. |
||
349 | * |
||
350 | * @return \yii\web\Response the redirection response if [[verifyUrl]] is set |
||
351 | * @throws ForbiddenHttpException |
||
352 | */ |
||
353 | 11 | protected function verifyRequired() |
|
354 | { |
||
355 | 11 | if ($this->verifyUrl !== null) { |
|
356 | 11 | $verifyUrl = (array)$this->verifyUrl; |
|
357 | |||
358 | 11 | if ($verifyUrl[0] !== Yii::$app->requestedRoute) { |
|
359 | 11 | return Yii::$app->getResponse()->redirect($this->verifyUrl); |
|
360 | } |
||
361 | } |
||
362 | |||
363 | throw new ForbiddenHttpException(Yii::t('app', 'Mfa verify required!')); |
||
364 | } |
||
365 | |||
366 | } |
||
367 |
It seems like the method you are trying to call exists only in some of the possible types.
Let’s take a look at an example:
Available Fixes
Add an additional type-check:
Only allow a single type to be passed if the variable comes from a parameter: