Key.php 5.5 KB

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