Escape Studios、Symfony2のwsse-authentication-bundleでのSha512サポート

最近、タスクはトークンの作成時のセキュリティとsha512のサポートを強化することでした。 この記事は焦点を絞ったことが判明しましたが、これに遭遇したのは私だけではないと確信しています。



Symfony2でオンラインストアのAPIをプログラミングする際の現在の問題を解決するために、彼はsha512アルゴリズムを使用してFOSUserBundleWSSEAuthenticationBundleを友達にすることを決めました。 これについては、私の記事で説明します。



基本設定:



app/config/config.yml fos_user: db_driver: orm firewall_name: wsse_secured user_class: Acme\DemoBundle\Entity\User # Escape WSSE authentication configuration escape_wsse_authentication: authentication_provider_class: Escape\WSSEAuthenticationBundle\Security\Core\Authentication\Provider\Provider authentication_listener_class: Escape\WSSEAuthenticationBundle\Security\Http\Firewall\Listener authentication_entry_point_class: Escape\WSSEAuthenticationBundle\Security\Http\EntryPoint\EntryPoint authentication_encoder_class: Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder app/config/security.yml security: providers: fos_userbundle: id: fos_user.user_provider.username encoders: FOS\UserBundle\Model\UserInterface: sha512 firewalls: wsse_secured: pattern: ^/api/.* wsse: lifetime: 300 #lifetime of nonce realm: "Secured API" #identifies the set of resources to which the authentication information will apply (WWW-Authenticate) profile: "UsernameToken" #WSSE profile (WWW-Authenticate) encoder: #digest algorithm algorithm: sha512 encodeHashAsBase64: true iterations: 1 anonymous: true
      
      







コントローラーのトークン生成コード:



 src\Acme\DemoBundle\Controller\SecurityController.php //... $created = date('c'); $nonce = substr(md5(uniqid('nonce_', true)), 0, 16); $nonceHigh = base64_encode($nonce); $salted = $nonce . $created . $user->getPassword() . "{" . $user->getSalt() . "}"; $passwordDigest = hash('sha512', $salted, true); $header = "UsernameToken Username=\"{$username}\", PasswordDigest=\"{$passwordDigest}\", Nonce=\"{$nonceHigh}\", Created=\"{$created}\""; $view->setHeader("Authorization", 'WSSE profile="UsernameToken"'); $view->setHeader("X-WSSE", "UsernameToken Username=\"{$username}\", PasswordDigest=\"{$passwordDigest}\", Nonce=\"{$nonceHigh}\", Created=\"{$created}\""); $data = array('WSSE' => $header); //...
      
      





この設定をそのまま使用したかったのですが、そうではありませんでした。 理由を理解しましょう。 Escapestudiosの標準プロバイダーには、次のような行があることがわかりました。

 WSSEAuthenticationBundle/Security/Core/Authentication/Provider/Provider.php //... //validate secret $expected = $this->encoder->encodePassword( sprintf( '%s%s%s', base64_decode($nonce), $created, $secret ), "" );
      
      





