検索
ホームページバックエンド開発PHPチュートリアルPHP 統合動的パスワード認証 (推奨)

PHP 統合動的パスワード認証 (推奨)

Jun 01, 2018 pm 04:43 PM
php推薦する認証

この記事では主に PHP 統合動的パスワード認証について詳しく紹介します。動的パスワードはワンタイム パスワードと無効化されたパスワードを使用してセキュリティ パフォーマンスを向上させます。興味のある方は参考にしてください。

現在、ほとんどのシステムでは ID 認証ログインに静的パスワードが使用されています。しかし、静的パスワードは盗まれやすいため、そのセキュリティはセキュリティ要件を満たしていません。

動的パスワードはワンタイムパスワードを使用し、パスワードの盗難によるセキュリティ上の問題を防ぐために使用済みのパスワードを無効にします。
動的パスワードは、HOTP (イベントカウントに基づく動的パスワード、RFC4226)、TOTP (時間カウントに基づく動的パスワード、RFC6238)、OCRA (チャレンジレスポンス動的パスワード、RFC6287) およびその他の方式に分類されます。

この記事では、TOTP 方式を統合した動的パスワード認証のソリューションを紹介します。PHP フレームワークは Thinkphp3.2.3 を使用し、動的パスワード ジェネレーターは Google 認証を使用します。

1. oath アルゴリズム クラスを Thinkphp フレームワークに追加します

oath アルゴリズムのカプセル化クラス oath.php コードは次のとおりです:

<?PHP
/**
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * PHP Google two-factor authentication module.
 *
 * See http://www.idontplaydarts.com/2011/07/google-totp-two-factor-authentication-for-php/
 * for more details
 *
 * @author Phil
 **/

class Google2FA {

 const keyRegeneration  = 30; // Interval between key regeneration
 const otpLength  = 6; // Length of the Token generated

 private static $lut = array( // Lookup needed for Base32 encoding
  "A" => 0, "B" => 1,
  "C" => 2, "D" => 3,
  "E" => 4, "F" => 5,
  "G" => 6, "H" => 7,
  "I" => 8, "J" => 9,
  "K" => 10, "L" => 11,
  "M" => 12, "N" => 13,
  "O" => 14, "P" => 15,
  "Q" => 16, "R" => 17,
  "S" => 18, "T" => 19,
  "U" => 20, "V" => 21,
  "W" => 22, "X" => 23,
  "Y" => 24, "Z" => 25,
  "2" => 26, "3" => 27,
  "4" => 28, "5" => 29,
  "6" => 30, "7" => 31
 );

 /**
  * Generates a 16 digit secret key in base32 format
  * @return string
  **/
 public static function generate_secret_key($length = 16) {
  $b32  = "234567QWERTYUIOPASDFGHJKLZXCVBNM";
  $s  = "";

  for ($i = 0; $i < $length; $i++)
   $s .= $b32[rand(0,31)];

  return $s;
 }

 /**
  * Returns the current Unix Timestamp devided by the keyRegeneration
  * period.
  * @return integer
  **/
 public static function get_timestamp() {
  return floor(microtime(true)/self::keyRegeneration);
 }

