File: login-otp.php

package info (click to toggle)
adminer 5.2.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 2,720 kB
  • sloc: php: 27,656; javascript: 1,178; xml: 107; makefile: 48; sh: 3
file content (79 lines) | stat: -rw-r--r-- 2,475 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<?php

/** Require One-Time Password at login
* @link https://www.adminer.org/plugins/otp/
* @author Jakub Vrana, https://www.vrana.cz/
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
*/
class AdminerLoginOtp extends Adminer\Plugin {
	protected $secret;

	/**
	* @param string $secret decoded secret, e.g. base64_decode("SECRET")
	*/
	function __construct(string $secret) {
		$this->secret = $secret;
		if ($_POST["auth"]) {
			$_SESSION["otp"] = (string) $_POST["auth"]["otp"];
		}
	}

	function loginFormField($name, $heading, $value) {
		if ($name == 'password') {
			return $heading . $value . "\n"
				. "<tr><th><abbr title='" . $this->lang('One Time Password') . "'>OTP</abbr>"
				. "<td><input type='number' name='auth[otp]' value='" . Adminer\h($_SESSION["otp"]) . "' size='6' autocomplete='one-time-code' inputmode='numeric' maxlength='6' pattern='\d{6}'>\n"
			;
		}
	}

	function login($login, $password) {
		if (isset($_SESSION["otp"])) {
			$timeSlot = floor(time() / 30);
			foreach (array(0, -1, 1) as $skew) {
				if ($_SESSION["otp"] == $this->getOtp($timeSlot + $skew)) {
					Adminer\restart_session();
					unset($_SESSION["otp"]);
					Adminer\stop_session();
					return;
				}
			}
			return $this->lang('Invalid OTP.');
		}
	}

	function getOtp($timeSlot) {
		$data = str_pad(pack('N', $timeSlot), 8, "\0", STR_PAD_LEFT);
		$hash = hash_hmac('sha1', $data, $this->secret, true);
		$offset = ord(substr($hash, -1)) & 0xF;
		$unpacked = unpack('N', substr($hash, $offset, 4));
		return ($unpacked[1] & 0x7FFFFFFF) % 1e6;
	}

	function screenshot() {
		return "https://www.adminer.org/static/login-otp.png";
	}

	protected $translations = array(
		'cs' => array(
			'' => 'Při přihlášení požaduje jednorázové heslo',
			'One Time Password' => 'Jednorázové heslo',
			'Invalid OTP.' => 'Neplatné jednorázové heslo.',
		),
		'de' => array(
			'' => 'Bei der Anmeldung ist ein Einmalpasswort (Zwei-Faktor-Authentifizierung) erforderlich',
			'One Time Password' => 'Einmal-Passwort',
			'Invalid OTP.' => 'Ungültiger OTP.',
		),
		'pl' => array(
			'' => 'Wymagaj jednorazowego hasła przy logowaniu',
		),
		'ro' => array(
			'' => 'Cereți o parolă unică la autentificare',
		),
		'ja' => array(
			'' => 'ログイン時にワンタイムパスワード (二要素認証) が必要',
		),
	);
}