9.6. ブラウザのセキュリティ対策機能との連携¶
9.6.1. Overview¶
本節では、ブラウザが提供しているセキュリティ対策機能との連携方法について説明する。
Spring Securityは、セキュリティ関連のレスポンスヘッダを出力する機能を用意することで、Webアプリケーションのセキュリティを強化する仕組みを提供している。
Note
セキュリティリスク
セキュリティ関連のレスポンスヘッダを出力しても、セキュリティへのリスクが100%なくなるわけではない。
あくまで、セキュリティリスクを減らすためのサポート機能と考えておくこと。
なお、セキュリティヘッダのサポート状況はブラウザによってことなる。
Note
HTTPヘッダの上書き
後述の設定を行ったとしても、アプリケーションにより、HTTPヘッダが上書きされる可能性は存在する。
9.6.1.1. Springがサポートしているセキュリティヘッダ¶
Spring Securityがサポートしているレスポンスヘッダは以下である。
Cache-Control (Pragma, Expires)
X-Frame-Options
X-Content-Type-Options
Strict-Transport-Security
Content-Security-Policy(Content-Security-Policy-Report-Only)
Referrer-Policy
Permissions-Policy
Clear-Site-Data
また、以下のセキュリティヘッダもサポートしているが、OWASPで非推奨とされている。
X-XSS-Protection
Public-Key-Pins(Public-Key-Pins-Report-Only)
Feature-Policy
詳しくはResponse Headersを参照されたい。
9.6.1.1.1. Cache-Control¶
コンテンツがキャッシュされないようにするためには、以下のようなヘッダを出力する。
レスポンスヘッダの出力例(Spring Securityのデフォルト出力)
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¶
<frame>
または<iframe>
要素) 内でのコンテンツの表示を許可するか否かを指示するためのヘッダである。フレーム内での表示を拒否するためには、以下のようなヘッダを出力する。
レスポンスヘッダの出力例(Spring Securityのデフォルト出力)
X-Frame-Options: DENY
出力例以外の設定についてはResponse HeadersのX-Frame-Optionsを参照されたい。
9.6.1.1.3. X-Content-Type-Options¶
コンテンツの種類の決定する際にコンテンツの内容を見ないようにするためには、以下のヘッダを出力する。
レスポンスヘッダの出力例(Spring Securityのデフォルト出力)
X-Content-Type-Options: nosniff
9.6.1.1.4. Strict-Transport-Security¶
HTTPSでアクセスした後にHTTPが使われないようにするためには、以下のようなヘッダを出力する。
レスポンスヘッダの出力例(Spring Securityのデフォルト出力)
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
出力例以外の設定についてはResponse HeadersのStrict-Transport-Securityを参照されたい。
Note
Strict-Transport-Security
Spring Securityのデフォルト実装では、Strict-Transport-Securityヘッダは、アプリケーションサーバに対してHTTPSを使ってアクセスがあった場合のみ出力される。
なお、Strict-Transport-Securityヘッダ値は、オプションを指定することで変更することができる。
Note
HTTP Strict Transport Security (HSTS) preload list
Strict-Transport-Securityヘッダーを設定していても、一度HTTPSアクセスが行われるまでの間や有効期限切れ後のアクセスでは中間者攻撃を受けるリスクがある。
Googleはこのリスクを回避出来るようにHSTS preload listを運営している。このリストにドメインを登録すると、ブラウザからのアクセスで自動的にHTTPSが使用される。
主要なブラウザは全て、HSTS preload listに対応している。
HSTS preload listへのドメインの登録方法はHSTS Preload List Submissionを参照されたい。
Spring SecurityではHSTS preload listへの登録に必要となるpreloadディレクティブをサポートしており、オプションを指定することで出力することが出来る。
9.6.1.1.5. Content-Security-Policy¶
Content-Security-Policyヘッダを送信しない場合、ブラウザは標準の同一オリジンポリシーを適用する。
コンテンツの取得元を同一オリジンのみに制限するためには、以下のようなヘッダを出力する。
レスポンスヘッダの出力例
Content-Security-Policy: default-src 'self'
出力例以外の設定についてはResponse HeadersのContent-Security-Policyを参照されたい。
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;
Note
混在コンテンツについて
HTTPSのページの中にHTTPで送られてくるコンテンツ(画像、動画、スタイルシート、スクリプト等)が含まれる場合、混在コンテンツと呼ばれる。混在コンテンツが存在する場合、中間者攻撃を受けるリスクが発生する
Google Chrome 81以降では混在コンテンツに対してHTTPSアクセスを強制し、HTTPSでアクセスできない場合はブロックを行う。
upgrade-insecure-requestsディレクティブを指定することでChromeと同等の動作をブラウザに指示することが出来る。
レスポンスヘッダの出力例
Content-Security-Policy: upgrade-insecure-requests; default-src 'self';
9.6.1.1.6. Referrer-Policy¶
レスポンスヘッダの出力例
Referrer-Policy: no-referrer
出力例以外の設定についてはResponse HeadersのReferrer-Policyを参照されたい。
9.6.1.1.7. Permissions-Policy¶
レスポンスヘッダの出力例
Permissions-Policy: geolocation=(self)
出力例以外の設定についてはResponse HeadersのPermissions-Policyを参照されたい。
9.6.1.1.8. Clear-Site-Data¶
レスポンスヘッダの出力例
Clear-Site-Data: "cache"
出力例以外の設定についてはResponse HeadersのClear-Site-Dataを参照されたい。
9.6.1.1.9. X-XSS-Protection¶
Warning
X-XSS-ProtectionヘッダはOWASPで非推奨となっている。
Spring Securityのデフォルト設定ではX-XSS-Protectionヘッダを出力しているが、XSS Auditorを無効にし、レスポンスを処理するブラウザのデフォルトの挙動を取らせないようにするために、ヘッダの値を”0
“としている。
OWASPではX-XSS-Protectionヘッダの代わりにContent-Security-Policyヘッダを使用することを推奨している。
レスポンスヘッダの出力例(Spring Securityのデフォルト出力)
X-XSS-Protection: 0
9.6.1.1.10. Public-Key-Pins¶
Warning
Public-Key-PinsヘッダはOWASPで非推奨となっている。
ブラウザが保持する情報と一致しない証明書を検出した場合にアクセスをブロックさせるためには、以下のようなヘッダを出力する。
レスポンスヘッダの出力例
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ヘッダの設定に誤りがあった場合、ユーザが長期間サイトにアクセスできなくなる可能性がある。
9.6.1.1.11. Feature-Policy¶
Warning
Feature-PolicyヘッダはOWASPで非推奨となっている。
OWASPではFeature-Policyヘッダの代わりにPermissions-Policyヘッダを使用することを推奨している。
ブラウザが保持する情報と一致しない証明書を検出した場合にアクセスをブロックさせるためには、以下のようなヘッダを出力する。
レスポンスヘッダの出力例
Feature-Policy: geolocation 'self'
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定義を行う。
SpringSecurityConfig.javaの定義例
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// omitted
http.headers(headers -> headers.disable()); // headers.disable()を呼び出して無効化
// omitted
return http.build();
}
spring-security.xmlの定義例
<sec:http request-matcher="ant">
<!-- omitted -->
<sec:headers disabled="true"/> <!-- disabled属性にtrueを設定して無効化 -->
<!-- omitted -->
</sec:http>
9.6.2.2. セキュリティヘッダの選択¶
SpringSecurityConfig.javaの定義例
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// omitted
http.headers(headers -> headers.defaultsDisabled() // (1)
.cacheControl(Customizer.withDefaults()) // (2)
.frameOptions(Customizer.withDefaults()) // (3)
.contentTypeOptions(Customizer.withDefaults()) // (4)
.httpStrictTransportSecurity(Customizer.withDefaults()) // (5)
.contentSecurityPolicy(csp -> csp.policyDirectives("default-src 'self'")) // (6)
.referrerPolicy(Customizer.withDefaults()) // (7)
.permissionsPolicy(permissions -> permissions.policy("geolocation=(self)")) // (8)
);
// omitted
return http.build();
}
@Bean
public ClearSiteDataHeaderWriter clearSiteDataHeaderWriter() {
return new ClearSiteDataHeaderWriter(Directive.CACHE);
}
項番 |
説明 |
---|---|
(1)
|
デフォルトで適用されるヘッダ出力を行うコンポーネント登録を無効化する。
|
(2)
|
Cache-Control(Pragma, Expires)ヘッダを出力するコンポーネントを登録する。
|
(3)
|
Frame-Optionsヘッダを出力するコンポーネントを登録する。
|
(4)
|
X-Content-Type-Optionsヘッダを出力するコンポーネントを登録する。
|
(5)
|
Strict-Transport-Securityヘッダを出力するコンポーネントを登録する。
|
(6)
|
Content-Security-PolicyヘッダまたはContent-Security-Policy-Report-Onlyヘッダを出力するコンポーネントを登録する。
|
(7)
|
Referrer-Policyヘッダを出力するコンポーネントを登録する。
|
(8)
|
Permissions-Policyヘッダを出力するコンポーネントを登録する。
|
spring-security.xmlの定義例
<sec:http request-matcher="ant">
<!-- omitted -->
<sec:headers defaults-disabled="true"> <!-- (1) -->
<sec:cache-control /> <!-- (2) -->
<sec:frame-options /> <!-- (3) -->
<sec:content-type-options /> <!-- (4) -->
<sec:hsts /> <!-- (5) -->
<sec:content-security-policy policy-directives="default-src 'self'"/> <!-- (6) -->
<sec:referrer-policy /> <!-- (7) -->
<sec:permissions-policy policy="geolocation=(self)"/> <!-- (8) -->
</sec:headers>
<!-- omitted -->
</sec:http>
項番 |
説明 |
---|---|
(1)
|
デフォルトで適用されるヘッダ出力を行うコンポーネント登録を無効化する。
|
(2)
|
Cache-Control(Pragma, Expires)ヘッダを出力するコンポーネントを登録する。
|
(3)
|
Frame-Optionsヘッダを出力するコンポーネントを登録する。
|
(4)
|
X-Content-Type-Optionsヘッダを出力するコンポーネントを登録する。
|
(5)
|
Strict-Transport-Securityヘッダを出力するコンポーネントを登録する。
|
(6)
|
Content-Security-PolicyヘッダまたはContent-Security-Policy-Report-Onlyヘッダを出力するコンポーネントを登録する。
|
(7)
|
Referrer-Policyヘッダを出力するコンポーネントを登録する。
|
(8)
|
Permissions-Policyヘッダを出力するコンポーネントを登録する。
|
また、不要なものだけ無効化する方法も存在する。
SpringSecurityConfig.javaの定義例
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// omitted
http.headers(headers -> headers.cacheControl(cacheControl -> cacheControl.disable())); // cacheControl.disable()を呼び出して無効化
// omitted
return http.build();
}
spring-security.xmlの定義例
<sec:http request-matcher="ant">
<!-- omitted -->
<sec:headers>
<sec:cache-control disabled="true"/> <!-- disabled属性にtrueを設定して無効化 -->
</sec:headers>
<!-- omitted -->
</sec:http>
上記の例だと、Cache-Control関連のヘッダだけが出力されなくなる。
セキュリティヘッダの詳細についてはSpring Security Reference -Default Security Headers-を参照されたい。
9.6.2.3. セキュリティヘッダのオプション指定¶
Spring Securityのbean定義を変更することで、各要素の属性にオプション[1]を指定することができる。
SpringSecurityConfig.javaの定義例
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// omitted
http.headers(headers -> headers.addHeaderWriter(
new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN)));
return http.build();
}
spring-security.xmlの定義例
<sec:frame-options policy="SAMEORIGIN" />
9.6.2.4. カスタムヘッダの出力¶
Spring Securityがデフォルトで用意していないヘッダを出力することもできる。
以下のヘッダを出力するケースの例を説明する。
X-WebKit-CSP: default-src 'self'
上記のヘッダを出力する場合は、以下のようなbean定義を行う。
SpringSecurityConfig.javaの定義例
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// omitted
http.headers(headers -> headers.addHeaderWriter(
new StaticHeadersWriter("X-WebKit-CSP", "default-src 'self'")));
// omitted
return http.build();
}
項番 |
説明 |
---|---|
(1)
|
HeadersConfigurer#addHeaderWriter の引数にStaticHeadersWriter を設定し、第一引数にヘッダ名を、第二引数にヘッダ値を指定する。 |
spring-security.xmlの定義例
<sec:http request-matcher="ant">
<!-- omitted -->
<sec:headers>
<sec:header name="X-WebKit-CSP" value="default-src 'self'"/>
</sec:headers>
<!-- omitted -->
</sec:http>
項番 |
説明 |
---|---|
(1)
|
<sec:headers> 要素の子要素として<sec:header> を追加し、name 属性にヘッダ名をvalue 属性にヘッダ値を指定する。 |
9.6.2.5. リクエストパターン毎のセキュリティヘッダの出力¶
Spring Securityは、RequestMatcher
インタフェースの仕組みを利用して、リクエストのパターン毎にセキュリティヘッダの出力を制御することも可能である。
例えば、保護対象のコンテンツが/secure/
というパスの配下に格納されていて、保護対象のコンテンツへアクセスした時だけCache-Controlヘッダを出力する場合は、以下のようなbean定義を行う。
SpringSecurityConfig.javaの定義例
// (1)
@Bean("secureCacheControlHeadersWriter")
public DelegatingRequestMatcherHeaderWriter secureCacheControlHeadersWriter() {
AntPathRequestMatcher antPathRequestMatcher = new AntPathRequestMatcher("/secure/**");
CacheControlHeadersWriter cacheControlHeadersWriter = new CacheControlHeadersWriter();
return new DelegatingRequestMatcherHeaderWriter(antPathRequestMatcher, cacheControlHeadersWriter);
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// omitted
http.headers(headers -> headers.defaultsDisabled().addHeaderWriter(
secureCacheControlHeadersWriter())); // (2)
// omitted
return http.build();
}
項番 |
説明 |
---|---|
(1)
|
RequestMatcher とHeadersWriter インタフェースの実装クラスを指定してDelegatingRequestMatcherHeaderWriter クラスのbeanを定義する。 |
(2)
|
HeadersConfigurer#addHeaderWriter の引数に(1)で定義したHeaderWriter の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 request-matcher="ant">
<!-- 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を指定する。 |