シムノート

PHPフレームワークSymfonyの学習帳

ユーザ用ツール

サイト用ツール


サイドバー

メニュー



このエントリーをはてなブックマークに追加

blog:2015-12-15:超入門_symfony3_security

超入門 Symfony3 : (12) Security

Security

ここでは、Symfonyのセキュリティ機能を学びます。 前半で、セキュリティ機能の概要や専門用語を説明し、後半で、おみくじサイトにセキュリティを実装します。

概要

セキュリティとは

ここで説明するセキュリティの要件とは以下の内容になります。

特定のリソースに対するアクセスを、特定のユーザーにのみ提供する

リソースとは、URL(領域)やModelオブジェクトMethodの実行Viewなどを指します。

特定のユーザーというのは「一般ユーザー」「管理者」といった、役割(Role)を持ったユーザの事を指します。

上記の要件を満たす為に、具体的に以下の事を行います。

① 利用者にID/PWDでログインしてもらい、ユーザー情報を特定する。
② ユーザー情報が持っている役割(Role)に基づいてリソースへのアクセス可否を判断する。

上記①②の事をSymfonyでは以下のように言います。

専門用語 分かりやすく言い換えると
① Authentication(認証) ログイン
② Authrization(承認) 権限チェック

Symfonyのドキュメントを読むためには、これらの用語を覚えて下さい。

  • Authentication(認証)
  • Authrization(承認)
  • Role(役割)

設定する箇所

セキュリティの為に設定が必要な箇所は以下の3ヶ所になります。

設定箇所 具体的な場所 設定内容
① 設定ファイル security.yml 各種方法の設定
② ソースコード Controller, View 権限チェックのコーディング
③ ユーザー情報 User Entity(DB), OAuth, security.yml ID, PWD, Roleの格納

① 設定ファイルの例

security:
    # 認証方法
    firewalls:
        # ...

    # アクセス制御(承認が必要なURLとRoleの設定)
    access_control:
        # ...

    # ユーザー情報の取得方法
    providers:
        # ...

    # パスワード暗号化アルゴリズム
    encoders:
        # ...

② ソースコードの例

// Controller

public function helloAction($name)
{
    $this->denyAccessUnlessGranted('ROLE_ADMIN');

    // ...
}

