PHPixie 3による安全な認証

画像
今日、 PHPixie 3の最も待望のコンポーネント-ユーザー認証の認証がリリースされました。 承認はあらゆるアプリケーションの最も重要な部分であり、正しくすることは難しく、多くのユーザーは、特にオープンソースに関しては、エラーを漏らす可能性があります。 廃止されたハッシュ関数、暗号的に安全でない乱数ジェネレーター、および不正なCookie処理の使用は一般的です。 Laravel古い脆弱性についてはすでに書きましたが、これは完全には修正されていません。 したがって、PHPixie Authでは、認証、特に長いセッションとCookieに非常に注意を払ってきました。

ちなみに、記事の最後に、非常に良いニュースがあります(ネタバレ:PHPixieは現在PHP-FIGのメンバーです)

PHPixie Authを安全にする理由:


最後の項目は最も興味深いものであり、PHPフレームワークの中にはすぐに使用できる類似物はありません。 一番下の行は、ログイントークンを保存するための別のテーブルです。
  1. ログインすると、いくつかのランダムな行が作成されます。Cookieの形式でユーザーに与えられるシリーズ識別子とパスワード
  2. パスワード付きのシリーズのハッシュが作成され、ユーザーIDと有効期限とともにデータベースに記録されます
  3. 再度サイトにアクセスすると、Cookieハッシュがデータベース内のハッシュと比較され、一致する場合、ログインが発生し、古いトークンが削除され、新しいトークンがユーザーに対して作成されますが、同じシリーズ
  4. ハッシュが一致しない場合、誰かがCookieを盗んだか、ピックアップしようとしています。 この場合、同じシリーズのすべてのトークンが削除されます

このアプローチにより、ユーザーは複数のデバイス(1つのデバイス-1つのシリーズ)に同時にログオンできます。 たとえば、Laravelはユーザーテーブルにトークンを保存するだけであるため、ユーザーはすべてのデバイスで1つのトークンしか持つことができません。

構成

他のPHPixieコンポーネントと同様に、Authもフレームワークなしで使用できますが、簡単にするために、この場合についてのみ説明します。

まず、ユーザーリポジトリが必要です。各バンドルは独自のリポジトリを提供できます。そこから、構成ファイルで既に必要なリポジトリを選択します。 ORMを使用してユーザーと作業する場合、authには既製のラッパーが付属しています。

namespace Project\App\ORMWrappers\User; //   class Repository extends \PHPixie\AuthORM\Repositories\Type\Login { //      //      protected function loginFields() { return array('username', 'email'); } } 


 namespace Project\App\ORMWrappers\User; //   class Entity extends \PHPixie\AuthORM\Repositories\Type\Login\User { //      protected function passwordHash() { return $this->password; } } 


ORMWrappers.phpに登録することを忘れないでください

 namespace Project\App; class ORMWrappers extends \PHPixie\ORM\Wrappers\Implementation { protected $databaseEntities = array('user'); protected $databaseRepositories = array('user'); public function userEntity($entity) { return new ORMWrappers\User\Entity($entity); } public function userRepisitory($repository) { return new ORMWrappers\User\Repository($repository); } } 


次に、このリポジトリをバンドルに登録します。

 namespace Project\App; class AuthRepositories extends \PHPixie\Auth\Repositories\Registry\Builder { protected $builder; public function __construct($builder) { $this->builder = $builder; } protected function buildUserRepository() { $orm = $this->builder->components()->orm(); return $orm->repository('user'); } } 


 namespace Project\App; class Builder extends \PHPixie\DefaultBundle\Builder { protected function buildAuthRepositories() { return new AuthRepositories($this); } } 


トークンを保存するためのテーブルを作成することも必要です(MongoDBを使用する場合、すべてがすぐに機能します)。

  CREATE TABLE `tokens` ( `series` varchar(50) NOT NULL, `userId` int(11) DEFAULT NULL, `challenge` varchar(50) DEFAULT NULL, `expires` bigint(20) DEFAULT NULL, PRIMARY KEY (`series`) ) 


