Skip to content

Commit a9ab4fb

Browse files
committed
Merge pull request #325 from kseta/best-practices-security
[best practice] Security 翻訳
2 parents 4b00657 + 6b6684e commit a9ab4fb

File tree

1 file changed

+357
-0
lines changed

1 file changed

+357
-0
lines changed

best_practices/security.rst

Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
セキュリティ
2+
========
3+
4+
認証とファイアウォール (i.e ユーザの権限情報を取得する)
5+
------------------------------------------------------------------
6+
7+
任意の方法を使用してユーザー認証を行い、
8+
任意のソースからユーザー情報を読み込むように Symfony を設定することができます。
9+
10+
セキュリティはとても難解なテーマですが、`Security Cookbook Section`_ に多くの情報が記載されています。
11+
12+
認証は、その必要性の有無に関わらず、
13+
``security.yml`` の ``firewalls`` キーの下に設定されています。
14+
15+
.. best-practice::
16+
17+
正規に許可された2つの異なる認証システムとユーザが無い限り
18+
(e.g. メインとなるサイトと API のためだけのトークンシステムのためのログイン)、
19+
``anonymous`` のキーを有効にしたたったひとつのファイヤーウォールを設けることを推奨します。
20+
21+
ほとんどのアプリケーションで認証システムとユーザのセットはひとつしかありません。
22+
このため、ひとつのファイヤーウォールの設定だけで事足ります。
23+
もちろん、サイトの API と WEB を分けたい場合などの例外もありますが、シンプルに考えていくことが重要です。
24+
25+
なお、ファイヤーウォールの下では ``anonymous`` キーを利用するべきです。
26+
サイトの異なるセクション (または 全てのセクションのようなもの) にユーザが
27+
ログインすることが必要となった場合は ``access_control`` エリアを利用します。
28+
29+
.. best-practice::
30+
31+
ユーザのパスワードのエンコーディングには ``bcrypt`` エンコーダーを利用する。
32+
33+
ユーザがパスワードを持つ場合、SHA-512 の代わりに ``bcrypt`` エンコーダーを利用することを推奨します。
34+
``bcrypt`` の主要なアドバンテージは、**ソルト** の値が有しているものが
35+
レインボーテーブルアタックから保護しブルートフォースアタックを遅延できることです。
36+
37+
この考えで、データベースからユーザをロードするログインフォームを利用する認証を
38+
アプリケーションにセットアップする設定は以下となります。
39+
40+
.. code-block:: yaml
41+
42+
security:
43+
encoders:
44+
AppBundle\Entity\User: bcrypt
45+
46+
providers:
47+
database_users:
48+
entity: { class: AppBundle:User, property: username }
49+
50+
firewalls:
51+
secured_area:
52+
pattern: ^/
53+
anonymous: true
54+
form_login:
55+
check_path: security_login_check
56+
login_path: security_login_form
57+
58+
logout:
59+
path: security_logout
60+
target: homepage
61+
62+
# ... access_control exists, but is not shown here
63+
64+
.. tip::
65+
66+
プロジェクトのためのソースコードはそれぞれの部分を説明するコメントを含む。
67+
68+
承認 (i.e. アクセスを拒否すること)
69+
-----------------------------------
70+
71+
Symfony は承認のためにいくつかの方法を提供しています。
72+
`security.yml`_ に ``access_control`` の設定を含めること、
73+
``security.context`` サービスに直接 `@Security annotation <best-practices-security-annotation>` と
74+
`isGranted <best-practices-directy-isGranted>` を使うという方法です。
75+
76+
.. best-practice::
77+
78+
* 広範囲の URL パターンをプロテクトするためには ``access_control`` を利用する。
79+
* いつでも可能なときは ``@Security`` アノテーションを利用する。
80+
* より複雑な状況の場合はいつでも、
81+
``security.context`` サービスでセキュリティを直接的にチェックする。
82+
83+
承認ロジックを一箇所に集中させるには
84+
カスタムセキュリティ Voter や ACL を利用するというような方法があります。
85+
86+
.. best-practice::
87+
88+
* きめ細かい制限のために、カスタムセキュリティ Voter を定義する。
89+
* 管理機能を経由したあらゆるユーザによる
90+
あらゆるのオブジェクトへのアクセスを制限するために Symfony ACL を利用する。
91+
92+
.. _best-practices-security-annotation:
93+
94+
@Security アノテーション
95+
------------------------
96+
97+
可能な限り、コントローラーごとにアクセスを制御するためには ``@Secury`` アノテーションを利用します。
98+
この記述は、可読性が高く、整合性を保ったままでそれぞれのアクションに設置することができます。
99+
100+
このアプリケーションでは、新しいポストを作成するための ``ROLE_ADMIN`` が必要です。
101+
``@Security`` を利用するとこのようになります。
102+
103+
.. code-block:: php
104+
105+
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
106+
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
107+
// ...
108+
109+
/**
110+
* Displays a form to create a new Post entity.
111+
*
112+
* @Route("/new", name="admin_post_new")
113+
* @Security("has_role('ROLE_ADMIN')")
114+
*/
115+
public function newAction()
116+
{
117+
// ...
118+
}
119+
120+
Expression による複雑なセキュリティの制限
121+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
122+
123+
セキュリティロジックがかなり複雑な場合は、
124+
``@Security`` の内部で `expression`_ を利用できます。
125+
例えば、
126+
``Post`` オブジェクトの ``getAuthorEmail`` メソッドの返り値とメールアドレスが一致したときのみ
127+
コントローラーへのアクセスを許可したい場合は以下のように実装できます。
128+
129+
.. code-block:: php
130+
131+
use AppBundle\Entity\Post;
132+
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
133+
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
134+
135+
/**
136+
* @Route("/{id}/edit", name="admin_post_edit")
137+
* @Security("user.getEmail() == post.getAuthorEmail()")
138+
*/
139+
public function editAction(Post $post)
140+
{
141+
// ...
142+
}
143+
144+
``Post`` オブジェクトとそれに与えられる ``$post`` という引数を自動的に取得する
145+
ために `PramConverter`_ の利用が必須であることに注意してください。
146+
147+
この方法にはよく知られた欠点があります。
148+
アノテーションによる記述は、アプリケーションの他の部分で簡単に再利用できません。
149+
投稿の著者のみが閲覧できるテンプレートのリンクを追加したい場合を想像してください。
150+
Twig の文法を利用して再び記述する必要があることがすぐに思い浮かぶでしょう。
151+
152+
.. code-block:: html+jinja
153+
154+
{% if app.user and app.user.email == post.authorEmail %}
155+
<a href=""> ... </a>
156+
{% endif %}
157+
158+
十分にシンプルなロジックの場合、最も簡単な解決手段は
159+
与えられたユーザがその投稿の著者であるかをチェックする新しい関数を ``Post`` エンティティに追加することです。
160+
161+
.. code-block:: php
162+
163+
// src/AppBundle/Entity/Post.php
164+
// ...
165+
166+
class Post
167+
{
168+
// ...
169+
170+
/**
171+
* Is the given User the author of this Post?
172+
*
173+
* @return bool
174+
*/
175+
public function isAuthor(User $user = null)
176+
{
177+
return $user && $user->getEmail() == $this->getAuthorEmail();
178+
}
179+
}
180+
181+
これで、この関数をテンプレートとセキュリティの記述のどちらでも再利用できます。
182+
183+
.. code-block:: php
184+
185+
use AppBundle\Entity\Post;
186+
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
187+
188+
/**
189+
* @Route("/{id}/edit", name="admin_post_edit")
190+
* @Security("post.isAuthor(user)")
191+
*/
192+
public function editAction(Post $post)
193+
{
194+
// ...
195+
}
196+
197+
.. code-block:: html+jinja
198+
199+
{% if post.isAuthor(app.user) %}
200+
<a href=""> ... </a>
201+
{% endif %}
202+
203+
.. _best-practices-directy-isGranted:
204+
205+
@Security を利用しない権限のチェック
206+
--------------------------------------
207+
208+
これまでの例は、
209+
``post`` という変数にアクセスできる記述を提供してくれる :ref:`ParamConverter <best-practices-paramconverter>` を
210+
利用する場合のみ動作します。
211+
これを利用しない場合やより応用的なユースケースの場合は、PHP で同様のセキュリティチェックができます。
212+
213+
.. code-block:: php
214+
215+
/**
216+
* @Route("/{id}/edit", name="admin_post_edit")
217+
*/
218+
public function editAction($id)
219+
{
220+
$post = $this->getDoctrine()->getRepository('AppBundle:Post')
221+
->find($id);
222+
223+
if (!$post) {
224+
throw $this->createNotFoundException();
225+
}
226+
227+
if (!$post->isAuthor($this->getUser())) {
228+
throw $this->createAccessDeniedException();
229+
}
230+
231+
// ...
232+
}
233+
234+
セキュリティ Voter
235+
---------------
236+
237+
セキュリティロジックが複雑で ``isAuthor()`` のようなメソッドに局所化できない場合、
238+
カスタム Voter を利用することができます。
239+
これらは `ACL's`_ よりもかなり簡単な方法かつほぼ全てのケースに柔軟に対応できます。
240+
241+
まずはじめに、Voter クラスを作成します。以下の例では、これまでと同じ ``getAuthorEmail`` ロジックを
242+
実装した Voter について示します。
243+
244+
.. code-block:: php
245+
246+
namespace AppBundle\Security;
247+
248+
use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter;
249+
use Symfony\Component\Security\Core\User\UserInterface;
250+
251+
// AbstractVoter class requires Symfony 2.6 or higher version
252+
class PostVoter extends AbstractVoter
253+
{
254+
const CREATE = 'create';
255+
const EDIT = 'edit';
256+
257+
protected function getSupportedAttributes()
258+
{
259+
return array(self::CREATE, self::EDIT);
260+
}
261+
262+
protected function getSupportedClasses()
263+
{
264+
return array('AppBundle\Entity\Post');
265+
}
266+
267+
protected function isGranted($attribute, $post, $user = null)
268+
{
269+
if (!$user instanceof UserInterface) {
270+
return false;
271+
}
272+
273+
if ($attribute === self::CREATE && in_array('ROLE_ADMIN', $user->getRoles(), true)) {
274+
return true;
275+
}
276+
277+
if ($attribute === self::EDIT && $user->getEmail() === $post->getAuthorEmail()) {
278+
return true;
279+
}
280+
281+
return false;
282+
}
283+
}
284+
285+
アプリケーションでセキュリティ Voter を有効にするために新しいサービスを定義します。
286+
287+
.. code-block:: yaml
288+
289+
# app/config/services.yml
290+
services:
291+
# ...
292+
post_voter:
293+
class: AppBundle\Security\PostVoter
294+
public: false
295+
tags:
296+
- { name: security.voter }
297+
298+
ここで ``@Security`` アノテーションに Voter を利用してみます。
299+
300+
.. code-block:: php
301+
302+
/**
303+
* @Route("/{id}/edit", name="admin_post_edit")
304+
* @Security("is_granted('edit', post)")
305+
*/
306+
public function editAction(Post $post)
307+
{
308+
// ...
309+
}
310+
311+
これを直接 ``security.context`` と一緒に使うことも可能です。
312+
また、コントローラーでより簡潔なショートカットを経由することも可能です。
313+
314+
.. code-block:: php
315+
316+
/**
317+
* @Route("/{id}/edit", name="admin_post_edit")
318+
*/
319+
public function editAction($id)
320+
{
321+
$post = // query for the post ...
322+
323+
if (!$this->get('security.context')->isGranted('edit', $post)) {
324+
throw $this->createAccessDeniedException();
325+
}
326+
}
327+
328+
さらに学ぶためには
329+
----------
330+
331+
Symfony のコミュニティによって開発された `FOSUserBundle`_ は Symfony2 における
332+
データベースによるユーザシステムのサポートを追加しました。
333+
これはユーザの登録やパスワードを忘れた時の機能などの共通のタスクも取り扱っています。
334+
335+
`Remember Me feature`_ を有効にすることによってユーザを長期期間ログインさせておくことも可能です。
336+
337+
カスタマーサポートを提供したとき、問題を再現するために異なるユーザでアプリケーションにアクセス
338+
することがたびたび必要になります。
339+
Symfony は `impersonate users`_ の機能を提供しています。
340+
341+
Symfony でサポートしていないユーザのログインメソッドを利用する場合、
342+
`your own user provider`_ と `your own authentication provider`_ で開発することができます。
343+
344+
.. _`Security Cookbook Section`: http://symfony.com/doc/current/cookbook/security/index.html
345+
.. _`security.yml`: http://symfony.com/doc/current/reference/configuration/security.html
346+
.. _`ParamConverter`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
347+
.. _`@Security annotation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/security.html
348+
.. _`security.yml`: http://symfony.com/doc/current/reference/configuration/security.html
349+
.. _`security voter`: http://symfony.com/doc/current/cookbook/security/voters_data_permission.html
350+
.. _`Acces Control List`: http://symfony.com/doc/current/cookbook/security/acl.html
351+
.. _`ACL's`: http://symfony.com/doc/current/cookbook/security/acl.html
352+
.. _`expression`: http://symfony.com/doc/current/components/expression_language/introduction.html
353+
.. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle
354+
.. _`Remember Me feature`: http://symfony.com/doc/current/cookbook/security/remember_me.html
355+
.. _`impersonate users`: http://symfony.com/doc/current/cookbook/security/impersonating_user.html
356+
.. _`your own user provider`: http://symfony.com/doc/current/cookbook/security/custom_provider.html
357+
.. _`your own authentication provider`: http://symfony.com/doc/current/cookbook/security/custom_authentication_provider.html

0 commit comments

Comments
 (0)