{# View #}

{% if is_granted('ROLE_ADMIN') %}
    <a href="...">Delete</a>
{% endif %}

おみくじサイトのセキュリティ設定

実際におみくじサイトにセキュリティ機能を実装してみます。

仕様

運勢の管理(追加、変更、削除)を管理者のみが行えるようにする
認証方式HTTP Basic認証
アクセス制御 URL:/unsei*は管理者のみがアクセス可
ユーザー情報の取得方法設定ファイルから、ID,PWD,Roleを取得
パスワード暗号化アルゴリズム bcrypt

上記の仕様から、設定が必要な箇所はsecurity.ymlだけになります。

設定箇所 具体的な場所 設定内容
設定ファイル security.yml 各種方法の設定
ユーザー情報 security.yml ID, PWD, Roleの格納

認証方法の設定

デフォルトの認証方式

app/config/security.ymlの初期状態は以下のようになっています。

security:
    providers:
        in_memory:
            memory: ~

    # ①
    firewalls:
        # ②
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        # ③
        main:
            # ④
            anonymous: ~
            # http_basic: ~
            # form_login: ~

firewallsが認証方法の設定するセクションになります。

dev firewallは開発ツールの為の設定なので無視してください。“/_profile”や“/_wdt”のようなURLをセキュリティの対象から外しています。

main firewallが全てのURLをハンドリングします。patternが設定されていないことが、全てのURLをハンドリングすることを意味します。

④ デフォルトで、main firewallはanonymous認証を備えています。anonymous認証はサイトにアクセスしてきたユーザーをログインなしで匿名ユーザーとして扱う方法です。(全員を自動でanonymousとしてログインさせているとも言える?)

http://localhost:8000/omikuji にアクセスすると、画面下のツールバーでanon(anonymous)ユーザーで認証されていることが分かります。

Http Basic認証の追加

firewallのメインの仕事は、どの様な方法でユーザー認証をするかを設定することです。ログインフォームやHTTP Basic認証、APIトークン等…

ここではmain firewallに HTTP Basic認証を追加します。

security:
    ...
    firewalls:
        ...
        main:
            anonymous: ~
            http_basic: ~

これだけです。今のところ何処にもアクセス制御をしていないので、おみくじサイトには何の影響もでていません。

アクセス制御の設定

次に、access_controllを追加して、承認(権限のチェック)が必要なURLを設定します。権限チェックをするには、ユーザーを特定する必要がある為、ここで指定したURLにアクセスするとログイン画面が表示されることになります。

security:
    ...
    firewalls:
        ...
    access_control:
        - { path: ^/unsei, roles: ROLE_ADMIN }

/unsei以下のURLにアクセスするにはROLE_ADMIN(管理者)である必要があります。
access_controllを定義する階層に注意して下さい。firewallsと同じ階層に定義します。

ここで、http://localhost:8000/unsei にアクセスすると以下の様なHTTP Basic認証のログイン画面が表示されます。

まだ、ユーザー情報を準備していない為、ログインは出来ません。

ユーザー情報の取得方法の設定

ユーザーIDやパスワードが入力された時、Symfonyは何処かからユーザー情報を読み込んで突き合わせる必要があります。このユーザー情報を読み込む方法(ユーザー情報を提供する方法)をUser providerと言います。SymfonyではユーザーをDBから読み込む方法や自分で作成するカスタムUser provider等から読み込む方法を提供しています。

ここでは、最も簡単に利用できるin_memory providerを使います。in_memory providerはsecurity.ymlに記述してあるユーザー情報を読み込む方法です。

User providersecurity.ymlprovidersセクションに設定します。in_memoryはデフォルトで存在しています。そこにユーザー情報を追記します。

security:
    providers:
        in_memory:
            memory:
                users:
                    user:
                        password: user123
                        roles: 'ROLE_USER'
                    admin:
                        password: admin123
                        roles: 'ROLE_ADMIN'
    ...

今のところ、パスワードは平文のテキストとしておきます。
Roleは“ROLE_“で始まる文字列なら何を使っても構いません。Role文字列にどの様な意味(役割)を持たせるかは、承認の設定次第です(ROLE_MASTERでないと/adminが見れないとか…別にROLE_JEDIでもROLE_PADAWANでも良いのです)。

ここで http://localhost:8000/unsei にアクセスしてadminでログインしてみてください。以下の様なエラーが発生するはずです。

No encoder has been configured for account "Symfony\Component\Security\Core\User\User"

No encoder has been configuredというエラーは、「まだパスワードの暗号化方法が設定されていない」というエラーです。

パスワード暗号化方法の設定

encodersセクションを追加して、パスワード暗号化方法の設定を行います。

平文テキスト

まずはパスワードを平文のテキストで扱います。

security:
    ...
    
    encoders:
        Symfony\Component\Security\Core\User\User: plaintext
    ...

User providerはユーザー情報を読み込むと、その内容をUserクラスのオブジェクトに格納します。もし、DBからユーザー情報を取得する場合は、自分自身で作成するUserクラスを使用することになります。in memoty providerの場合は、Symfony\Component\Security\Core\User\Userというクラスを使用することになっています。 そして、そのUserクラスで使用しているパスワードの暗号化アルゴリズムをSymfonyに教えてあげる必要があります。

現在、in memory providerを使用していて、パスワードは平文で書かれている為、Symfony\Component\Security\Core\User\Userに対してpaintextアルゴリズムを指定します。

Symfonyでは複数のUser providerが混在することも可能な為、暗号化アルゴリズムは各User providerが使用するUserクラス毎に設定することになります。

ここで http://localhost:8000/unsei にアクセスしてadminでログインしてみてください。画面下のツールバーでadminで認証されたことが確認できます。

ユーザーパスワードの暗号化

次に平文テキストから、bcryptアルゴリズムでの暗号化に変更します。

security:
    ...
    
    encoders:
        Symfony\Component\Security\Core\User\User:
            algorithm: bcrypt
            cost: 12
    ...

security.yml内のパスワードも暗号化された物に書き換える必要があります。
暗号化されたパスワードを生成するにはconsoleコマンドを使用します。
先ほど変更したsecurity.ymlを一旦保存してから実行してください。consoleコマンドはsecurity.yml内のencoders設定を参照しています。

$ php bin/console security:encode-password

Symfony Password Encoder Utility
================================

 Type in your password to be encoded:
 > admin123 [enter]

 ------------------ --------------------------------------------------------------- 
  Key                Value                                                          
 ------------------ --------------------------------------------------------------- 
  Encoder used       Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder  
  Encoded password   $2y$12$JGUn8Q0SM49zYcZzH/VCWexKF6Zzh5dFra7aUdpsTw3AAzMnUyvJW   
 ------------------ --------------------------------------------------------------- 

 ! [NOTE] Bcrypt encoder used: the encoder generated its own built-in salt.                                             

                                                                                                                        
 [OK] Password encoding succeeded

security.yml内のパスワードを暗号化された物に変更します。

security:
    providers:
        in_memory:
            memory:
                users:
                    user:
                        password: $2y$12$wZ2X4JCakB5DWnOHMMjqCeOfl1dFZgXtzmjo7EaOftf2.LeMD3QNC
                        roles: 'ROLE_USER'
                    admin:
                        password: $2y$12$JGUn8Q0SM49zYcZzH/VCWexKF6Zzh5dFra7aUdpsTw3AAzMnUyvJW
                        roles: 'ROLE_ADMIN'
    ...

http://localhost:8000/unsei にアクセスしてadminでログインしてみてください。ログインできるはずです。

ログインのテストをする時は一旦ブラウザを終了してから行って下さい。HTTP Basic認証はブラウザが終了するまで、ログインしたままになります。一旦ログインすると、ブラウザが終了するまでログイン画面が出てこないことに注意してください。

運勢の管理(追加、変更、削除)を管理者だけが出来るようになったので、おみくじサイトのセキュリティ設定はこれで完了です。やりました!

ソースコードでの承認(権限チェック)

アプリケーションの仕様によっては、コントローラーやビューのコード上で極め細かく権限のチェックをする必要もあります。以下のように承認します。

Controller

RoleがROLE_ADMINでない場合、アクセス拒否をします。

public function helloAction($name)
{
    $this->denyAccessUnlessGranted('ROLE_ADMIN');

    // ...
}

アノテーションを使ってアクセス拒否をすることも出来ます。

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;

/**
 * @Security("has_role('ROLE_ADMIN')")
 */
public function helloAction($name)
{
    // ...
}

管理者権限の時のみ…

public function helloAction($name)
{
    if ($this->isGranted('ROLE_ADMIN')) {
        // ...
    }
    
    // ...
}

View

RoleがROLE_ADMINの時、Deleteリンクを表示します。

{# View #}

{% if is_granted('ROLE_ADMIN') %}
    <a href="...">Delete</a>
{% endif %}

ユーザーオブジェクトの取得

現在ログインしているユーザーオブジェクトは以下のように取得します。

Controller

ログインユーザーの取得。

public function helloAction($name)
{
    $user = $this->getUser();

    // ...
}

ログインしていない時はnullが返って来ます。ログインしているかどうかの判断にも使えます。

public function helloAction($name)
{
    if ($this->getUser()) {
        // Do something
    }

    // ...
}

View

ログインユーザーの取得。

{# View #}

{{ app.user.name }}

ログインしていない時はnullが返って来ます。 ログインしているかどうかの判断にも使えます。

{# View #}

{% if app.user %}
    <a href="...">Delete</a>
{% endif %}


Comments



227 -6 =᠎ ?
blog/2015-12-15/超入門_symfony3_security.txt · 最終更新: 2016/02/09 16:00 by tsubo