これで設定ファイル自体。 最も一般的なアプローチは次のようになります。

 // /assets/auth.php return array( 'domains' => array( 'default' => array( //  user   app 'repository' => 'app.user', 'providers' => array( //    'session' => array( 'type' => 'http.session' ), //   ( "remember me") 'cookie' => array( 'type' => 'http.cookie', //     session //     'persistProviders' => array('session'), //    'tokens' => array( 'storage' => array( 'type' => 'database', 'table' => 'tokens', 'defaultLifetime' => 3600*24*14 //   ) ) ), //    'password' => array( 'type' => 'login.password', //    . //       'cookies' //     "remember me"  //  ,       'persistProviders' => array('session') ) ) ) ); 


すべてのプロバイダーは互いに独立しているため、機能を簡単に変更できます。 たとえば、セッションなしでCookieのみを使用し、同時に各リクエストでトークンの更新を無効にする場合、次のようにできます。

 // /assets/auth.php return array( 'domains' => array( 'default' => array( 'cookie' => array( 'type' => 'http.cookie', //    'tokens' => array( 'storage' => array( 'type' => 'database', 'table' => 'tokens', 'defaultLifetime' => 3600*24*14, 'refresh' => false ) ) ), 'password' => array( 'type' => 'login.password', 'persistProviders' => array('cookie') ) ) ) ); 


使用する

単純なプロセッサを作成して、すべてがどのように連携するかを試してみましょう。

 namespace Project\App\HTTPProcessors; class Auth extends \PHPixie\DefaultBundle\Processor\HTTP\Actions { protected $builder; public function __construct($builder) { $this->builder = $builder; } //       public function defaultAction($request) { $user = $this->domain()->user(); return $user ? $user->username : 'not logged'; } //       public function addAction($request) { $query = $request->query(); $username = $query->get('username'); $password = $query->get('password'); $orm = $this->builder->components()->orm(); $provider = $this->domain()->provider('password'); $user = $orm->createEntity('user'); $user->username = $username; //     $user->passwordHash = $provider->hash($password); $user->save(); return 'added'; } //     public function loginAction($request) { $query = $request->query(); $username = $query->get('username'); $password = $query->get('password'); $provider = $this->domain()->provider('password'); $user = $provider->login($username, $password); if($user) { // generate persistent cookie $provider = $this->domain()->provider('cookie'); $provider->persist(); } return $user ? 'success' : 'wrong password'; } //  public function logoutAction($request) { $this->domain()->forgetUser(); return 'logged out'; } protected function domain() { $auth = $this->builder->components()->auth(); return $auth->domain(); } } 


次に、URLを調べて結果を確認します。

  1. / auth-ユーザーはログインしていません
  2. / auth / add?username = jigpuzzled&password = 5-ユーザーを作成します
  3. / auth / login?username = jigpuzzled&password = 5-ログイン
  4. / auth-ログインを確認
  5. / auth / logout-ログアウト


構成ファイルとプロセッサの両方でドメインの概念に遭遇していることにすでに気付いています。 ドメインは、リポジトリとプロバイダーを備えた個別の認証インスタンスです。 ほとんどの場合、ドメインは1つしかありません。 たとえば、フロントエンドではユーザーがFacebookログインを介してサイトにアクセスできますが、バックエンドはログインとパスワードのみでアクセスできるようにするなど、サイトのセクションに異なるログインがある場合、いくつかのドメインが必要になります。

自社プロバイダー

時間が経つにつれて、おそらくソーシャルネットワークを介した承認などのために、ログインプロバイダーを追加する必要があります。 これを行うには、独自のプロバイダービルダーを作成し(たとえば、 Loginとの類推により) 、拡張機能として登録する必要があります。 詳細な説明については、別の記事を書きますが、これらの2つのリンクで開始するには十分です。

PHPixieはPHP-FIGのメンバーになりました


ありがとう、 SamDark PHPixieは明日からPHP-FIGのメンバーになります! 完全な投票スレッドはここで見ることができます 。 人気とダウンロード数が主要な選択基準の1つであるため、フレームワークのすべてのユーザーに感謝します^ __ ^

Source: https://habr.com/ru/post/J267177/


All Articles