アクセス制御<br />Zend_Aclin ServiceLayer<br />
CWE(Common Weakness Enumeration)~脆弱性の種類を識別するための共通の脆弱性タイプの一覧~<br />http://cwe.mitre.org/top25/<br />http://www.ipa.go.jp/se...
脆弱性タイプの一覧<br />1位:XSS<br />2位:SQLインジェクション<br />3位:バッファオーバーフロー<br />4位:CSRF<br />5位:不正確なアクセス制御<br />制御方法がさまざまでノウハウが蓄積されにくい?...
手動アクセス制御の失敗<br />アクセス制御を個別メソッドに書く限りうっかりミスは防げない。<br />動作とアクセス制御は分離するべき<br />テストで防げる?<br />ついうっかりが発生するシチュエーションではテスト仕様自体に漏れがあ...
サイバー・ノーガード戦法<br />対策は<br />例えば、PHPを避ける<br />サービスレイヤーでアクセス制御<br />
アクセス制御<br />アクセス制御は脆弱性ランキング5位<br />コントローラーでアクセス制御<br />ZF+ OSSで見るアクセス制御の実例<br />サービス層でリファレンスモニタを使う<br />テーマ<br />
アクセス制御用コンポーネント<br />Zend_Acl(承認)<br />ロールベースアクセス制御(RBAC)<br />Zend_Auth(認証)<br />認証アダプタを利用した認証<br />DBアダプタを利用するとユーザーの関連情報も...
クィックスタート<br />http://framework.zend.com/manual/ja/learning.multiuser.html<br />Zend Frameworkによるマルチユーザーアプリケーションの構築<br />ユー...
Zend_Acl<br />
ルールの設定<br />定義<br />セット(リソース・ロール・権限)に対するルールを設定する<br />nullは常に一致する<br />同じセットへのルールは上書きされる。<br />同じセットに対して、原則許可、assert付きで拒否と...
リソースとロールの継承<br />継承はクラス継承ではなく、Aclへの登録時に親を指定する。<br />権限チェックは親を辿ってルールを探索<br />リソース継承<br />リソースをツリー状に構成<br />ロール継承<br />権限の集約...
疑似ロールの例(CURRENT_USER)<br />単一ユーザーアクセス規約<br />現在アクセスしているユーザーの権限でだけ動作すると仮定できる場合<br />ログインしたユーザーが持つ複数のロールを、疑似ロール CURRENT_USER...
疑似ロール(CURRENT_USER)<br />構成<br />@singleton Current_User<br />$user = Current_User::getInstance();<br />$roles = $user->ge...
Assertion<br />$acl->allow($role, $res, $priv, $assertion);<br />
Assertionの仕様<br />http://framework.zend.com/manual/ja/zend.acl.advanced.html#zend.acl.advanced.assertions<br />Zend_Acl_As...
Assertionの使いどころ<br />banリスト<br />IPアドレス等でアクセス拒否<br />個別ユーザーの任意アクセス制御<br />友達まで公開<br />一時的特権<br />1日店長とか<br />ASPで試用期間、サポート...
ルール+Assertion<br />$acl->allow($role, $res, $priv, $assert);<br />$assertがtrueならallow、 falseならnull<br />$acl->deny($role, ...
ルール評価順チートシート<br />指定リソース<br />指定ロール<br />指定権限<br />全権(null)<br />親ロール<br />指定権限<br />全権(null)<br />親リソース<br />指定ロール<br />指...
ルール作成と運用のコツ<br />複雑化するときはスコープを分けてAclオブジェクト自体を切り替える<br />アクセスブロック、コントローラー、サービス<br />リソースと権限の表を作り、分割と集約で整理する<br />権限が多すぎる場合は...
リソース、権限の整理<br />
サンプルコード<br />
規約重視の制御例<br />NewsControllerextends Zend_Controller_Action<br />News_Aclextends Zend_Acl <br />Newsextends Zend_Db_Table_A...
News_Acl<br /><?php<br />class News_Acl extends Zend_Acl<br />{<br />    protected $_name = 'news';<br />    public functi...
NewsController<br /><?php<br />class NewsController extends Zend_Controller_Action<br />{<br />    // resource name<br /> ...
NewsController::preDispatch()<br />$action = $this->getRequest()->getActionName();<br />$auth = Zend_Auth::getInstance();<...
コントローラーでのアクセス制御<br />介入ポイント<br />init<br />preDispatch<br />アクションヘルパー<br />プラグイン<br />メリット<br />MVCで動作するかぎり必ず処理される<br />代替...
Final Answer?<br />"Keep it simple, stupid“<br />システムに複雑さを持ち込まない。<br />アクセス制御は可能な限りシンプルに<br />必要最小限の複雑さで実装する<br />規約重視パターン...
Zend_Aclの実際<br />オープンソースで見る<br />
Live Commerce<br />http://www.live-commerce.com/<br />LoginController<br />Aclを定義してZend_Aclインスタンスを返す<br />セッションにAclごと保存<br...
Omeka<br />http://omeka.org/<br />AclはZend_Application用のAclリソースを作成して使う<br />aclを定義したリソースファイルから読み込むOmeka_Aclを準備してリソースとして返す<...
TomatoCMS<br />http://www.tomatocms.com/<br />SingletonServices_Acl<br />データベースからルールセットを取得してAclをビルドしている<br />isUserOrRoleA...
Aclデータの保存<br />データの使用法にはさまざまなものが考えられるので、 ACL データの保存は、場面に応じて開発者側で考えることになります。 Zend_Acl はシリアライズ可能なので、ACL オブジェクトを PHP の serial...
Aclデータのロード・保存方法比較<br />Live Commerce<br />コントローラーに設定<br />セッションに保存<br />Omeka<br />Zend_Applicationリソースでacl.phpを読む<br />To...
いずれも<br />コントローラーでのアクセス制御<br />
コントローラー制御の困り所<br />プラグイン開発<br />サービスBの機能を利用するプラグインαは、コントローラーYと同様のアクセス制御を自主的にセット<br />サービスBへのアクセス制御レベルが変更になった<br />変更を必要とする...
複雑なシステムでの制御<br />コントローラーでのアクセス制御は画面に対するアクセス制御<br />回避ルートを作らない<br />境界とポリシーを明確にする<br />サービスはコントローラーを無条件で信用してはいけない。<br />モデル...
安全な仕組みを提供する<br />コントローラーの仕事<br />クライアント認証を保証する<br />画面レベルの制御ポリシーを作る<br />モデルへ問い合わせる<br />サービスレイヤー<br />承認を経ていないアクセスを拒否する<b...
Zend_Acl in Service Layer<br />
アクセス制御レイヤーについて調べてみた<br />http://www.infoq.com/articles/ddd-in-practice<br />
サービスレイヤーでアクセス制御<br />Service Layer(PofEAA)<br />http://www.martinfowler.com/eaaCatalog/serviceLayer.html<br />http://www.s...
Domain Layer<br />Data Source Layer<br />ServiceLayer<br />Presentation Layer<br />ドメインロジックと<br />アプリケーションロジックの境界で、アプリケーショ...
サービスレイヤーで実装例<br />サービスはリソース<br />渡されたAclに自分のAclを追加定義する<br />渡されたRoleを元に、権限をチェックして許可がなければ実行しない<br />
サンプルコード<br />
Model_User<br />implements Zend_Acl_Role_Interface<br /> <br />    public function getRoleId()<br />    {<br />        if ...
Service_Posts<br />implements Zend_Acl_Resource_Interface<br /> public function getResourceId()<br />    {<br />        re...
Aclの受け入れと設定<br /> public function setAcl(Zend_Acl $acl)<br />    {<br />        $acl->add($this)<br />            // ...<b...
Roleの受け入れ(subject設定)<br /> public function setRole(        Zend_Role_Interface $role    ) {<br />        $this->_role = $r...
メソッドに制御を追加<br /> public function getList()<br />    {<br />        if (!$this->getAcl()->isAllowed(<br />                $...
クライアントコード<br />$service->setAcl($acl);<br />$service->setRole($role);<br />try {<br />  $list = $service->getList();<br />...
これでサービスレイヤーでのアクセス制御が可能になりました<br />
サンプルソースの課題<br />サービスがAclを操作している<br />制御実装がサービスクラスに依存<br />Aclに対する操作は実際は単純ではない<br />ロールやリソースの継承、2重登録チェック<br />メソッドにアクセス制御コー...
そこで、セキュリティといえばIPA<br />ということで、調べてみました<br />http://www.ipa.go.jp/security/awareness/administrator/security-model.pdf<br />
リファレンスモニターを使う<br />Subject<br />参考<br />Acl<br />http://www.ipa.go.jp/security/awareness/administrator/security-model.pdf<...
リファレンスモニター<br />アクセスをする側(サブジェクト)から、アクセスされる対象(オブジェクト)へどんなアクセス(リファレンス)ができるのかを明確化する。<br />「リファレンス・モニター」はすべてのアクセス(リファレンス)を監視・仲...
目標<br />Acl操作はリファレンスモニターの責務<br />制御に対応するサービス<br />getRulesでルールを提供<br />許可されていないメソッドを禁止する<br />制御に対応するサブジェクト<br />getRoleId...
リファクタリング手順<br />クラスの責務と関係を明確にする<br />Visitorパターン<br />処理を仲介する<br />Mediatorパターン<br />構成を管理する<br />コンテナ<br />
「金づちを持つとすべて釘に見える」<br />http://www.doyouphp.jp/phpdp/phpdp_01-2-2_demerit_of_design_pattern.shtml<br />
サンプルの構成<br /><ul><li>サービスがAclを操作する。
サービスがAclに対する操作を知っている必要がある</li></li></ul><li>Visitorパターン<br />クラスの責務と関係を明確にする<br />Monitor<br />Aclを管理する<br />アクセスを監視する<br ...
interface<br />interface Acceptor<br />{<br />public function accept(visitor $visitor);<br />}<br />interface Visitor <br ...
interface Object <br />	extends Acceptor, Zend_Acl_Resource_Interface {}<br />interface Subject <br />	extends Acceptor, V...
Object::accept($monitor)<br />・子オブジェクトにも伝達<br />Monitor::visit(Acceptor $acceptor)<br />・オブジェクトのAclリストを取得<br />・サブジェクトのロール...
    public function accept(Visitor $visitor)<br />{<br />        switch (true) {<br />            case $visitor instanceof...
public function visit(Acceptor $acceptor)<br />{<br />        switch (true) {<br />            case $acceptor instanceof O...
protected function _visitObject(Object $object)<br />{<br />        $resourceID = $object->getResourceId();<br />        i...
Mediatorを通す<br />手続きをMediatorに任せる<br />クライアントからはサービスがMediatorに見える<br />public function __construct(.......<br />$object->a...
Mediator<br /> public function __construct(Monitor $monitor<br />                              , Subject $subject<br />   ...
Mediator<br /> public function __call($method, $args)<br />{<br />        $privilege = <br />		$this->_object->getPrivileg...
ContainerからMediatorを取得<br />・Zend_ApplicationのBootstrap用リソースで、サービス用コンテナを作成しMediatorを取得します。<br />・アクセス制御用コードを分離<br />・Aclルー...
DIコンテナを使う<br />Zend_Applicationのリソースだと、メソッドからのビルドを設定に書き出せません。<br />yadif等をZend_Applicationのリソースで使ってみるとよいと思います。<br />手前みそです...
コンテナでのサンプル実装(1)<br />$acl = $this->getApplication()->acl;<br />$accessControl = new Foo_Service_AccessControl($acl);<br />...
コンテナでのサンプル実装(2)<br />$acl = $this->getApplication()->acl;<br />$monitor = new Monitor($acl);<br />$mediator = <br />	new M...
Controller<br />$container = $this->getInvokeArg(‘bootstrap’);<br />$service = $container->services->bugService;<br />try ...
許可されたメソッドだけを実行<br />     public function __call($methodName, $args)<br />{<br />        if (! $this->_isAllowed($methodNam...
メソッドと権限をマップして許可<br />protected function _isAllowed($methodName)<br />{<br />        $privilege = $this->getPrivilege($meth...
基礎設計の完成<br />コントローラーはサービスを利用する<br />制御情報はサービスのプロパティ<br />制御操作・約束事は集中管理<br />回避できないアクセス制御<br />
サービスレイヤーでのアクセス制御をお勧めするもうひとつの理由はZF 2.0<br />バージョンアップ対応<br />アプリ層でのアクセス制御では、アプリ層のV.upで再実装になるリスクが高い。サービスレイヤーなら、そのまま移植できる<br /...
まとめ<br />モデルもAclもコアな部分は<br />実装対象にベストマッチする<br />アーキテクチャを構成しよう<br />
何か作りませんか?<br />共同開発者募集中<br />
ご清聴ありがとうございました<br />
参考書籍、サイトなど<br />http://framework.zend.com/manual<br />Patterns of Enterprise Application Architecture エンタープライズアーキテクチャパターン:...
Upcoming SlideShare
Loading in...5
×

Zend_Acl in ServiceLayer

4,321

Published on

phpstudy_91 Zend Framework勉強会#2 noopable's presentation

Published in: Technology
0 Comments
5 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
4,321
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
25
Comments
0
Likes
5
Embeds 0
No embeds

No notes for slide

Zend_Acl in ServiceLayer

  1. 1. アクセス制御<br />Zend_Aclin ServiceLayer<br />
  2. 2. CWE(Common Weakness Enumeration)~脆弱性の種類を識別するための共通の脆弱性タイプの一覧~<br />http://cwe.mitre.org/top25/<br />http://www.ipa.go.jp/security/vuln/CWE.html<br />
  3. 3. 脆弱性タイプの一覧<br />1位:XSS<br />2位:SQLインジェクション<br />3位:バッファオーバーフロー<br />4位:CSRF<br />5位:不正確なアクセス制御<br />制御方法がさまざまでノウハウが蓄積されにくい?<br />http://cwe.mitre.org/top25/<br />http://cwe.mitre.org/top25/#CWE-285<br />http://www.ipa.go.jp/security/vuln/CWE.html<br />
  4. 4. 手動アクセス制御の失敗<br />アクセス制御を個別メソッドに書く限りうっかりミスは防げない。<br />動作とアクセス制御は分離するべき<br />テストで防げる?<br />ついうっかりが発生するシチュエーションではテスト仕様自体に漏れがある<br />制御コードを回避?<br />ある画面へのアクセス許可があっても、その画面から対象外のリソースへアクセスするコードが混入すると、回避リスクが発生する<br />課題<br />
  5. 5. サイバー・ノーガード戦法<br />対策は<br />例えば、PHPを避ける<br />サービスレイヤーでアクセス制御<br />
  6. 6. アクセス制御<br />アクセス制御は脆弱性ランキング5位<br />コントローラーでアクセス制御<br />ZF+ OSSで見るアクセス制御の実例<br />サービス層でリファレンスモニタを使う<br />テーマ<br />
  7. 7. アクセス制御用コンポーネント<br />Zend_Acl(承認)<br />ロールベースアクセス制御(RBAC)<br />Zend_Auth(認証)<br />認証アダプタを利用した認証<br />DBアダプタを利用するとユーザーの関連情報も取得できる。<br />認証の永続化<br />デフォルトではセッションを利用するが、memcached等の分散ストレージで構成することも可能。ファーム構成でも使える<br />
  8. 8. クィックスタート<br />http://framework.zend.com/manual/ja/learning.multiuser.html<br />Zend Frameworkによるマルチユーザーアプリケーションの構築<br />ユーザーセッション管理<br />ユーザー認証<br />承認システムの構築<br />
  9. 9. Zend_Acl<br />
  10. 10. ルールの設定<br />定義<br />セット(リソース・ロール・権限)に対するルールを設定する<br />nullは常に一致する<br />同じセットへのルールは上書きされる。<br />同じセットに対して、原則許可、assert付きで拒否というようなルールは作成できない<br />
  11. 11. リソースとロールの継承<br />継承はクラス継承ではなく、Aclへの登録時に親を指定する。<br />権限チェックは親を辿ってルールを探索<br />リソース継承<br />リソースをツリー状に構成<br />ロール継承<br />権限の集約<br />許可がわかりやすくなる<br />× if super-admin or manager<br />○ if manager<br />複数ロールの所有をまとめる<br />Zend_Acl 2.0では、複数ロールに対応するかもしれない<br />
  12. 12. 疑似ロールの例(CURRENT_USER)<br />単一ユーザーアクセス規約<br />現在アクセスしているユーザーの権限でだけ動作すると仮定できる場合<br />ログインしたユーザーが持つ複数のロールを、疑似ロール CURRENT_USERでまとめる<br />特定の許可は与えず、ユーザーが保持しているロールを継承するのみとする。<br />権限チェックは疑似ロールに対して行う<br />
  13. 13. 疑似ロール(CURRENT_USER)<br />構成<br />@singleton Current_User<br />$user = Current_User::getInstance();<br />$roles = $user->getRoles();<br />$acl->addRole(CURRENT_USER,$roles);<br />チェック<br />$acl->isAllowed(CURRENT_USER, $resource, $priv);<br />
  14. 14. Assertion<br />$acl->allow($role, $res, $priv, $assertion);<br />
  15. 15. Assertionの仕様<br />http://framework.zend.com/manual/ja/zend.acl.advanced.html#zend.acl.advanced.assertions<br />Zend_Acl_Assert_Interface<br />// @return boolean <br />functionassert(Zend_Acl $acl,<br />                           Zend_Acl_Role_Interface $role = null,<br />                           Zend_Acl_Resource_Interface $resource = null,<br />                           $privilege = null)<br />リソース自身がassetするassertionという実装も<br />
  16. 16. Assertionの使いどころ<br />banリスト<br />IPアドレス等でアクセス拒否<br />個別ユーザーの任意アクセス制御<br />友達まで公開<br />一時的特権<br />1日店長とか<br />ASPで試用期間、サポートチケットとか<br />※Zend_Cacheを使うと簡単<br />
  17. 17. ルール+Assertion<br />$acl->allow($role, $res, $priv, $assert);<br />$assertがtrueならallow、 falseならnull<br />$acl->deny($role, $res, $priv, $assert);<br />$assertがtrueならdeny、 falseならnull<br />$acl->allow(null, null, null, $assert);<br />assertがtrueならallow、falseならdeny<br />Assertに失敗した場合は次のルールを評価する。全null許可は最後に確認され、反転した許可タイプが返される<br />
  18. 18. ルール評価順チートシート<br />指定リソース<br />指定ロール<br />指定権限<br />全権(null)<br />親ロール<br />指定権限<br />全権(null)<br />親リソース<br />指定ロール<br />指定権限<br />全権(null)<br />親ロール<br />指定権限<br />全権(null)<br />nullリソース<br />以下同じ<br />$acl->isAllowed($resource, $role, $privilege)<br />指定したリソース、ロール、権限に一致するルールを順に検索する。<br />一致したルール以後は評価されない。<br />(assertionは一致条件の追加)<br />nullを指定した検索では、nullルールにマッチする<br />注)間違いがあったら、ご指摘ください。<br />
  19. 19. ルール作成と運用のコツ<br />複雑化するときはスコープを分けてAclオブジェクト自体を切り替える<br />アクセスブロック、コントローラー、サービス<br />リソースと権限の表を作り、分割と集約で整理する<br />権限が多すぎる場合は、リソースを分割<br />リソースが多すぎる場合には、リソースの継承で集約リソースを作成<br />ロールを組み立てる<br />リソース毎の権限を整理して、機能ロールを作成<br />機能ロールを集め、集約ロールを作る<br />
  20. 20. リソース、権限の整理<br />
  21. 21. サンプルコード<br />
  22. 22. 規約重視の制御例<br />NewsControllerextends Zend_Controller_Action<br />News_Aclextends Zend_Acl <br />Newsextends Zend_Db_Table_Abstract<br />参考) <br />http://weierophinney.net/matthew/archives/201-Applying-ACLs-to-Models.html<br />http://www.oplabo.jp/article/44<br />
  23. 23. News_Acl<br /><?php<br />class News_Acl extends Zend_Acl<br />{<br /> protected $_name = 'news';<br /> public function __construct()<br />{<br /> //sample<br /> $this->addRole(new Zend_Acl_Role('marketing'), 'staff')<br /> ->addResource(new Zend_Acl_Resource($this->_name))<br /> ->allow('marketing', $this->_name, array('publish', 'archive'));<br />}<br />}<br />
  24. 24. NewsController<br /><?php<br />class NewsController extends Zend_Controller_Action<br />{<br /> // resource name<br /> protected $_name = 'news';<br /> public function init()<br />{<br /> $this->_acl = new News_Acl;<br />}<br />}<br />
  25. 25. NewsController::preDispatch()<br />$action = $this->getRequest()->getActionName();<br />$auth = Zend_Auth::getInstance();<br />$role = $this->getRole($auth->getIdentity());<br />if (! $this->_acl->isAllowed($role, $this->_name, $action)) {<br /> throw new Exception('action not permitted');<br /> //ここでエラーアクションに書き換えてもよい。<br />}<br />parent::preDispatch();<br />
  26. 26. コントローラーでのアクセス制御<br />介入ポイント<br />init<br />preDispatch<br />アクションヘルパー<br />プラグイン<br />メリット<br />MVCで動作するかぎり必ず処理される<br />代替画面などクライアント向けの親切対応ができる<br />
  27. 27. Final Answer?<br />"Keep it simple, stupid“<br />システムに複雑さを持ち込まない。<br />アクセス制御は可能な限りシンプルに<br />必要最小限の複雑さで実装する<br />規約重視パターンは単純な実装形態の一つ<br />単純な実装で複雑な仕様を実現しようとすれば破綻する<br />仕様と実装のバランスで最適な表現方法を見つけることが重要<br />
  28. 28. Zend_Aclの実際<br />オープンソースで見る<br />
  29. 29. Live Commerce<br />http://www.live-commerce.com/<br />LoginController<br />Aclを定義してZend_Aclインスタンスを返す<br />セッションにAclごと保存<br />アクションヘルパーLoginValidator<br />ログインチェック<br />セッションに保存されたAclでコントローラーに対する権限チェック<br />管理画面用モジュール<br />コントローラー::initで、コントローラーへのアクセス権があるかどうかを必ずチェックしてリダイレクト<br />c::isAllowed<br />グローバル参照可能なチェックメソッド<br />ビューでメニューの表示を調整<br />
  30. 30. Omeka<br />http://omeka.org/<br />AclはZend_Application用のAclリソースを作成して使う<br />aclを定義したリソースファイルから読み込むOmeka_Aclを準備してリソースとして返す<br />omeka/application/core/acl.php<br />アクションヘルパーacl<br />preDispatchで基本的な権限チェック<br />モジュール+コントローラー = リソース<br />基底コントローラーにisAllowedメソッドを持ち、重要なアクションの前にチェック<br />リソースに権限セットを割り当て<br />
  31. 31. TomatoCMS<br />http://www.tomatocms.com/<br />SingletonServices_Acl<br />データベースからルールセットを取得してAclをビルドしている<br />isUserOrRoleAllowed<br />Authプラグイン<br />preDispatchでモジュール・コントローラーへのアクセス権をチェック<br />Services_RuleChecker<br />アクセス許可があればコールバックの結果を返すプロキシ<br />主にviewヘルパーで使う<br />Block IP assertion<br />BackEnd Access white list<br />
  32. 32. Aclデータの保存<br />データの使用法にはさまざまなものが考えられるので、 ACL データの保存は、場面に応じて開発者側で考えることになります。 Zend_Acl はシリアライズ可能なので、ACL オブジェクトを PHP の serialize() 関数でシリアライズすることができます。シリアライズした結果を、 ファイルやデータベースあるいはキャッシュなどのお好みの場所に保存することができます。<br />マニュアルより<br />
  33. 33. Aclデータのロード・保存方法比較<br />Live Commerce<br />コントローラーに設定<br />セッションに保存<br />Omeka<br />Zend_Applicationリソースでacl.phpを読む<br />TomatoCMS<br />データベースから読み込み<br />任意アクセス制御にはDB読込<br />
  34. 34. いずれも<br />コントローラーでのアクセス制御<br />
  35. 35. コントローラー制御の困り所<br />プラグイン開発<br />サービスBの機能を利用するプラグインαは、コントローラーYと同様のアクセス制御を自主的にセット<br />サービスBへのアクセス制御レベルが変更になった<br />変更を必要とするのはどのライブラリ??<br />WebサービスサーバーやCLIの実装<br />サーバーライブラリやCLIからサービスを使いたいというシナリオは増加傾向<br />
  36. 36. 複雑なシステムでの制御<br />コントローラーでのアクセス制御は画面に対するアクセス制御<br />回避ルートを作らない<br />境界とポリシーを明確にする<br />サービスはコントローラーを無条件で信用してはいけない。<br />モデルでの制御が不要になるのは、サービスとコントローラーの関係が固定されているときのみ<br />
  37. 37. 安全な仕組みを提供する<br />コントローラーの仕事<br />クライアント認証を保証する<br />画面レベルの制御ポリシーを作る<br />モデルへ問い合わせる<br />サービスレイヤー<br />承認を経ていないアクセスを拒否する<br />クライアントからの問い合わせに答える?<br />Mediatorを入れている場合はMediatorで<br />
  38. 38. Zend_Acl in Service Layer<br />
  39. 39. アクセス制御レイヤーについて調べてみた<br />http://www.infoq.com/articles/ddd-in-practice<br />
  40. 40. サービスレイヤーでアクセス制御<br />Service Layer(PofEAA)<br />http://www.martinfowler.com/eaaCatalog/serviceLayer.html<br />http://www.slideshare.net/weierophinney/architecting-ajax-applications-with-zend-framework<br />P28~32<br />参考<br />
  41. 41. Domain Layer<br />Data Source Layer<br />ServiceLayer<br />Presentation Layer<br />ドメインロジックと<br />アプリケーションロジックの境界で、アプリケーション層からの要求を監査する<br />http://www.martinfowler.com/eaaCatalog/serviceLayer.html<br />
  42. 42. サービスレイヤーで実装例<br />サービスはリソース<br />渡されたAclに自分のAclを追加定義する<br />渡されたRoleを元に、権限をチェックして許可がなければ実行しない<br />
  43. 43. サンプルコード<br />
  44. 44. Model_User<br />implements Zend_Acl_Role_Interface<br /> <br />    public function getRoleId()<br />    {<br />        if ($this->_aclRoleId == null) {<br />            return 'guest';<br />        }<br /> <br />        return $this->_aclRoleId;<br />    }<br />
  45. 45. Service_Posts<br />implements Zend_Acl_Resource_Interface<br /> public function getResourceId()<br /> {<br /> return ‘service_posts';<br /> }<br />
  46. 46. Aclの受け入れと設定<br /> public function setAcl(Zend_Acl $acl)<br /> {<br /> $acl->add($this)<br /> // ...<br /> ->allow('admin', $this, array(<br /> 'register', 'update', 'list', 'delete'));<br /> $this->_acl = $acl;<br /> return $this;<br /> }<br />
  47. 47. Roleの受け入れ(subject設定)<br /> public function setRole( Zend_Role_Interface $role ) {<br /> $this->_role = $role;<br /> return $this;<br /> }<br />
  48. 48. メソッドに制御を追加<br /> public function getList()<br /> {<br /> if (!$this->getAcl()->isAllowed(<br /> $this->getRole(), $this, 'list')<br /> ) {<br /> throw new UnauthorizedException();<br /> }<br /> // ...<br /> }<br />
  49. 49. クライアントコード<br />$service->setAcl($acl);<br />$service->setRole($role);<br />try {<br /> $list = $service->getList();<br />} catch (UnauthorizedException $e) {<br /> $this->_redirect(‘unauth’);<br />}<br />
  50. 50. これでサービスレイヤーでのアクセス制御が可能になりました<br />
  51. 51. サンプルソースの課題<br />サービスがAclを操作している<br />制御実装がサービスクラスに依存<br />Aclに対する操作は実際は単純ではない<br />ロールやリソースの継承、2重登録チェック<br />メソッドにアクセス制御コード必須<br />メソッドに要件以外の内容を追加すべきか<br />すべてのサービスにsetAcl SetRoleを実装すべきか。<br />SRP単一責任原則は?<br />
  52. 52. そこで、セキュリティといえばIPA<br />ということで、調べてみました<br />http://www.ipa.go.jp/security/awareness/administrator/security-model.pdf<br />
  53. 53. リファレンスモニターを使う<br />Subject<br />参考<br />Acl<br />http://www.ipa.go.jp/security/awareness/administrator/security-model.pdf<br />Object<br />リファレンス<br />モニタ<br />
  54. 54. リファレンスモニター<br />アクセスをする側(サブジェクト)から、アクセスされる対象(オブジェクト)へどんなアクセス(リファレンス)ができるのかを明確化する。<br />「リファレンス・モニター」はすべてのアクセス(リファレンス)を監視・仲介してアクセス制御ポリシーを適用する。<br />参考<br />
  55. 55. 目標<br />Acl操作はリファレンスモニターの責務<br />制御に対応するサービス<br />getRulesでルールを提供<br />許可されていないメソッドを禁止する<br />制御に対応するサブジェクト<br />getRoleIdでロールを提示<br />サービスコンテナ<br />オブジェクトの関係を定義して、使えるサービスを保管する<br />
  56. 56. リファクタリング手順<br />クラスの責務と関係を明確にする<br />Visitorパターン<br />処理を仲介する<br />Mediatorパターン<br />構成を管理する<br />コンテナ<br />
  57. 57. 「金づちを持つとすべて釘に見える」<br />http://www.doyouphp.jp/phpdp/phpdp_01-2-2_demerit_of_design_pattern.shtml<br />
  58. 58. サンプルの構成<br /><ul><li>サービスがAclを操作する。
  59. 59. サービスがAclに対する操作を知っている必要がある</li></li></ul><li>Visitorパターン<br />クラスの責務と関係を明確にする<br />Monitor<br />Aclを管理する<br />アクセスを監視する<br />Object(サービス)<br />Monitorを受け入れる<br />アクセスルールを提供する<br />Subject<br />自分が何者かを表明する<br />
  60. 60. interface<br />interface Acceptor<br />{<br />public function accept(visitor $visitor);<br />}<br />interface Visitor <br />{<br /> public function visit(Acceptor $acceptor);<br />}<br />
  61. 61. interface Object <br /> extends Acceptor, Zend_Acl_Resource_Interface {}<br />interface Subject <br /> extends Acceptor, Visitor, Zend_Acl_Role_Interface {}<br />interface Monitor <br /> extends Visitor {}<br />
  62. 62. Object::accept($monitor)<br />・子オブジェクトにも伝達<br />Monitor::visit(Acceptor $acceptor)<br />・オブジェクトのAclリストを取得<br />・サブジェクトのロールを取得<br />Subject::visit(Acceptor $object)<br />・オブジェクトに対する処理<br />Visitorパターン<br />オブジェクト間処理の定型化<br />SubjectからObjectへの要求<br />リファレンスモニターの受け入れ<br />受け入れの再帰処理<br />メリット<br />定型処理を集中管理できる<br />クライアントが定型処理を知る必要がない<br />phpにはオーバーロードがないけど?<br />型チェックでswitchしたら意味がない?<br />
  63. 63. public function accept(Visitor $visitor)<br />{<br /> switch (true) {<br /> case $visitor instanceof Subject:<br /> $this->_subject = $subject;<br /> break;<br /> case $visitor instanceof Monitor:<br /> $this->_monitor = $visitor;<br /> break;<br /> default:<br /> return $this;<br />}<br /> $visitor->visit($this);<br /> return $this;<br />}<br />
  64. 64. public function visit(Acceptor $acceptor)<br />{<br /> switch (true) {<br /> case $acceptor instanceof Object:<br /> $this->_visitObject($acceptor);<br /> break;<br /> case $acceptor instanceof Subject:<br /> $this->_visitSubject($acceptor);<br /> break;<br />}<br />}<br />
  65. 65. protected function _visitObject(Object $object)<br />{<br /> $resourceID = $object->getResourceId();<br /> if (! $this->_acl->has($object)) {<br /> $this->_acl->addResource($object);<br />}<br /> if ($object instanceofRuleAggregate) {<br /> $rules = $object->getRules();<br />foreach ($rules as $rule) {<br /> $type = isset($rule['type']) ? $rule['type'] : Zend_Acl::TYPE_ALLOW;<br /> $role = isset($rule['role']) ? $rule['role'] : null;<br /> $resource = isset($rule['resource']) ? $rule['resource'] : $object;<br /> $privileges = isset($rule['privileges']) ? $rule['privileges'] : null;<br /> $assert = isset($rule['assert']) && $rule['assert'] instanceof Zend_Acl_Assert_Interface ? $rule['assert'] : null;<br /> $this->_acl->setRule(Zend_Acl::OP_ADD, $type, $resource, $acceptor, $privileges, $assert);<br />}<br />}<br />if ($object instanceof Zend_Acl_Assert_Interface) {<br /> $this->_acl->allow(null, $object, null, $object);<br />}<br /> return $this;<br />}<br />
  66. 66. Mediatorを通す<br />手続きをMediatorに任せる<br />クライアントからはサービスがMediatorに見える<br />public function __construct(.......<br />$object->accept($monitor);<br />$subject->accept($monitor);<br />$object->accept($subject);<br />
  67. 67. Mediator<br /> public function __construct(Monitor $monitor<br /> , Subject $subject<br /> , Object $object)<br />{<br /> $this->_monitor = $monitor;<br /> $this->_subject = $subject;<br /> $this->_object = $object;<br /> $this->_subject->accept($this->_monitor);<br /> $this->_object->accept($this->_monitor);<br /> $this->_object->accept($this->_subject);<br />}<br />
  68. 68. Mediator<br /> public function __call($method, $args)<br />{<br /> $privilege = <br /> $this->_object->getPrivilege($method);<br /> if (! $this->_monitor->isAllowed(<br /> $this->_subject->getRole()<br /> , $this->_object, $privilege)) {<br /> throw new Exception('method not allowed');<br />}<br /> }<br />
  69. 69. ContainerからMediatorを取得<br />・Zend_ApplicationのBootstrap用リソースで、サービス用コンテナを作成しMediatorを取得します。<br />・アクセス制御用コードを分離<br />・Aclルールはサービスのプロパティとする<br />
  70. 70. DIコンテナを使う<br />Zend_Applicationのリソースだと、メソッドからのビルドを設定に書き出せません。<br />yadif等をZend_Applicationのリソースで使ってみるとよいと思います。<br />手前みそですがブログに書きました<br />http://d.hatena.ne.jp/noopable/20100213/1266050466<br />
  71. 71. コンテナでのサンプル実装(1)<br />$acl = $this->getApplication()->acl;<br />$accessControl = new Foo_Service_AccessControl($acl);<br />$user = new Foo_Model_User;<br />$user->accept($accessControl);<br />$service = new Foo_Service;<br />$service->accept($accessControl);<br />
  72. 72. コンテナでのサンプル実装(2)<br />$acl = $this->getApplication()->acl;<br />$monitor = new Monitor($acl);<br />$mediator = <br /> new Mediator($monitor, $user, $service);<br />
  73. 73. Controller<br />$container = $this->getInvokeArg(‘bootstrap’);<br />$service = $container->services->bugService;<br />try {<br /> $service->execFoo();<br />} catch (PermissionDeniedException $e) {<br /> die($e->getMessage());<br />}<br />
  74. 74. 許可されたメソッドだけを実行<br /> public function __call($methodName, $args)<br />{<br /> if (! $this->_isAllowed($methodName)) {<br /> throw new Exception('method not allowed');<br />}<br /> if (method_exists($this, '_' . $methodName)) {<br /> return call_user_func_array(array($this, ‘_’ .    $methodName), $args);<br />}<br /> // etc. etc. etc.<br />}<br />
  75. 75. メソッドと権限をマップして許可<br />protected function _isAllowed($methodName)<br />{<br /> $privilege = $this->getPrivilege($methodName);<br /> if (!$this->_monitor || !$this->_subject) {<br /> throw new Exception('Acc is not activated.');<br />}<br /> return $this->_monitor<br /> ->isAllowed($this->_subject, $this, $privilege);<br />}<br />
  76. 76. 基礎設計の完成<br />コントローラーはサービスを利用する<br />制御情報はサービスのプロパティ<br />制御操作・約束事は集中管理<br />回避できないアクセス制御<br />
  77. 77. サービスレイヤーでのアクセス制御をお勧めするもうひとつの理由はZF 2.0<br />バージョンアップ対応<br />アプリ層でのアクセス制御では、アプリ層のV.upで再実装になるリスクが高い。サービスレイヤーなら、そのまま移植できる<br />リファレンスモニターを使う理由<br />setAcl形式だとZF1.xのZend_Aclを前提とせざるをえない<br />リファレンスモニターなら両バージョンに対応可能<br />
  78. 78. まとめ<br />モデルもAclもコアな部分は<br />実装対象にベストマッチする<br />アーキテクチャを構成しよう<br />
  79. 79. 何か作りませんか?<br />共同開発者募集中<br />
  80. 80. ご清聴ありがとうございました<br />
  81. 81. 参考書籍、サイトなど<br />http://framework.zend.com/manual<br />Patterns of Enterprise Application Architecture エンタープライズアーキテクチャパターン: マーチン・ハウラー著 長瀬嘉秀 監訳<br />Domain-Driven Design: Tackling Complexity in the Heart of Software :Eric Evans著<br />http://www.martinfowler.com/eaaCatalog/transactionScript.html<br /><ul><li>http://www.slideshare.net/bngsudheer/framework-shootout-zf</li></ul>http://cwe.mitre.org/top25/<br />http://www.ipa.go.jp/<br />http://www.2ch.net<br />http://weierophinney.net/matthew/archives/201-Applying-ACLs-to-Models.html<br />http://www.slideshare.net/weierophinney/architecting-ajax-applications-with-zend-framework<br />http://www.oplabo.jp/article/44<br />http://www.live-commerce.com/<br />http://omeka.org/<br />http://tomatocms.com/<br />
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×