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