 /**
  * Decodes a base32 string into a binary string.
  **/
 public static function base32_decode($b32) {

  $b32  = strtoupper($b32);

  if (!preg_match(&#39;/^[ABCDEFGHIJKLMNOPQRSTUVWXYZ234567]+$/&#39;, $b32, $match))
   throw new Exception(&#39;Invalid characters in the base32 string.&#39;);

  $l  = strlen($b32);
  $n = 0;
  $j = 0;
  $binary = "";

  for ($i = 0; $i < $l; $i++) {

   $n = $n << 5;     // Move buffer left by 5 to make room
   $n = $n + self::$lut[$b32[$i]];  // Add value into buffer
   $j = $j + 5;    // Keep track of number of bits in buffer

   if ($j >= 8) {
    $j = $j - 8;
    $binary .= chr(($n & (0xFF << $j)) >> $j);
   }
  }

  return $binary;
 }
 /*by tang*/  
 public static function base32_encode($data, $length){
  $basestr = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
  $count = 0;
  if ($length > 0) {
   $buffer = $data[0];
   $next = 1;
   $bitsLeft = 8;

   while (($bitsLeft > 0 || $next < $length)) {
    if ($bitsLeft < 5) {
    if ($next < $length) {
     $buffer <<= 8;
     $buffer |= $data[$next++] & 0xFF;
     $bitsLeft += 8;
    } else {
     $pad = 5 - $bitsLeft;
     $buffer <<= $pad;
     $bitsLeft += $pad;
    }
    }
    $index = 0x1F & ($buffer >> ($bitsLeft - 5));
    $bitsLeft -= 5;
    $result .= $basestr[$index];
    $count++;
   }
   }
   return $result;  
 }
 /**
  * Takes the secret key and the timestamp and returns the one time
  * password.
  *
  * @param binary $key - Secret key in binary form.
  * @param integer $counter - Timestamp as returned by get_timestamp.
  * @return string
  **/
 public static function oath_hotp($key, $counter)
 {
  if (strlen($key) < 8)
  throw new Exception(&#39;Secret key is too short. Must be at least 16 base 32 characters&#39;);

  $bin_counter = pack(&#39;N*&#39;, 0) . pack(&#39;N*&#39;, $counter);  // Counter must be 64-bit int
  $hash  = hash_hmac (&#39;sha1&#39;, $bin_counter, $key, true);

  return str_pad(self::oath_truncate($hash), self::otpLength, &#39;0&#39;, STR_PAD_LEFT);
 }

 /**
  * Verifys a user inputted key against the current timestamp. Checks $window
  * keys either side of the timestamp.
  *
  * @param string $b32seed
  * @param string $key - User specified key
  * @param integer $window
  * @param boolean $useTimeStamp
  * @return boolean
  **/
 public static function verify_key($b32seed, $key, $window = 5, $useTimeStamp = true) {

  $timeStamp = self::get_timestamp();

  if ($useTimeStamp !== true) $timeStamp = (int)$useTimeStamp;

  $binarySeed = self::base32_decode($b32seed);

  for ($ts = $timeStamp - $window; $ts <= $timeStamp + $window; $ts++)
   if (self::oath_hotp($binarySeed, $ts) == $key)
    return true;

  return false;

 }

 /**
  * Extracts the OTP from the SHA1 hash.
  * @param binary $hash
  * @return integer
  **/
 public static function oath_truncate($hash)
 {
  $offset = ord($hash[19]) & 0xf;

  return (
   ((ord($hash[$offset+0]) & 0x7f) << 24 ) |
   ((ord($hash[$offset+1]) & 0xff) << 16 ) |
   ((ord($hash[$offset+2]) & 0xff) << 8 ) |
   (ord($hash[$offset+3]) & 0xff)
  ) % pow(10, self::otpLength);
 }


}
/*
$InitalizationKey = "LFLFMU2SGVCUIUCZKBMEKRKLIQ";     // Set the inital key

$TimeStamp  = Google2FA::get_timestamp();
$secretkey  = Google2FA::base32_decode($InitalizationKey); // Decode it into binary
$otp    = Google2FA::oath_hotp($secretkey, $TimeStamp); // Get current token

echo("Init key: $InitalizationKey\n");
echo("Timestamp: $TimeStamp\n");
echo("One time password: $otp\n");

// Use this to verify a key as it allows for some time drift.

$result = Google2FA::verify_key($InitalizationKey, "123456");

var_dump($result);
*/
?>

Google の動的パスワード アルゴリズムのシード キーは、base32 エンコーディングを使用するため、base32 アルゴリズムは、 php の内容は次のとおりです。

<?php
//namespace Base32;
/**
 * Base32 encoder and decoder
 *
 * Last update: 2012-06-20
 *
 * RFC 4648 compliant
 * @link http://www.ietf.org/rfc/rfc4648.txt
 *
 * Some groundwork based on this class
 * https://github.com/NTICompass/PHP-Base32
 *
 * @author Christian Riesen <chris.riesen@gmail.com>
 * @link http://christianriesen.com
 * @license MIT License see LICENSE file
 */
class Base32
{
 /**
  * Alphabet for encoding and decoding base32
  *
  * @var array
  */
 private static $alphabet = &#39;ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=&#39;;
 /**
  * Creates an array from a binary string into a given chunk size
  *
  * @param string $binaryString String to chunk
  * @param integer $bits Number of bits per chunk
  * @return array
  */
 private static function chunk($binaryString, $bits)
 {
  $binaryString = chunk_split($binaryString, $bits, &#39; &#39;);
  if (substr($binaryString, (strlen($binaryString)) - 1) == &#39; &#39;) {
   $binaryString = substr($binaryString, 0, strlen($binaryString)-1);
  }
  return explode(&#39; &#39;, $binaryString);
 }
 /**
  * Encodes into base32
  *
  * @param string $string Clear text string
  * @return string Base32 encoded string
  */
 public static function encode($string)
 {
  if (strlen($string) == 0) {
   // Gives an empty string
   return &#39;&#39;;
  }
  // Convert string to binary
  $binaryString = &#39;&#39;;
  foreach (str_split($string) as $s) {
   // Return each character as an 8-bit binary string
   $binaryString .= sprintf(&#39;%08b&#39;, ord($s));
  }
  // Break into 5-bit chunks, then break that into an array
  $binaryArray = self::chunk($binaryString, 5);
  // Pad array to be pisible by 8
  while (count($binaryArray) % 8 !== 0) {
   $binaryArray[] = null;
  }
  $base32String = &#39;&#39;;
  // Encode in base32
  foreach ($binaryArray as $bin) {
   $char = 32;
   if (!is_null($bin)) {
    // Pad the binary strings
    $bin = str_pad($bin, 5, 0, STR_PAD_RIGHT);
    $char = bindec($bin);
   }
   // Base32 character
   $base32String .= self::$alphabet[$char];
  }
  return $base32String;
 }
 /**
  * Decodes base32
  *
  * @param string $base32String Base32 encoded string
  * @return string Clear text string
  */
 public static function decode($base32String)
 {
  // Only work in upper cases
  $base32String = strtoupper($base32String);
  // Remove anything that is not base32 alphabet
  $pattern = &#39;/[^A-Z2-7]/&#39;;
  $base32String = preg_replace($pattern, &#39;&#39;, $base32String);
  if (strlen($base32String) == 0) {
   // Gives an empty string
   return &#39;&#39;;
  }
  $base32Array = str_split($base32String);
  $string = &#39;&#39;;
  foreach ($base32Array as $str) {
   $char = strpos(self::$alphabet, $str);
   // Ignore the padding character
   if ($char !== 32) {
    $string .= sprintf(&#39;%05b&#39;, $char);
   }
  }
  while (strlen($string) %8 !== 0) {
   $string = substr($string, 0, strlen($string)-1);
  }
  $binaryArray = self::chunk($string, 8);
  $realString = &#39;&#39;;
  foreach ($binaryArray as $bin) {
   // Pad each value to 8 bits
   $bin = str_pad($bin, 8, 0, STR_PAD_RIGHT);
   // Convert binary strings to ASCII
   $realString .= chr(bindec($bin));
  }
  return $realString;
 }
}

?>

これら 2 つのファイルを Thinkphp フレームワークの ThinkPHPLibraryVendoroath ディレクトリに置きます。 oath ディレクトリは自分で作成しました。

2. データベースフィールドを追加します

次のフィールドをユーザーテーブルに追加します:
auth_type (0-静的パスワード、1-動的パスワード)
seed (シードキー)
temp_seed (一時シードキー)
last_logintime (last) time ログイン成功時間)
last_otp (最後に使用したパスワード)
ここで、auth_type はユーザーが使用する認証方法を示し、seed はユーザーのシード キー、temp_seed はユーザーがアカウントをアクティブ化する前に一時的に保存されたシード キーです。動的パスワード認証が正常にアクティブ化されると、このフィールドの内容がシード フィールドに入力されます。 last_logintime と last_otp は、最後に成功した認証の時刻と動的パスワードであり、ユーザーが同じパスワードを再利用するのを防ぐために使用されます。

3. コードの統合

1)、動的パスワードをアクティブ化します

元のシステムのパスワード変更ページで、認証方法の選択を追加します。例:

PHP 統合動的パスワード認証 (推奨)

動的パスワード方式を選択すると、QR コードが生成され、ユーザーが動的パスワードをアクティブ化するためのページに表示されます。 Google認証に対応するため、QRコードの形式はGoogleと同じです。 QRコードの生成方法については、別記事「Thinkphp3.2.3でphpqrcodeを統合してロゴ付きQRコードを生成」を参照してください。
キーQRコードを生成するコードは以下の通りです:

public function qrcode()
 { 
  Vendor(&#39;oath.base32&#39;);
  $base32 = new \Base32();
  $rand = random(16);//生成随机种子
  $rand = $base32->encode($rand);
  $rand=str_replace(&#39;=&#39;,&#39;&#39;,$rand);//去除填充的‘=&#39;

  $errorCorrectionLevel =intval(3) ;//容错级别 
  $matrixPointSize = intval(8);//生成图片大小

  //生成二维码图片 
  Vendor(&#39;phpqrcode.phpqrcode&#39;);
  $object = new \QRcode();
  $text = sprintf("otpauth://totp/%s?secret=%s", $user, $rand);
  $object->png($text, false, $errorCorrectionLevel, $matrixPointSize, 2);

  生成的种子$rand保存到数据库的temp_seed字段
 }

randomはランダムな文字列を生成する関数です。コード $rand=str_replace('=','',$rand) は、Google モバイル トークンの Base32 デコード アルゴリズムが '=' 記号を埋めていないためです。

ユーザーの動的パスワードを検証するコードは次のとおりです:

从数据库读取temp_seed
Vendor(&#39;oath.oath&#39;);
$object = new \Google2FA();
if($object->verify_key($temp_seed, $otp)){
 验证成功,将数据库更新seed为temp_seed,auth_type为1,last_otp为otp
}

2)、動的パスワードのログイン

ユーザーの動的パスワードのログインを検証するコード:

データベースから auth_type、seed、last_otp フィールドを読み取ります。

if($auth_type==1){//动态口令
 //防止重复认证    
 if($lat_otp == $otp) {
  动态口令重复使用返回    
 }
 Vendor(&#39;oath.oath&#39;);
 $object = new \Google2FA();
 if(!$object->verify_key($seed, $otp))
 {
  动态口令不正确
 }
 else
 {
  登录成功,将数据库更新last_otp为$otp,last_logintime为time()
 }    
}

4. テスト検証

Google 認証をダウンロードし、静的パスワードを使用してシステムにログインし、パスワード変更ページに入ります。
Google認証を開き、QRコードをスキャンすると、動的パスワードが表示されます。

PHP 統合動的パスワード認証 (推奨)

PHP 統合動的パスワード認証 (推奨)

コンテンツを保存し、動的パスワードを正常にアクティブ化してください。
これで、ハイエンドの動的パスワードを使用してシステムにログインできます。

概要: 上記がこの記事の全内容です。皆さんの学習に役立つことを願っています。

関連おすすめ:

PHPによるAlipay即時決済機能へのアクセス

PHPの2次元配列重複排除アルゴリズムの画像とテキストによる詳細な説明

phpPOSTデータを取得する3つの方法の詳細な説明

以上がPHP 統合動的パスワード認証 (推奨)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
誇大広告を超えて:今日のPHPの役割の評価誇大広告を超えて:今日のPHPの役割の評価Apr 12, 2025 am 12:17 AM

PHPは、特にWeb開発の分野で、最新のプログラミングで強力で広く使用されているツールのままです。 1)PHPは使いやすく、データベースとシームレスに統合されており、多くの開発者にとって最初の選択肢です。 2)動的コンテンツ生成とオブジェクト指向プログラミングをサポートし、Webサイトを迅速に作成および保守するのに適しています。 3)PHPのパフォーマンスは、データベースクエリをキャッシュおよび最適化することで改善でき、その広範なコミュニティと豊富なエコシステムにより、今日のテクノロジースタックでは依然として重要になります。

PHPの弱い参照は何ですか、そしていつ有用ですか?PHPの弱い参照は何ですか、そしていつ有用ですか?Apr 12, 2025 am 12:13 AM

PHPでは、弱い参照クラスを通じて弱い参照が実装され、ガベージコレクターがオブジェクトの回収を妨げません。弱い参照は、キャッシュシステムやイベントリスナーなどのシナリオに適しています。オブジェクトの生存を保証することはできず、ごみ収集が遅れる可能性があることに注意する必要があります。

PHPで__invoke Magicメソッドを説明してください。PHPで__invoke Magicメソッドを説明してください。Apr 12, 2025 am 12:07 AM

\ _ \ _ Invokeメソッドを使用すると、オブジェクトを関数のように呼び出すことができます。 1。オブジェクトを呼び出すことができるように\ _ \ _呼び出しメソッドを定義します。 2。$ obj(...)構文を使用すると、PHPは\ _ \ _ Invokeメソッドを実行します。 3。ロギングや計算機、コードの柔軟性の向上、読みやすさなどのシナリオに適しています。

同時性については、PHP 8.1の繊維を説明します。同時性については、PHP 8.1の繊維を説明します。Apr 12, 2025 am 12:05 AM

繊維はPhp8.1で導入され、同時処理機能が改善されました。 1)繊維は、コルーチンと同様の軽量の並行性モデルです。 2)開発者がタスクの実行フローを手動で制御できるようにし、I/O集約型タスクの処理に適しています。 3)繊維を使用すると、より効率的で応答性の高いコードを書き込むことができます。

PHPコミュニティ:リソース、サポート、開発PHPコミュニティ:リソース、サポート、開発Apr 12, 2025 am 12:04 AM

PHPコミュニティは、開発者の成長を支援するための豊富なリソースとサポートを提供します。 1)リソースには、公式のドキュメント、チュートリアル、ブログ、LaravelやSymfonyなどのオープンソースプロジェクトが含まれます。 2)StackOverFlow、Reddit、およびSlackチャネルを通じてサポートを取得できます。 3)開発動向は、RFCに従うことで学ぶことができます。 4)コミュニティへの統合は、積極的な参加、コード共有への貢献、および学習共有への貢献を通じて達成できます。

PHP対Python:違いを理解しますPHP対Python:違いを理解しますApr 11, 2025 am 12:15 AM

PHP and Python each have their own advantages, and the choice should be based on project requirements. 1.PHPは、シンプルな構文と高い実行効率を備えたWeb開発に適しています。 2。Pythonは、簡潔な構文とリッチライブラリを備えたデータサイエンスと機械学習に適しています。

PHP:それは死にかけていますか、それとも単に適応していますか?PHP:それは死にかけていますか、それとも単に適応していますか?Apr 11, 2025 am 12:13 AM

PHPは死にかけていませんが、常に適応して進化しています。 1)PHPは、1994年以来、新しいテクノロジーの傾向に適応するために複数のバージョンの反復を受けています。 2)現在、電子商取引、コンテンツ管理システム、その他の分野で広く使用されています。 3)PHP8は、パフォーマンスと近代化を改善するために、JITコンパイラおよびその他の機能を導入します。 4)Opcacheを使用してPSR-12標準に従って、パフォーマンスとコードの品質を最適化します。

PHPの未来:適応と革新PHPの未来:適応と革新Apr 11, 2025 am 12:01 AM

PHPの将来は、新しいテクノロジーの傾向に適応し、革新的な機能を導入することで達成されます。1)クラウドコンピューティング、コンテナ化、マイクロサービスアーキテクチャに適応し、DockerとKubernetesをサポートします。 2)パフォーマンスとデータ処理の効率を改善するために、JITコンパイラと列挙タイプを導入します。 3)パフォーマンスを継続的に最適化し、ベストプラクティスを促進します。

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

WebStorm Mac版

WebStorm Mac版

便利なJavaScript開発ツール

MantisBT

MantisBT

Mantis は、製品の欠陥追跡を支援するために設計された、導入が簡単な Web ベースの欠陥追跡ツールです。 PHP、MySQL、Web サーバーが必要です。デモおよびホスティング サービスをチェックしてください。

SublimeText3 Linux 新バージョン

SublimeText3 Linux 新バージョン

SublimeText3 Linux 最新バージョン

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター