6.5. 認可¶
目次
6.5.1. Overview¶
- Web(リクエストURL)
- 特定のURLにアクセスするために必要な権限を設定できる
- 画面項目(JSP)
- 画面中の特定の要素を表示するために必要な権限を設定できる
- メソッド
- 特定のメソッドを実行するために必要な権限を設定できる
6.5.1.1. アクセス認可(リクエストURL)¶
- ユーザのリクエストに対し、Spring Securityのフィルタチェーンが割り込み処理を行う。
- 認可制御の対象となるURLとリクエストのマッチングを行い、アクセス認可マネージャにアクセス認可の判断を問い合わせる。
- アクセス認可マネージャが、ユーザの権限とアクセス認可情報をチェックし、 必要なロールが割り当てられていない場合は、アクセス拒否例外をスローする。
- 必要なロールが割り当てられている場合は、処理を継続する。
6.5.1.2. アクセス認可(JSP)¶
- JSPから生成されたサーブレットが、アクセス認可マネージャに問い合わせる。
- アクセス認可マネージャが、ユーザの権限とアクセス認可情報をチェックし、 必要なロールが割り当てられていない場合は、タグの内部を評価しない。
- 必要なロールが割り当てられている場合は、タグの内部を評価する。
6.5.1.3. アクセス認可(Method)¶
- Springコンテナがアクセス認可情報をもとに、対象のオブジェクトに対してインターセプタを生成、割り込みさせる。
- インターセプタは設定されたロールをもとにアクセス認可マネージャに問い合わせる。
- アクセス認可マネージャが、ユーザが持つ権限とアクセス認可情報をチェックし、 必要なロールが割り当てられていない場合はアクセス拒否例外をスローする。
- 必要なロールが割り当てられている場合は、処理を継続する(設定により、処理を実行した後に権限をチェックすることもできる)。
6.5.2. How to use¶
6.5.2.1. アクセス認可(リクエストURL)¶
6.5.2.1.1. <sec:intercept-url>
要素の設定¶
<sec:http>
要素の子要素である<sec:intercept-url>
要素に制御対象とするURL、認可するロールを記述することで、spring-security.xml
<sec:http auto-config="true" use-expressions="true"> <sec:intercept-url pattern="/admin/*" access="hasRole('ROLE_ADMIN')"/> <!-- omitted --> </sec:http>
属性名説明pattern
アクセス認可を行う対象のURLパターンを記述する。ワイルドカード「*」、「**」が使用できる。「*」では、同一階層のみが対象であるのに対し、「**」では、指定階層以下の全URLが、認可設定の対象となる。access
Spring EL式でのアクセス制御式や、アクセス可能なロールを指定する。method
HTTPメソッド(GETやPOST等)を指定する。指定したメソッドのみに関して、URLパターンとマッチングを行う。指定しない場合は、任意のHTTPメソッドに適用される。主にRESTを利用したWebサービスの利用時に活用できる。requires-channel
「http」、もしくは「https」を指定する。指定したプロトコルでのアクセスを強制する。指定しない場合、どちらでもアクセスできる。上記以外の属性については、B.1.10 <intercept-url>を参照されたい。
spring-security.xml
<sec:http auto-config="true" use-expressions="true"> <sec:intercept-url pattern="/reserve/*" access="hasAnyRole('ROLE_USER','ROLE_ADMIN')" /> <!-- (1) --> <sec:intercept-url pattern="/admin/*" access="hasRole('ROLE_ADMIN')" /> <!-- (2) --> <sec:intercept-url pattern="/**" access="denyAll" /> <!-- (3) --> <!-- omitted --> </sec:http>
項番説明(1)「/reserve/*」にアクセスするためには、「ROLE_USER」もしくは「ROLE_ADMIN」ロールが必要である。hasAnyRole
については、後述する。(2)「/admin/*」にアクセスするためには、「ROLE_ADMIN」ロールが必要である。hasRole
については、後述する。(3)denyAll
を全てのパターンに設定し、権限設定が記述されていないURLに対してはどのユーザもアクセス出来ない設定としている。denyAll
については、後述する。Note
URLパターンの記述順序について
クライアントからのリクエストに対して、intercept-urlで記述されているパターンに、上から順にマッチさせ、 マッチしたパターンに対してアクセス認可を行う。そのため、パターンの記述は、必ず、より限定されたパターンから記述すること。
<sec:http>
属性にuse-expressions="true"
の設定をしたことで、Spring EL式が有効になる。access
属性に記述したSpring EL式は真偽値で評価され、式が真の場合に、アクセスが認可される。spring-security.xml
<sec:http auto-config="true" use-expressions="true"> <sec:intercept-url pattern="/admin/*" access="hasRole('ROLE_ADMIN')"/> <!-- (1) --> <!-- omitted --> </sec:http>
項番 説明 (1)hasRole('ロール名')
を指定することで、ログインユーザが指定したロールを保持していれば真を返す。使用可能なExpression一覧例属性名 説明 hasRole('ロール名')
ユーザが指定したロールを保持していれば、真を返す。hasAnyRole('ロール1','ロール2')
ユーザが指定したいずれかのロールを保持していれば、真を返す。permitAll
常に真を返す。認証されていない場合も、アクセスできることに注意する。denyAll
常に偽を返す。isAnonymous()
匿名ユーザであれば、真を返す。isAuthenticated()
認証されたユーザならば、真を返す。isFullyAuthenticated()
匿名ユーザ、もしくはRememberMe機能での認証であれば、偽を返す。hasIpAddress('IPアドレス')
リクエストURL、およびJSPタグへのアクセス認可のみで、有効となる。指定のIPアドレスからのリクエストであれば、真を返す。その他、使用可能なSpring EL式は、 Common built-in expressionsを参照されたい。演算子を使用した判定も行うことができる。以下の例では、ロールと、リクエストされたIPアドレス両方に合致した場合、アクセス可能となる。spring-security.xml
<sec:http auto-config="true" use-expressions="true"> <sec:intercept-url pattern="/admin/*" access="hasRole('ROLE_ADMIN') and hasIpAddress('192.168.10.1')"/> <!-- omitted --> </sec:http>
使用可能な演算子一覧演算子 説明 [式1] and [式2]
式1、式2が、どちらも真の場合に、真を返す。[式1] or [式2]
いずれかの式が、真の場合に、真を返す。![式]
式が真の場合は偽を、偽の場合は真を返す。
Warning
Spring MVCとSpring Securityでは、リクエストとのマッチングの仕組みが厳密には異なっており、この差異を利用してSpring Securityの認可機能を突破し、ハンドラメソッドにアクセスできる脆弱性が存在する。 本事象の詳細は「CVE-2016-5007 Spring Security / MVC Path Matching Inconsistency」を参照されたい。
本事象は、trimTokens プロパティに false を設定した org.springframework.util.AntPathMatcher のBeanをSpring MVCに適用することで回避することができる。
なお、この設定を適用できるのはSpring Framework 3.2.17以降である。詳細は SPR-14186を参照されたい。
<mvc:annotation-driven> <mvc:path-matching path-matcher="pathMatcher" /> </mvc:annotation-driven> <bean id="pathMatcher" class="org.springframework.util.AntPathMatcher"> <property name="trimTokens" value="false" /> </bean>
上記の対策をTERASOLUNA Global Framework for Javaで提供するブランクプロジェクトでは設定しているが、 設定を外すと脆弱性にさらされてしまうので注意する必要がある。
また、特定のURLに対してアクセスポリシーを設ける(pattern
属性に*
や**
などのワイルドカード指定を含めない)場合、
拡張子を付けたパターンとリクエストパスの末尾に/
を付けたパターンに対するアクセスポリシーの追加が必須である。
下記の設定例は、/restrict
に対して「ROLE_ADMIN」ロールを持つユーザからのアクセスのみを許可している。
<sec:http> <sec:intercept-url pattern="/restrict.*" access="hasRole('ADMIN')" /> <!-- (1) --> <sec:intercept-url pattern="/restrict/" access="hasRole('ADMIN')" /> <!-- (2) --> <sec:intercept-url pattern="/restrict" access="hasRole('ADMIN')" /> <!-- (3) --> <!-- omitted --> </sec:http>
項番 説明 (1)/restrict
に拡張子を付けたパターン(/restrict.json
など)のアクセスポリシーを定義する。 (2)/restrict
にリクエストパスの末尾に/
を付けたパターン(/restrict/
など)のアクセスポリシーを定義する。 (3)/restrict
に対するアクセスポリシーを定義する。
Warning
Spring SecurityとSpring MVCではアクセスされたURLを取得する方法が異なっているため、この差異を利用してSpring Securityの認可機能を突破しハンドラメソッドにアクセスできる脆弱性が存在する。 本事象は、APサーバまたはバージョンによって発生状況が異なる。 詳細は「CVE-2016-9879 Encoded “/” in path variables」を参照されたい。
対策として、本事象への対策が行われているTERASOLUNA Server Framework for Java 5.3.0.RELEASE以降にバージョンアップされたい。
6.5.2.1.2. アクセス認可制御を行わないURLの設定¶
- spring-security.xml
<sec:http pattern="/css/*" security="none"/> <!-- 属性の指定順番で(1)~(2) --> <sec:http pattern="/login" security="none"/> <sec:http auto-config="true" use-expressions="true"> <!-- omitted --> </sec:http>
項番 説明 (1)pattern
属性に設定を行う対象のURLパターンを記述する。pattern
属性を記述しない場合、すべてのパターンにマッチする。 (2)security
属性にnone
を指定することで、pattern
属性に記述されたパスは、Spring Securityフィルタチェインを回避することができる。
6.5.2.1.3. URLパターンでの例外処理¶
org.springframework.security.access.AccessDeniedException
がスローされる。org.springframework.security.web.access.ExceptionTranslationFilter
に設定されたorg.springframework.security.web.access.AccessDeniedHandlerImpl
が、エラーコード403を返却する。spring-security.xml
<sec:http auto-config="true" use-expressions="true"> <!-- omitted --> <sec:access-denied-handler error-page="/accessDeneidPage" /> <!-- (1) --> </sec:http>
項番説明(1)<sec:access-denied-handler>
要素のerror-page
属性に、遷移先のパスを指定する。
6.5.2.2. アクセス認可(JSP)¶
<sec:authorize>
を利用する。<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<sec:authorize>
タグの属性一覧属性名説明access
アクセス制御式を記述する。真であれば、タグ内が評価される。url
設定したURLに対して権限が与えられている場合に、タグ内が評価される。リンクの表示の制御等に利用する。method
HTTPメソッド(GETやPOST等)を指定する。 url属性と合わせて利用し、指定したメソッドのみに関して、指定したURLパターンとマッチングを行う。指定しない場合、GETが適用される。ifAllGranted
設定したロールが全て与えられている場合に、タグ内が評価される。ロール階層機能は効かない。ifAnyGranted
設定したロールについて、いずれかが与えられている場合に、タグ内が評価される。ロール階層機能は効かない。ifNotGranted
設定されたロールが与えられていない場合、タグの中身が評価される。ロール階層機能は効かない。var
タグの評価結果を格納するpageスコープの変数を宣言する。同等の権限チェックをページ内で行う場合に利用する。
<sec:authorize>
タグの使用例を示す。spring-security.xml
<div> <sec:authorize access="ROLE_USER"> <!-- (1) --> <p>This screen is for ROLE_USER</p> </sec:authorize> <sec:authorize url="/admin/menu"> <!-- (2) --> <p> <a href="/admin/menu">Go to admin screen</a> </p> </sec:authorize> </div>
項番説明(1)「ROLE_USER」を持つ場合のみ、タグ内が表示される。(2)「/admin/menu」に対してアクセスが認可されている場合、タグ内が表示される。Warning
<sec:authorize>
タグによる認可処理は、画面表示の制御でしかないため、特定の権限でリンクを表示されなくても、URLが推測されれば、直接リンク先のURLにアクセスできてしまう。 そのため、必ず、前述の「アクセス認可(リクエストURL)」、もしくは、後述の「アクセス認可(Method)」を併用して、本質的な認可制御をに行うこと。
6.5.2.3. アクセス認可(Method)¶
org.springframework.security.access.prepost.PreAuthorize
アノテーションを設定すればよい。spring-security.xml
<sec:global-method-security pre-post-annotations="enabled"/> <!-- (1) -->
項番説明(1)<sec:global-method-security>
要素のpre-post-annotations
属性をenabled
に指定する。デフォルトはdisabled
である。Javaコード
@Service @Transactional public class UserServiceImpl implements UserSerice // omitted @PreAuthorize("hasRole('ROLE_ADMIN')") // (1) @Override public User create(User user) { // omitted } @PreAuthorize("isAuthenticated()") @Override public User update(User user) { // omitted } }
項番説明(1)アクセス制御式を記述する。メソッドを実行する前に式が評価され、真であれば、メソッドが実行される。偽であれば、org.springframework.security.access.AccessDeniedException
がスローされる。設定可能な値は、<sec:intercept-url>要素の設定で述べたExpressionや、およびSpring Expression Language (SpEL)で記述された式である。Tip
上記の設定では
org.springframework.security.access.prepost.PreAuthorize
以外にも、以下のアノテーションを使用できる。org.springframework.security.access.prepost.PostAuthorize
org.springframework.security.access.prepost.PreFilter
org.springframework.security.access.prepost.PostFilter
これらの詳細はSpring Security マニュアルを参照されたい。
Note
Spring SecurityではJava標準であるJSR-250の
javax.annotation.security.RolesAllowed
アノテーションによる認可制御も可能であるが、@RolesAllowed
ではSpELによる記述ができない。@PreAuthorize
であればSpELを用いて、spring-security.xmlの設定と同じ記法で認可制御Note
リクエストパスに対する認可制御はControllerのメソッドにアノテーションをつけるのではなく、spring-security.xmlに設定を行うことを推奨する。
ServiceがWeb経由でしか実行されず、リクエストパスのすべてのパターンが認可制御されているのであればServiceの認可制御は行わなくても良い。 Serviceがどこから実行されるか分からず、認可制御が必要な場合にアノテーションを使用するとよい。
6.5.3. How to extend¶
6.5.3.1. ロール階層機能¶
Spring Security 設定ファイル
<sec:http auto-config="true" use-expressions="true">
<sec:intercept-url pattern="/user/*" access="hasAnyRole('ROLE_USER')" />
<!-- omitted -->
</sec:http>
6.5.3.1.1. 共通設定¶
org.springframework.security.access.hierarchicalroles.RoleHierarchy
クラスのBean定義を行う。spring-security.xml
<bean id="roleHierarchy" class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl"> <!-- (1) --> <property name="hierarchy"> <value> <!-- (2) --> ROLE_ADMIN > ROLE_STAFF ROLE_STAFF > ROLE_USER </value> </property> </bean>
項番説明(1)RoleHierarchy
のデフォルトorg.springframework.security.access.hierarchicalroles.RoleHierarchyImpl
クラスを指定する。(2)hierarchy
プロパティに階層関係を定義する。書式:[上位ロール] > [下位ロール]例では、STAFFはUSERに認可されたすべてのリソースに、アクセスできる。ADMINはUSER、STAFFに認可されたすべてのリソースに、アクセスできる。
6.5.3.1.2. アクセス認可(リクエストURL)、アクセス認可(JSP)での使用方法¶
spring-security.xml
<bean id="webExpressionHandler" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler"> <!-- (1) --> <property name="roleHierarchy" ref="roleHierarchy"/> <!-- (2) --> </bean> <sec:http auto-config="true" use-expression="true"> <!-- omitted --> <sec:expression-handler ref="webExpressionHandler" /> <!-- (3) --> </sec:http>
項番説明(1)クラスにorg.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler
を指定する。(2)roleHierarchy
プロパティにRoleHierarchy
のBean IDをプロパティに設定する。(3)expression-handler
要素に、org.springframework.security.access.expression.SecurityExpressionHandler
を実装したハンドラクラスのBean IDを指定する。
6.5.3.1.3. アクセス認可(Method)での使用方法¶
spring-security.xml
<bean id="methodExpressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler"> <!-- (1) --> <property name="roleHierarchy" ref="roleHierarchy"/> <!-- (2) --> </bean> <sec:global-method-security pre-post-annotations="enabled"> <sec:expression-handler ref="methodExpressionHandler" /> <!-- (3) --> </sec:global-method-security>
項番説明(1)クラスにorg.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler
を指定する。(2)roleHierarchy
プロパティにRoleHierarchy
のBean IDをプロパティに設定する。(3)expression-handler
要素に、org.springframework.security.access.expression.SecurityExpressionHandler
を実装したハンドラクラスのBean IDを指定する。