Key.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. <?php
  2. /**
  3. * Second authentication factor handling
  4. */
  5. declare(strict_types=1);
  6. namespace PhpMyAdmin\Plugins\TwoFactor;
  7. use PhpMyAdmin\Plugins\TwoFactorPlugin;
  8. use PhpMyAdmin\Response;
  9. use PhpMyAdmin\TwoFactor;
  10. use Samyoul\U2F\U2FServer\U2FException;
  11. use Samyoul\U2F\U2FServer\U2FServer;
  12. use stdClass;
  13. use Throwable;
  14. use Twig\Error\LoaderError;
  15. use Twig\Error\RuntimeError;
  16. use Twig\Error\SyntaxError;
  17. use function json_decode;
  18. use function json_encode;
  19. /**
  20. * Hardware key based two-factor authentication
  21. *
  22. * Supports FIDO U2F tokens
  23. */
  24. class Key extends TwoFactorPlugin
  25. {
  26. /** @var string */
  27. public static $id = 'key';
  28. /**
  29. * Creates object
  30. *
  31. * @param TwoFactor $twofactor TwoFactor instance
  32. */
  33. public function __construct(TwoFactor $twofactor)
  34. {
  35. parent::__construct($twofactor);
  36. if (isset($this->twofactor->config['settings']['registrations'])) {
  37. return;
  38. }
  39. $this->twofactor->config['settings']['registrations'] = [];
  40. }
  41. /**
  42. * Returns array of U2F registration objects
  43. *
  44. * @return array
  45. */
  46. public function getRegistrations()
  47. {
  48. $result = [];
  49. foreach ($this->twofactor->config['settings']['registrations'] as $index => $data) {
  50. $reg = new stdClass();
  51. $reg->keyHandle = $data['keyHandle'];
  52. $reg->publicKey = $data['publicKey'];
  53. $reg->certificate = $data['certificate'];
  54. $reg->counter = $data['counter'];
  55. $reg->index = $index;
  56. $result[] = $reg;
  57. }
  58. return $result;
  59. }
  60. /**
  61. * Checks authentication, returns true on success
  62. *
  63. * @return bool
  64. */
  65. public function check()
  66. {
  67. $this->provided = false;
  68. if (! isset($_POST['u2f_authentication_response'], $_SESSION['authenticationRequest'])) {
  69. return false;
  70. }
  71. $this->provided = true;
  72. try {
  73. $response = json_decode($_POST['u2f_authentication_response']);
  74. if ($response === null) {
  75. return false;
  76. }
  77. $auth = U2FServer::authenticate(
  78. $_SESSION['authenticationRequest'],
  79. $this->getRegistrations(),
  80. $response
  81. );
  82. $this->twofactor->config['settings']['registrations'][$auth->index]['counter'] = $auth->counter;
  83. $this->twofactor->save();
  84. return true;
  85. } catch (U2FException $e) {
  86. $this->message = $e->getMessage();
  87. return false;
  88. }
  89. }
  90. /**
  91. * Loads needed javascripts into the page
  92. *
  93. * @return void
  94. */
  95. public function loadScripts()
  96. {
  97. $response = Response::getInstance();
  98. $scripts = $response->getHeader()->getScripts();
  99. $scripts->addFile('vendor/u2f-api-polyfill.js');
  100. $scripts->addFile('u2f.js');
  101. }
  102. /**
  103. * Renders user interface to enter two-factor authentication
  104. *
  105. * @return string HTML code
  106. */
  107. public function render()
  108. {
  109. $request = U2FServer::makeAuthentication(
  110. $this->getRegistrations(),
  111. $this->getAppId(true)
  112. );
  113. $_SESSION['authenticationRequest'] = $request;
  114. $this->loadScripts();
  115. return $this->template->render('login/twofactor/key', [
  116. 'request' => json_encode($request),
  117. 'is_https' => $GLOBALS['PMA_Config']->isHttps(),
  118. ]);
  119. }
  120. /**
  121. * Renders user interface to configure two-factor authentication
  122. *
  123. * @return string HTML code
  124. *
  125. * @throws U2FException
  126. * @throws Throwable
  127. * @throws LoaderError
  128. * @throws RuntimeError
  129. * @throws SyntaxError
  130. */
  131. public function setup()
  132. {
  133. $registrationData = U2FServer::makeRegistration(
  134. $this->getAppId(true),
  135. $this->getRegistrations()
  136. );
  137. $_SESSION['registrationRequest'] = $registrationData['request'];
  138. $this->loadScripts();
  139. return $this->template->render('login/twofactor/key_configure', [
  140. 'request' => json_encode($registrationData['request']),
  141. 'signatures' => json_encode($registrationData['signatures']),
  142. 'is_https' => $GLOBALS['PMA_Config']->isHttps(),
  143. ]);
  144. }
  145. /**
  146. * Performs backend configuration
  147. *
  148. * @return bool
  149. */
  150. public function configure()
  151. {
  152. $this->provided = false;
  153. if (! isset($_POST['u2f_registration_response'], $_SESSION['registrationRequest'])) {
  154. return false;
  155. }
  156. $this->provided = true;
  157. try {
  158. $response = json_decode($_POST['u2f_registration_response']);
  159. if ($response === null) {
  160. return false;
  161. }
  162. $registration = U2FServer::register(
  163. $_SESSION['registrationRequest'],
  164. $response
  165. );
  166. $this->twofactor->config['settings']['registrations'][] = [
  167. 'keyHandle' => $registration->getKeyHandle(),
  168. 'publicKey' => $registration->getPublicKey(),
  169. 'certificate' => $registration->getCertificate(),
  170. 'counter' => $registration->getCounter(),
  171. ];
  172. return true;
  173. } catch (U2FException $e) {
  174. $this->message = $e->getMessage();
  175. return false;
  176. }
  177. }
  178. /**
  179. * Get user visible name
  180. *
  181. * @return string
  182. */
  183. public static function getName()
  184. {
  185. return __('Hardware Security Key (FIDO U2F)');
  186. }
  187. /**
  188. * Get user visible description
  189. *
  190. * @return string
  191. */
  192. public static function getDescription()
  193. {
  194. return __('Provides authentication using hardware security tokens supporting FIDO U2F.');
  195. }
  196. }