Spring Security 使い方メモ　レスポンスヘッダー


:::note warn
Spring Security は 5.4 以降、設定の書き方に大幅な変更が入っています。
詳しくは @suke_masa さんの [Spring Security 5.7でセキュリティ設定の書き方が大幅に変わる件 - Qiita](https://qiita.com/suke_masa/items/908805dd45df08ba28d8) を参照してください。
:::


[基礎・仕組み的な話](http://qiita.com/opengl-8080/items/c105152c9ca48509bd0c)
[認証・認可の話](http://qiita.com/opengl-8080/items/032ed0fa27a239bdc1cc)
[Remember-Me の話](http://qiita.com/opengl-8080/items/7c34053c74448d39e8f5)
[CSRF の話](http://qiita.com/opengl-8080/items/3720ed415c2df26251a7)
[セッション管理の話](http://qiita.com/opengl-8080/items/ad9159910501d1989876)
[メソッドセキュリティの話](http://qiita.com/opengl-8080/items/76715fd3a92b6ca680d0)
[CORS の話](http://qiita.com/opengl-8080/items/db0aa1ec0be36886953b)
[Run-As の話](http://qiita.com/opengl-8080/items/49aa6381c16c8b9a7f3c)
[ACL の話](http://qiita.com/opengl-8080/items/24a3118ef36bcf55ba71)
[テストの話](http://qiita.com/opengl-8080/items/eaa8f4eb9286a3df7986)
[MVC, Boot との連携の話](http://qiita.com/opengl-8080/items/4824d4a8c012f7ec53db)

番外編
[Spring Security にできること・できないこと](http://qiita.com/opengl-8080/items/6dc37f8b77abb5ae1642)

# デフォルトで設定されるヘッダー
Spring Security を使用していると、デフォルトで下記ヘッダーがレスポンスに追加される。

```
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
```

※`Strict-Transport-Security` は HTTPS のときのみ追加。

## キャッシュに関するヘッダー
`Cache-Control`, `Pragma`, `Expires` は、どれもブラウザにキャッシュをさせないように設定されている。

ログインしなければ見れないページをキャッシュされていると、ログアウト後であっても悪意あるユーザはローカルに残されたキャッシュを見ることで保護すべき情報を見れてしまう可能性がある。

そのため、このようにキャッシュを許可しない設定になっている。

## X-Content-Type-Options: nosniff
Web ブラウザのなかには、 `Content-Type` ではなくファイルの中身を見て、そのファイルの種類を判別しようとするものがある。
これを Content Sniffing と言うっぽい。

これが有効になっていると、ファイルに仕込まれた悪意のあるコード[^1]をブラウザが誤って実行してしまう危険性がある。

[^1]: XSS を成立させる JavaScript コードが埋め込まれた画像ファイルとか

このヘッダー（`X-Content-Type-Options: nosniff`）がレスポンスに設定されていると、ブラウザはファイル種別の自動判定をしなくなる（IE は 8 以上がサポート）。

## Strict-Transport-Security
このヘッダーは HTTPS 通信の場合のみデフォルトで設定される。

あるサイトにアクセスする際、ブラウザの URL 欄に `xxx.com/xxxx` のようにプロトコルを省略してホスト名だけを入力してアクセスしたとする。
普通、この URL のプロトコルは HTTP で補完され、リクエストが実行される

サイトによっては HTTP でリクエストが来た場合は HTTPS の通信に切り替えるようリダイレクトをするものがあるかもしれない。

しかし、最初の通信は HTTP で行われているため、[中間者攻撃](http://www.weblio.jp/content/%E4%B8%AD%E9%96%93%E8%80%85%E6%94%BB%E6%92%83) を受ける危険性がある。

`Strict-Transport-Security` ヘッダーをつけると、ブラウザは「そのホストには HTTPS で通信しなければならない」と認識するようになる。
すると、プロトコルを省略して URL 入力しても、自動的に HTTPS で通信するようになる。

ただし、このヘッダーはレスポンスヘッダーなので、最低１回は HTTPS で通信していなければならない。
当然、最初の１回目を HTTP でアクセスした場合、その通信は無防備になっている。
（こういうのを [TOFU (Trust On First Use)](https://en.wikipedia.org/wiki/Trust_on_first_use) と言うらしい）

このヘッダーにはパラメータが指定でき、 namespace や Java Configuration の場合は次のようにする。

**namespace**

```xml:applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:sec="http://www.springframework.org/schema/security"
       ...>
    
    ...
    
    <sec:http>
        ...
        <sec:headers>
            <sec:hsts max-age-seconds="60"
                      include-subdomains="false" />
        </sec:headers>
    </sec:http>
    
    ...
</beans>
```

- `<http>` の下に `<headers>` タグを追加し、さらにその下に `<hsts>` タグを追加して制御する。
    - `hsts` = `HTTP Strict Transport Security` の略
- `max-age-seconds` は `max-age` を、
- `include-subdomains` は `includeSubDomains` をそれぞれ設定する。
- パラメータの意味は [Strict-Transport-Securty](https://developer.mozilla.org/ja/docs/Web/Security/HTTP_Strict_Transport_Security#Enabling_Strict_Transport_Security) の説明を参照。

**Java Configuration**

```java:
package sample.spring.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

import java.util.Collections;

@EnableWebSecurity
@ComponentScan
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                ...
                .headers()
                    .httpStrictTransportSecurity()
                    .maxAgeInSeconds(60)
                    .includeSubDomains(false);
    }
    
    ...
}
```

- `.headers().httpStrictTransportSecurity()` で `Strict-Transport-Security` の設定を開始できる。

## X-Frame-Options
自分の Web サイトを `<iframe>` で埋め込むことができるようにしていると、[クリックジャッキング](http://www.techscore.com/blog/2015/03/05/%E3%82%AF%E3%83%AA%E3%83%83%E3%82%AF%E3%82%B8%E3%83%A3%E3%82%AE%E3%83%B3%E3%82%B0%E3%81%A3%E3%81%A6%EF%BC%9F/)攻撃を受ける危険性がある。

英語だが、 [Youtube に解説動画](https://www.youtube.com/watch?v=3mk0RySeNsU) が上がっている（Spring Security のリファレンスで紹介されていた動画）。
前半は解説のために `<iframe>` が見えるようにして、後半は `<iframe>` を完全に見えなくして攻撃の様子を説明している。

これを防ぐには、自サイトを `<iframe>` で埋め込むことができないようにする必要がある。
`X-Frame-Options` をレスポンスヘッダーに付けることで、それが実現できる。

Spring Security がデフォルトで設定する `DENY` は、全てのサイトから `<iframe>` での埋め込みを拒否する。

同じオリジン（スキーム・ホスト・ポートの組み合わせ）は信頼できるということで埋め込みを許可したい場合は、次のように設定する。

**namespace**

```xml:applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:sec="http://www.springframework.org/schema/security"
       ...>
    
    ...
    
    <sec:http>
        ...
        <sec:headers>
            <sec:frame-options policy="SAMEORIGIN" />
        </sec:headers>
    </sec:http>
    
    ...
</beans>
```

- `<frame-options>` タグの `policy` で設定する。

**Java Configuration**

```java:MySpringSecurityConfig.java
package sample.spring.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

import java.util.Collections;

@EnableWebSecurity
@ComponentScan
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                ...
                .headers()
                    .frameOptions().sameOrigin();
    }
    
    ...
}
```

- `frameOptions()` で `X-Frame-Options` についての設定を開始できる。

## X-XSS-Protection
Web ブラウザには 反射型の XSS を抑止するための機能が標準で備わっているものがある。
しかしブラウザによっては何もしないとその機能が有効にならないものもある。

レスポンスヘッダーに `X-XSS-Protection` を入れておくと、ブラウザの反射型 XSS を抑止する機能を有効にすることができる。

ただし、この機能は XSS を完全に防げるようになるものではなく、あくまで攻撃を緩和させるものな点に注意（これをセットしていればオールOKという話ではない、ということ）。

# デフォルトでは設定されないヘッダー
## Content-Security-Policy
`Content-Security-Policy` は、 XSS 攻撃の軽減と報告を目的としたヘッダー。

例えば、レスポンスヘッダーに

```
Content-Security-Policy: script-src 'self'
```

とすることで、自分自身のオリジン以外から JavaScript のソースを読み込もうとするのをブロックできるようになる。
要は、あらかじめ信頼できるオリジンだけからしかファイルなどを読み込めないようにしておくことで、攻撃者の用意した意図しないスクリプトを読み込んで実行してしまわないようにする防衛策、という感じ。

このヘッダー自体の細かい書き方は

- [Content Security Policy (CSP) - Web セキュリティ | MDN](https://developer.mozilla.org/ja/docs/Web/Security/CSP)
- [CSP のポリシーディレクティブ - Web セキュリティ | MDN](https://developer.mozilla.org/ja/docs/Web/Security/CSP/CSP_policy_directives#Source_lists)

この辺を参照のこと。

Spring Security で使う場合は次のように書く。

**namespace**

```xml:applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:sec="http://www.springframework.org/schema/security"
       ...>
    
    <context:component-scan base-package="sample.spring.security" />
    
    <sec:http>
        ...
        <sec:headers>
            <sec:content-security-policy policy-directives="script-src 'self'" />
        </sec:headers>
    </sec:http>
    
    ...
</beans>
```

- `<content-security-policy>` タグを追加し、 `policy-directives` 属性で定義する。

**Java Configuration**

```java:MySpringSecurityConfig.java
package sample.spring.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

import java.util.Collections;

@EnableWebSecurity
@ComponentScan
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                ...
                .headers()
                    .contentSecurityPolicy("script-src 'self'");
    }
    
    ...
}
```

- `contentSecurityPolicy()` メソッドで定義する。

# 任意のヘッダーを書き出す
## ヘッダーを固定で指定する
**namespace**

```xml:applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:sec="http://www.springframework.org/schema/security"
       ...>
    
    <sec:http>
        ...
        <sec:headers>
            <sec:header name="Hoge" value="fuga" />
        </sec:headers>
    </sec:http>
    
    ...
</beans>
```
- `<header>` タグで任意のレスポンスヘッダーを設定できる。

**Java Configuration**

```java:MySpringSecurityConfig.java
package sample.spring.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.header.writers.StaticHeadersWriter;

import java.util.Collections;

@EnableWebSecurity
@ComponentScan
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                ...
                .headers()
                    .addHeaderWriter(new StaticHeadersWriter("Hoge", "fuga"));
    }
    
    ...
}
```

- `addHeaderWriter()` の引数に `StaticHeadersWriter` のインスタンスを渡す。
- `StaticHeadersWriter` のコンストラクタは、第一引数がヘッダー名で、第二引数がヘッダーの値。

**実行結果**

![spring-security.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/d75eaa68-de96-ac0a-2954-89ba12c5cb03.jpeg)


## HeaderWriter を指定する
`HeaderWriter` インターフェースを実装したクラスを作成すれば、ヘッダーの書き出しをプログラムで制御できる。

```java:MyHeaderWriter.java
package sample.spring.security.header;

import org.springframework.security.web.header.HeaderWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyHeaderWriter implements HeaderWriter {
    
    @Override
    public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
        response.setHeader("My-Header", "My-Value");
    }
}
```

**namespace**

```xml:applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:sec="http://www.springframework.org/schema/security"
       ...>
    
    <sec:http>
        ...
        <sec:headers>
            <sec:header ref="myHeaderWriter" />
        </sec:headers>
    </sec:http>
    
    <bean id="myHeaderWriter" class="sample.spring.security.header.MyHeaderWriter" />
    
    ...
</beans>
```

- `<header>` タグの `ref` 属性に、 `HeaderWriter` の Bean を指定する。


**Java Configuration**

```java:
package sample.spring.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import sample.spring.security.header.MyHeaderWriter;

import java.util.Collections;

@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                ...
                .headers()
                    .addHeaderWriter(new MyHeaderWriter());
    }
    
    ...
}
```

- `addHeaderWriter()` に、 `HeaderWriter` のインスタンスを設定。

**実行結果**

![spring-security.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/2b43a5eb-157a-4cb6-965a-3bbc77916547.jpeg)


# 参考
- [キャッシュについて整理してみた - Qiita](http://qiita.com/karore/items/2dc6ab8347c940ea4648)
- [X-XSS-Protection - セキュリティ](http://kaworu.jpn.org/security/X-XSS-Protection)
- [HTTP Strict Transport Security - Web セキュリティ | MDN](https://developer.mozilla.org/ja/docs/Web/Security/HTTP_Strict_Transport_Security)
- [IEのMIME Sniffing - てきとうなメモ](http://boscono.hatenablog.com/entry/20070805/p1)
- [IE8のMIME処理 - てきとうなメモ](http://boscono.hatenablog.com/entry/20110106/p1)
- [#05　ブラウザのバッドノウハウ　コンテンツ編：BK通信 ―Bad Knowhow Tsushin―｜gihyo.jp … 技術評論社](http://gihyo.jp/dev/serial/01/bk/0005)
- [X-Content-Type-Options: nosniff つかわないやつは死ねばいいのに! - 葉っぱ日記](http://d.hatena.ne.jp/hasegawayosuke/20110106/p1)
- [クリックジャッキングって？ TECHSCORE BLOG](http://www.techscore.com/blog/2015/03/05/%E3%82%AF%E3%83%AA%E3%83%83%E3%82%AF%E3%82%B8%E3%83%A3%E3%82%AE%E3%83%B3%E3%82%B0%E3%81%A3%E3%81%A6%EF%BC%9F/)
- [「クロスサイトスクリプティング（XSS）」の脆弱性の種類 - IPA (PDF)](https://www.ipa.go.jp/files/000024726.pdf)
- [X-XSS-Protection - セキュリティ](http://kaworu.jpn.org/security/X-XSS-Protection)
- [同一オリジンポリシー - Web セキュリティ | MDN](https://developer.mozilla.org/ja/docs/Web/Security/Same-origin_policy)
- [Content Security Policy (CSP) - Web セキュリティ | MDN](https://developer.mozilla.org/ja/docs/Web/Security/CSP)
- [リファラとは｜referer ： 意味/定義 - IT用語辞典](http://e-words.jp/w/%E3%83%AA%E3%83%95%E3%82%A1%E3%83%A9.html)
- [自堕落な技術者の日記 : HPKP(HTTP Public Key Pinning)公開鍵ピニングについて考える - livedoor Blog（ブログ）](http://blog.livedoor.jp/k_urushima/archives/1811745.html)