最後から2番目の行にある引用符は興味深いものです。代わりに塩を追加すると、すべてが奇跡的に機能し始めます。 バンドル内のこのプロバイダーを書き換えて、状況を修正しましょう。



 src\Acme\DemoBundle\Security\Authentication\Provider\WsseProvider.php namespace Acme\DemoBundle\Security\Authentication\Provider; use Escape\WSSEAuthenticationBundle\Security\Core\Authentication\Provider\Provider; use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface; use Symfony\Component\Security\Core\Exception\CredentialsExpiredException; use Symfony\Component\Security\Core\Exception\NonceExpiredException; /** * Class WsseProvider * @package Acme\DemoBundle\Security\Authentication\Provider */ class WsseProvider extends Provider implements AuthenticationProviderInterface { /** * @param $user \Symfony\Component\Security\Core\User\UserInterface * @param $digest * @param $nonce * @param $created * @param $secret * * @return bool * @throws \Symfony\Component\Security\Core\Exception\CredentialsExpiredException * @throws \Symfony\Component\Security\Core\Exception\NonceExpiredException */ protected function validateDigest($user, $digest, $nonce, $created, $secret) { //check whether timestamp is not in the future if (strtotime($created) > time()) { throw new CredentialsExpiredException('Future token detected.'); } //expire timestamp after specified lifetime if (time() - strtotime($created) > $this->getLifetime()) { throw new CredentialsExpiredException('Token has expired.'); } //validate that nonce is unique within specified lifetime //if it is not, this could be a replay attack if ($this->getNonceCache()->contains($nonce)) { throw new NonceExpiredException('Previously used nonce detected.'); } $this->getNonceCache()->save($nonce, time(), $this->getLifetime()); //validate secret $expected = $this->getEncoder()->encodePassword( sprintf( '%s%s%s', base64_decode($nonce), $created, $secret ), $user->getSalt() ); return $digest === $expected; } }
      
      





最後に、執筆時点でバンドルのバージョンでは、構成でnonceの使用を無効にすることはできず、受信したトークンは1回だけ有効であることに注意してください。 このチェック行を変更してノンスを追加するには、単純に削除できます。



このクラスを設定に追加します。

 app/config/config.yml # Escape WSSE authentication configuration escape_wsse_authentication: authentication_provider_class: Acme\DemoBundle\Security\Authentication\Provider\WsseProvider authentication_listener_class: Escape\WSSEAuthenticationBundle\Security\Http\Firewall\Listener authentication_entry_point_class: Escape\WSSEAuthenticationBundle\Security\Http\EntryPoint\EntryPoint authentication_encoder_class: Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder
      
      





それでは、少し防御を改善しましょう。 エンコーダーの設定には、次のような反復パラメーターがあります。

 app/config/security.yml security: firewalls: wsse_secured: wsse: encoder: #digest algorithm iterations: 1
      
      





このパラメーターは、トークンのエンコード/デコード中のハッシュ反復の回数を担当します。 デフォルトでは、「1」です。 比較のために、Symfony2でパスワードをハッシュする場合、「5000」です(Symfony \ Component \ Security \ Core \ Encoder \ MessageDigestPasswordEncoder)。



この機能を実装するには、コントローラーと構成にいくつかの変更を加えます。

 app/config/security.yml parameters: wsse_iterations: 300 security: firewalls: wsse_secured: wsse: encoder: #digest algorithm iterations: %wsse_iterations% src\Acme\DemoBundle\Controller\SecurityController.php //... $created = date('c'); $nonce = substr(md5(uniqid('nonce_', true)), 0, 16); $nonceHigh = base64_encode($nonce); $container = $this->get('service_container'); $iterations = $container->getParameter('wsse_iterations'); $salted = $nonce . $created . $user->getPassword() . "{" . $user->getSalt() . "}"; $passwordDigest = hash('sha512', $salted, true); for ($i = 1; $i < $iterations; $i++) { $passwordDigest = hash('sha512', $passwordDigest . $salted, true); } $passwordDigest = base64_encode($passwordDigest); $header = "UsernameToken Username=\"{$username}\", PasswordDigest=\"{$passwordDigest}\", Nonce=\"{$nonceHigh}\", Created=\"{$created}\""; $view->setHeader("Authorization", 'WSSE profile="UsernameToken"'); $view->setHeader( "X-WSSE", "UsernameToken Username=\"{$username}\", PasswordDigest=\"{$passwordDigest}\", Nonce=\"{$nonceHigh}\", Created=\"{$created}\"" ); $data = array('WSSE' => $header); //...
      
      





実際、この記事の主なポイントはプロバイダーの1行の置き換えに限定されていますが、追加とその説明もかなり不適当です。 誰かが役に立つといいな。



DigitovビジネススクールのWeb開発のコースに招待しますはこれを教えています。 ジュニアPHP開発者になりたいです! (初心者向け)、 Symfony2。柔軟な開発 (専門家向け)、および同僚が行った開発Python / Django (初心者向け)およびRuby on Railsでの Webアプリケーション開発 専門能力開発のためのレール (初心者向け)。 今すぐコースを購読すると、割引で購入できます



投稿者: Sergey Harlanchuk、シニアPHP開発者/チームリーダー、 SECLグループ/インターネットセールステクノロジー



All Articles