淺談TOTP(Time-Based One-Time Password)
TOTP 是利用 Application and services 上時間是一致,拿二邊共同的 key ,並且作一樣的 Hash 後,結果會產生一樣的 token,即可拿來作驗證,常被使用來作二次驗證(two-step verification)。 因為這部份的演算法是 RFC6238,所以可使用任何的 apps 來協助驗證,例如:Google Authenticator, Duo 之類的。
怎麼實作的
PHP 有人作好了: otphp 跟據 wikipedia 裡的 Implementation 章節中,有一段描述如何產生 token:
- Calculate C as the number of times TI has elapsed after T0.
- Compute the HMAC hash H with C as the message and K as the key (the HMAC algorithm is defined in the previous section, but also most cryptographical libraries support it). K should be passed as it is, C should be passed as a raw 64-bit unsigned integer.
- Take the least 4 significant bits of H and use it as an offset, O.
- Take 4 bytes from H starting at O bytes MSB, discard the most significant bit and store the rest as an (unsigned) 32-bit integer, I.
- The token is the lowest N digits of I in base 10. If the result has fewer digits than N, pad it with zeroes from the left.
如果覺得太復雜,我挖了一下 otphp,應該在 otphp/src/OTP.php 裡面的 protected function generateOTP($input)
中有實作。
使用
安裝 composer require spomky-labs/otphp
sample code:
<?php require __DIR__ . '/vendor/autoload.php';
use OTPHP\TOTP;
$totp = new TOTP( "whatup.tw@gmail.com" // The label (string) );
$google_chart = $totp->getQrCodeUri();
$otpCode = $totp->now();
echo "<img src='{$google_chart}'><br/>";
echo "Current OTP: " . $totp->now() . "<br/>\n";
echo "Current Secret: " . $totp->getSecret() . "<br/>\n";
echo "Verify OTP: " . $totp->verify($otpCode) . "<br/>\n";
?>
到 browser 執行後會產生下面畫面:
接下來你就拿出你的 Google Authenticator 來掃這個條碼,就可以新增一組新驗證碼。
Google Authenticator 預設是 30s ,所以 30s 後,這個 OTP 產生出來的 token 應該會無法使用。如果你的 application 可以看到 secret 的話,應該會看到 app & service 的 secret 應該要一樣。 剩下的可以參考 document 說的很清楚。
限制
- 其實無法防止 phishing 網頁用假的頁面來騙你資料,在你輸入 token 後,壞人在 30s 之內也可以同時登入。
- secret key 如果被人拿走,別人也同時擁有通過二次驗證的能力。
- app & services 之間的時間一定不能差太多,所以手機不能調整時間(有人喜歡看時鐘快五分鐘)。