9.6. ブラウザのセキュリティ対策機能との連携¶
目次
9.6.1. Overview¶
本節では、ブラウザが提供しているセキュリティ対策機能との連携方法について説明する。
主要なWebブラウザは、ブラウザが提供する機能が悪用されないようにするために、いくつかのセキュリティ対策機能を提供している。 ブラウザが提供するセキュリティ対策機能の一部は、サーバ側でHTTPのレスポンスヘッダを出力することで動作を制御することができる。
Spring Securityは、セキュリティ関連のレスポンスヘッダを出力する機能を用意することで、Webアプリケーションのセキュリティを強化する仕組みを提供している。
Note
セキュリティリスク
セキュリティ関連のレスポンスヘッダを出力しても、セキュリティへのリスクが100%なくなるわけではない。 あくまで、セキュリティリスクを減らすためのサポート機能と考えておくこと。
なお、セキュリティヘッダのサポート状況はブラウザによってことなる。
Note
HTTPヘッダの上書き
後述の設定を行ったとしても、アプリケーションにより、HTTPヘッダが上書きされる可能性は存在する。
9.6.1.1. デフォルトでサポートしているセキュリティヘッダ¶
Spring Securityがデフォルトでサポートしているレスポンスヘッダは以下の9つである。
- Cache-Control (Pragma, Expires)
- X-Frame-Options
- X-Content-Type-Options
- X-XSS-Protection
- Strict-Transport-Security
- Content-Security-Policy(Content-Security-Policy-Report-Only)
- Public-Key-Pins(Public-Key-Pins-Report-Only)
- Referrer-Policy
- Feature-Policy
Tip
ブラウザのサポート状況
これらのヘッダに対する処理は、一部のブラウザではサポートされていない。ブラウザの公式サイトまたは以下のページを参照されたい。
- https://www.owasp.org/index.php/HTTP_Strict_Transport_Security_Cheat_Sheet (Strict-Transport-Security)
- https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet (X-Frame-Options)
- https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#tab=Headers (X-Content-Type-Options, X-XSS-Protection, Content-Security-Policy, Public-Key-Pins)
Note
Referrer-Policyヘッダ
Spring Security 4.2より、ブラウザにReferrer Policyを指示するためのヘッダであるReferrer-Policyヘッダがサポートされた。 詳細については次版以降の開発ガイドラインで記載する予定である。
Note
Feature-Policyヘッダ
Spring Security 5.1より、ブラウザにFeature-Policyを指示するためのヘッダであるFeature-Policyヘッダがサポートされた。 詳細については次版以降の開発ガイドラインで記載する予定である。
9.6.1.1.1. Cache-Control¶
Cache-Controlヘッダは、コンテンツのキャッシュ方法を指示するためのヘッダである。 保護されたコンテンツがブラウザにキャッシュされないようにすることで、権限のないユーザーが保護されたコンテンツを閲覧できてしまうリスクを減らすことができる。
コンテンツがキャッシュされないようにするためには、以下のようなヘッダを出力する。
- レスポンスヘッダの出力例
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
Note
Cache-Controlヘッダの上書き
Spring MVCのControllerクラスが @SessionAttributes
のフォームクラスを定義している、もしくは、
リクエストハンドラで @SessionAttributes
属性のModelを使用している場合は、 Cache-Controlヘッダが上書きされる。
Note
HTTP1.0互換のブラウザ
Spring SecurityはHTTP1.0互換のブラウザもサポートするために、PragmaヘッダとExpiresヘッダも出力する。
9.6.1.1.2. X-Frame-Options¶
X-Frame-Optionsヘッダは、フレーム(<frame>
または<iframe>
要素) 内でのコンテンツの表示を許可するか否かを指示するためのヘッダである。
フレーム内でコンテンツが表示されないようすることで、クリックジャッキングと呼ばれる攻撃手法を使って機密情報を盗みとられるリスクをなくすことができる。
フレーム内での表示を拒否するためには、以下のようなヘッダを出力する。
- レスポンスヘッダの出力例(Spring Securityのデフォルト出力)
X-Frame-Options: DENY
なお、X-Frame-Optionsヘッダには、出力例以外のオプションを指定することができる。
9.6.1.1.3. X-Content-Type-Options¶
X-Content-Type-Optionsヘッダは、コンテンツの種類の決定方法を指示するためのヘッダである。 一部のブラウザでは、Content-Typeヘッダの値を無視してコンテンツの内容をみて決定する。 コンテンツの種類の決定する際にコンテンツの内容を見ないようにすることで、クロスサイトスクリプティングを使った攻撃を受けるリスクを減らすことができる。
コンテンツの種類の決定する際にコンテンツの内容を見ないようにするためには、以下のヘッダを出力する。
- レスポンスヘッダの出力例
X-Content-Type-Options: nosniff
9.6.1.1.4. X-XSS-Protection¶
X-XSS-Protectionヘッダは、ブラウザのXSSフィルター機能を使って有害スクリプトを検出する方法を指示するためのヘッダである。 XSSフィルター機能を有効にして有害なスクリプトを検知するとこで、クロスサイトスクリプティングを使った攻撃を受けるリスクを減らすことができる。
XSSフィルター機能を有効にして有害なスクリプトを検知するためには、以下のようなヘッダを出力する。
- レスポンスヘッダの出力例(Spring Securityのデフォルト出力)
X-XSS-Protection: 1; mode=block
なお、X-XSS-Protectionヘッダには、出力例以外のオプションを指定することができる。
9.6.1.1.5. Strict-Transport-Security¶
Strict-Transport-Securityヘッダーは、HTTPSを使ってアクセスした後にHTTPを使ってアクセスしようとした際に、HTTPSに置き換えてからアクセスすることを指示するためヘッダである。 HTTPSでアクセスした後にHTTPが使われないようにすることで、中間者攻撃と呼ばれる攻撃手法を使って悪意のあるサイトに誘導されるリスクを減らすことができる。
HTTPSでアクセスした後にHTTPが使われないようにするためには、以下のようなヘッダを出力する。
- レスポンスヘッダの出力例(Spring Securityのデフォルト出力)
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
Note
Strict-Transport-Security
Spring Securityのデフォルト実装では、Strict-Transport-Securityヘッダは、アプリケーションサーバに対してHTTPSを使ってアクセスがあった場合のみ出力される。 なお、Strict-Transport-Securityヘッダ値は、オプションを指定することで変更することができる。
9.6.1.1.6. Content-Security-Policy¶
Content-Security-Policyヘッダーはブラウザに読み込みを許可するコンテンツを指示するためのヘッダーである。 ブラウザはContent-Security-Policyヘッダーに指定したホワイトリストのコンテンツのみを読み込むため、悪意のあるコンテンツを読み込むことで実行される攻撃(クロスサイトスクリプティング攻撃など)を受けるリスクを減らすことができる。
Content-Security-Policyヘッダーを送信しない場合、ブラウザは標準の同一オリジンポリシーを適用する。
コンテンツの取得元を同一オリジンのみに制限するためには、以下のようなヘッダーを出力する。
- レスポンスヘッダの出力例
Content-Security-Policy: default-src 'self'
Note
ポリシー違反時のレポート送信について
ポリシー違反時にレポートを送信したい場合、report-uriディレクティブに報告先のURIを指定する。
同一オリジンポリシー違反があった場合にコンテンツをブロックして/csp_report
にレポートを送信するためには、以下のようなヘッダーを出力する。
- レスポンスヘッダの出力例
Content-Security-Policy: default-src 'self'; report-uri /csp_report;
また、ポリシー違反があった際に、コンテンツのブロックを行わずレポートの送信のみを行いたい場合はContent-Security-Policy-Report-Onlyヘッダーを使用する。 Content-Security-Policy-Report-Onlyヘッダーを使用してレポートを収集しながら段階的にポリシーとコンテンツを修正することで、既にサービス提供しているサイトに対してポリシーを適用した場合に正常に動作しなくなるリスクを減らすことが出来る。
同一オリジンポリシー違反があった場合にコンテンツをブロックせず/csp_report
にレポートを送信するためには、以下のようなヘッダーを出力する。
- レスポンスヘッダの出力例
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp_report;
9.6.1.1.7. Public-Key-Pins¶
Public-Key-Pinsヘッダはサイトの証明書の真正性を担保するために、サイトに紐づく証明書の公開鍵をブラウザに提示するヘッダである。 サイトへの再訪問時に中間者攻撃と呼ばれる攻撃手法を使って悪意のあるサイトに誘導された場合でも、 ブラウザが保持する真性のサイト証明書の公開鍵と悪意あるサイトが提示する証明書の公開鍵の不一致を検知して、 アクセスをブロックすることができる。
ブラウザが保持する情報と一致しない証明書を検出した場合にアクセスをブロックさせるためには、以下のようなヘッダを出力する。
- レスポンスヘッダの出力例
Public-Key-Pins: max-age=5184000 ; pin-sha256="d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=" ; pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g="
Note
違反レポートの送信について
アクセスブロック時にブラウザに違反レポートを送信させるためには、Content-Security-Policyと同様にreport-uriディレクティブを指定する。
また、ブラウザにアクセスをブロックさせずに違反レポートを送信させるためには、Public-Key-Pinsヘッダの代わりにPublic-Key-Pins-Report-Onlyヘッダを使用する。
Note
Public-Key-Pinsヘッダの設定について
Public-Key-Pinsヘッダの設定に誤りがあった場合、ユーザが長期間サイトにアクセスできなくなる可能性があるため、 Public-Key-Pins-Report-Onlyヘッダで十分に試験を実施した上でPublic-Key-Pinsヘッダに切り替えることを推奨する。
9.6.2. How to use¶
9.6.2.1. セキュリティヘッダ出力機能の適用¶
前述のセキュリティヘッダ出力機能を適用する方法を説明する。
セキュリティヘッダ出力機能は、Spring 3.2から追加された機能であり、以下のセキュリティヘッダがデフォルトで適用されるようになっている。
- Cache-Control (Pragma, Expires)
- X-Frame-Options
- X-Content-Type-Options
- X-XSS-Protection
- Strict-Transport-Security
そのため、デフォルトで適用されるセキュリティヘッダ出力機能を有効にするための特別な定義は不要である。 なお、デフォルトで適用されるセキュリティヘッダ出力機能を適用したくない場合は、明示的に無効化する必要がある。
セキュリティヘッダ出力機能を無効化する場合は、以下のようなbean定義を行う。
- spring-security.xmlの定義例
<sec:http>
<!-- omitted -->
<sec:headers disabled="true"/> <!-- disabled属性にtrueを設定して無効化 -->
<!-- omitted -->
</sec:http>
9.6.2.2. セキュリティヘッダの選択¶
出力するセキュリティヘッダを選択したい場合は、以下のようなbean定義を行う。 ここではSpring Securityが提供しているすべてのセキュリティヘッダを出力する例になっているが、実際には必要なものだけ指定すること。
- spring-security.xmlの定義例
<sec:headers defaults-disabled="true"> <!-- (1) -->
<sec:cache-control/> <!-- (2) -->
<sec:frame-options/> <!-- (3) -->
<sec:content-type-options/> <!-- (4) -->
<sec:xss-protection/> <!-- (5) -->
<sec:hsts/> <!-- (6) -->
<sec:content-security-policy policy-directives="default-src 'self'" /> <!-- (7) -->
<sec:hpkp report-uri="https://www.example.net/hpkp-report"> <!-- (8) -->
<sec:pins>
<sec:pin algorithm="sha256">d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=</sec:pin>
<sec:pin algorithm="sha256">E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=</sec:pin>
</sec:pins>
</sec:hpkp>
</sec:headers>
項番 | 説明 |
---|---|
(1)
|
まずデフォルトで適用されるヘッダ出力を行うコンポーネント登録を無効化する。
|
(2)
|
Cache-Control(Pragma, Expires)ヘッダを出力するコンポーネントを登録する。
|
(3)
|
Frame-Optionsヘッダを出力するコンポーネントを登録する。
|
(4)
|
X-Content-Type-Optionsヘッダを出力するコンポーネントを登録する。
|
(5)
|
X-XSS-Protectionヘッダを出力するコンポーネントを登録する。
|
(6)
|
Strict-Transport-Securityヘッダを出力するコンポーネントを登録する。
|
(7)
|
Content-Security-PolicyヘッダまたはContent-Security-Policy-Report-Onlyヘッダを出力するコンポーネントを登録する。
|
(8)
|
Public-Key-PinsヘッダまたはPublic-Key-Pins-Report-Onlyヘッダを出力するコンポーネントを登録する。
|
Note
Public-Key-Pinsヘッダの出力について
Spring Securityのデフォルトの設定では、Public-Key-Pinsヘッダではなく、Public-Key-Pins-Report-Onlyヘッダが出力される。
また、Spring Securityのデフォルト実装では、Public-Key-Pinsヘッダは、アプリケーションサーバに対してHTTPSを使ってアクセスがあった場合のみ出力される。
また、不要なものだけ無効化する方法も存在する。
- spring-security.xmlの定義例
<sec:headers>
<sec:cache-control disabled="true"/> <!-- disabled属性にtrueを設定して無効化 -->
</sec:headers>
上記の例だと、Cache-Control関連のヘッダだけが出力されなくなる。
セキュリティヘッダの詳細については公式リファレンスを参照されたい。
Note
Spring Securityによるセキュリティヘッダ付与の仕様変更
Macchinetta Server Framework for Java 1.5.1の依存ライブラリであるSpring Security 4.2.4では、Spring Securityによって先にセキュリティヘッダが付与されることによりController等で任意に付与したヘッダが有効にならないことがあった。
例えば、Controllerで個別にキャッシュ制御のヘッダを付与した場合でもSpring Securityが先に付与したPragma: no-cache
ヘッダが残ることにより意図したキャッシュ制御ができないといった問題があった。
このため、Spring Security 4.2.5及び5.0.2以降ではレスポンスコミットのタイミングでセキュリティヘッダを付与するように変更(spring-projects/spring-security/issues/#5004)されている。
Warning
個別に付与したセキュリティヘッダがSpring Securityにより上書き(追加)される問題
DispatcherServlet
内の処理で付与したセキュリティヘッダがSpring SecurityのHeaderWriter
により上書き(追加)される問題(spring-projects/spring-security/issues/#5193)が報告されている。
Spring Securityでデフォルトのセキュリティヘッダを付与するが、一部のユースケースのみController等で個別にセキュリティヘッダを付与したい場合は、この問題の影響を受けることになる。
ただし、CacheControlHeaderWriterは既に付与されているヘッダを優先する実装となっているため、キャッシュ制御に関するヘッダ(Cache-Control, Pragma, Expires)ではこの問題は発生しない。
9.6.2.3. セキュリティヘッダのオプション指定¶
以下のヘッダでは、Spring Securityがデフォルトで出力する内容を変更することができる。
- X-Frame-Options
- X-XSS-Protection
- Strict-Transport-Security
- Content-Security-Policy(Content-Security-Policy-Report-Only)
- Public-Key-Pins(Public-Key-Pins-Report-Only)
- Referrer-Policy
- Feature-Policy
Spring Securityのbean定義を変更することで、各要素の属性にオプション[1]を指定することができる。
- spring-security.xmlの定義例
<sec:frame-options policy="SAMEORIGIN" />
[1] | 各要素で指定できるオプションは https://docs.spring.io/spring-security/site/docs/5.1.3.RELEASE/reference/htmlsingle/#nsa-headers を参照されたい。 |
9.6.2.4. カスタムヘッダの出力¶
Spring Securityがデフォルトで用意していないヘッダを出力することもできる。
以下のヘッダを出力するケースの例を説明する。
X-WebKit-CSP: default-src 'self'
上記のヘッダを出力する場合は、以下のようなbean定義を行う。
- spring-security.xmlの定義例
<sec:headers>
<sec:header name="X-WebKit-CSP" value="default-src 'self'"/>
</sec:headers>
項番 | 説明 |
---|---|
(1)
|
<sec:headers> 要素の子要素として<sec:header> を追加し、name 属性にヘッダ名をvalue 属性にヘッダ値を指定する。 |
9.6.2.5. リクエストパターン毎のセキュリティヘッダの出力¶
Spring Securityは、RequestMatcher
インタフェースの仕組みを利用して、リクエストのパターン毎にセキュリティヘッダの出力を制御することも可能である。
例えば、保護対象のコンテンツが/secure/
というパスの配下に格納されていて、保護対象のコンテンツへアクセスした時だけCache-Controlヘッダを出力する場合は、以下のようなbean定義を行う。
- spring-security.xmlの定義例
<!-- (1) -->
<bean id="secureCacheControlHeadersWriter"
class="org.springframework.security.web.header.writers.DelegatingRequestMatcherHeaderWriter">
<constructor-arg>
<bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
<constructor-arg value="/secure/**"/>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.security.web.header.writers.CacheControlHeadersWriter"/>
</constructor-arg>
</bean>
<sec:http>
<!-- omitted -->
<sec:headers>
<sec:header ref="secureCacheControlHeadersWriter"/> <!-- (2) -->
</sec:headers>
<!-- omitted -->
</sec:http>
項番 | 説明 |
---|---|
(1)
|
RequestMatcher とHeadersWriter インタフェースの実装クラスを指定してDelegatingRequestMatcherHeaderWriter クラスのbeanを定義する。 |
(2)
|
<sec:headers> 要素の子要素として<sec:header> を追加し、ref 属性に(1)で定義したHeaderWriter のbeanを指定する。 |
Warning
指定したパスが意図した通りに認識されない問題
<sec:http>
とDelegatingRequestMatcherHeaderWriter
がパスマッチングを行うタイミングの違いにより、指定したパスが意図した通りに認識されない場合がある。
具体的には、DelegatingRequestMatcherHeaderWriter
に指定されたパスはセキュリティヘッダ書き込み時(レスポンスのコミット時およびインクルード時)にリクエストパスとマッチングされる。
このため、リクエストのフォワードによりリクエストパスが変更された場合、当初リクエストのパスとマッチングが行われないため、意図したパスでセキュリティヘッダが出力されなくなる。
なお、Spring Security 5.0.10および5.1.2でインクルード時にセキュリティヘッダの書き込みが行われるよう変更された。
特にTilesを利用している場合は、Tilesの処理によりテンプレートJSPにフォワードされるため、DelegatingRequestMatcherHeaderWriter
との併用ができないことが確認されている。
詳細は https://github.com/spring-projects/spring-security/issues/6338 を参照されたい。