Spring Securityは、HTTPレスポンスにセキュリティヘッダーを自動的に付与することで、XSS攻撃、クリックジャッキング、MIMEスニッフィングなどの一般的なWeb攻撃からアプリケーションを保護します。本記事では、headers()メソッドを使用したセキュリティヘッダーの設定方法と、各ヘッダーの効果について詳しく解説します。

実行環境と前提条件

本記事の内容を実践するにあたり、以下の環境を前提としています。

項目 バージョン・要件
Java 17以上
Spring Boot 3.4.x
Spring Security 6.4.x
ビルドツール Maven または Gradle
IDE VS Code または IntelliJ IDEA

事前に以下の知識があると理解がスムーズです。

  • Spring Securityの基本的な設定方法
  • SecurityFilterChainの概念
  • HTTPヘッダーの基礎知識

Spring Securityのデフォルトセキュリティヘッダー

Spring Securityは、特別な設定をしなくても以下のセキュリティヘッダーをデフォルトで付与します。

デフォルトで付与されるヘッダー

1
2
3
4
5
6
7
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: 0

各ヘッダーの役割を確認しましょう。

ヘッダー 効果
Cache-Control, Pragma, Expires 認証済みコンテンツのキャッシュを防止
X-Content-Type-Options MIMEスニッフィング攻撃を防止
Strict-Transport-Security HTTPS通信を強制(HTTPSリクエスト時のみ付与)
X-Frame-Options クリックジャッキング攻撃を防止
X-XSS-Protection 非推奨のXSSフィルターを無効化

デフォルト設定の確認

Spring Bootプロジェクトでは、spring-boot-starter-securityを追加するだけでこれらのヘッダーが有効になります。

1
2
3
4
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

cURLコマンドでレスポンスヘッダーを確認できます。

1
curl -I http://localhost:8080/api/resource

headers()メソッドによるセキュリティヘッダーの設定

Spring Securityでは、SecurityFilterChainの設定内でheaders()メソッドを使用してセキュリティヘッダーをカスタマイズします。

基本的な設定構造

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .headers(headers -> headers
                // ヘッダー設定をここに記述
            );
        return http.build();
    }
}

設定フローの概要

flowchart TD
    A[HTTPリクエスト] --> B[SecurityFilterChain]
    B --> C[HeaderWriterFilter]
    C --> D{headers設定を適用}
    D --> E[X-Frame-Options]
    D --> F[X-Content-Type-Options]
    D --> G[Strict-Transport-Security]
    D --> H[Content-Security-Policy]
    E & F & G & H --> I[HTTPレスポンス]

X-Frame-Optionsの設定(クリックジャッキング対策)

X-Frame-Optionsヘッダーは、ページが<iframe><frame><embed><object>要素内に表示されることを制御し、クリックジャッキング攻撃を防止します。

クリックジャッキング攻撃の仕組み

sequenceDiagram
    participant User as ユーザー
    participant Attacker as 攻撃者サイト
    participant Target as 標的サイト

    User->>Attacker: 攻撃者サイトにアクセス
    Attacker->>User: 透明なiframeで標的サイトを重ねて表示
    Note over User: ユーザーには攻撃者サイトの<br/>コンテンツのみが見える
    User->>Attacker: ボタンをクリック(意図しない操作)
    Attacker->>Target: 実際には標的サイトへの<br/>リクエストが送信される

X-Frame-Optionsの設定オプション

効果
DENY すべてのフレーム内表示を拒否(デフォルト)
SAMEORIGIN 同一オリジンからのフレーム内表示のみ許可
無効化 フレーム内表示の制限なし(非推奨)

設定例:DENYの場合(デフォルト)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .anyRequest().authenticated()
            )
            .headers(headers -> headers
                .frameOptions(frameOptions -> frameOptions
                    .deny()
                )
            );
        return http.build();
    }
}

レスポンスヘッダー:

1
X-Frame-Options: DENY

設定例:SAMEORIGINの場合

同一オリジンのページからのiframe埋め込みを許可する場合は、sameOrigin()を使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .anyRequest().authenticated()
            )
            .headers(headers -> headers
                .frameOptions(frameOptions -> frameOptions
                    .sameOrigin()
                )
            );
        return http.build();
    }
}

レスポンスヘッダー:

1
X-Frame-Options: SAMEORIGIN

X-Frame-Optionsを無効化する場合

Content-Security-PolicyのFrame-ancestorsディレクティブを使用する場合など、X-Frame-Optionsを無効化したいケースがあります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .headers(headers -> headers
            .frameOptions(frameOptions -> frameOptions
                .disable()
            )
        );
    return http.build();
}

X-Content-Type-Optionsの設定(MIMEスニッフィング対策)

X-Content-Type-Optionsヘッダーは、ブラウザによるMIMEタイプの推測(スニッフィング)を防止し、Content-Typeヘッダーで指定されたタイプのみを信頼するよう指示します。

MIMEスニッフィング攻撃のリスク

MIMEスニッフィングが有効な場合、攻撃者が悪意のあるスクリプトを画像やテキストファイルに偽装してアップロードし、ブラウザがそれをJavaScriptとして実行してしまう可能性があります。

flowchart LR
    A[攻撃者] -->|悪意のあるJSを<br/>画像として偽装| B[サーバー]
    B -->|Content-Type: image/png| C[ブラウザ]
    C -->|MIMEスニッフィング| D{コンテンツを解析}
    D -->|JSとして検出| E[スクリプト実行]
    E --> F[XSS攻撃成功]

設定例

Spring Securityはデフォルトでnosniffを設定しますが、明示的に設定する場合は以下のようにします。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .headers(headers -> headers
                .contentTypeOptions(contentTypeOptions -> {})
            );
        return http.build();
    }
}

レスポンスヘッダー:

1
X-Content-Type-Options: nosniff

無効化する場合(非推奨)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .headers(headers -> headers
            .contentTypeOptions(contentTypeOptions -> contentTypeOptions
                .disable()
            )
        );
    return http.build();
}

Strict-Transport-Security(HSTS)の設定

Strict-Transport-Security(HSTS)ヘッダーは、ブラウザに対してHTTPS通信を強制し、中間者攻撃のリスクを軽減します。

HSTSが防ぐ攻撃

sequenceDiagram
    participant User as ユーザー
    participant Browser as ブラウザ
    participant Attacker as 攻撃者
    participant Server as サーバー

    Note over User,Browser: HSTSなしの場合
    User->>Browser: http://example.com
    Attacker->>Browser: HTTPリクエストを傍受<br/>改ざんしたレスポンスを返却
    Browser->>User: 攻撃者のコンテンツ表示

    Note over User,Browser: HSTSありの場合
    User->>Browser: http://example.com
    Browser->>Browser: HSTSポリシーを確認<br/>HTTPSに自動変換
    Browser->>Server: https://example.com
    Server->>Browser: 正規のレスポンス

HSTSの設定オプション

オプション 説明 デフォルト値
maxAgeInSeconds HSTSポリシーの有効期間(秒) 31536000(1年)
includeSubDomains サブドメインにも適用するか true
preload ブラウザのプリロードリストに登録する意図を示す false

設定例:カスタムHSTS設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .headers(headers -> headers
                .httpStrictTransportSecurity(hsts -> hsts
                    .maxAgeInSeconds(63072000)  // 2年
                    .includeSubDomains(true)
                    .preload(true)
                )
            );
        return http.build();
    }
}

レスポンスヘッダー(HTTPSリクエスト時のみ付与):

1
Strict-Transport-Security: max-age=63072000 ; includeSubDomains ; preload

HSTSプリロードについて

preloadオプションを有効にした場合、hstspreload.orgでサイトを登録することで、主要ブラウザのプリロードリストに追加されます。これにより、初回アクセス時からHTTPS通信が強制されます。

プリロード登録の条件:

  • 有効なSSL証明書を持つこと
  • HTTPからHTTPSへリダイレクトすること
  • すべてのサブドメインがHTTPSであること
  • max-ageが31536000秒(1年)以上であること

Content-Security-Policy(CSP)の設定

Content-Security-Policy(CSP)は、XSS攻撃やデータインジェクション攻撃を防ぐための強力なセキュリティ機構です。Spring Securityは、デフォルトではCSPを付与しません。アプリケーションごとに適切なポリシーを設定する必要があります。

CSPの主要ディレクティブ

ディレクティブ 説明
default-src 他のディレクティブで指定されていないリソースのデフォルト
script-src JavaScriptの読み込み元
style-src CSSの読み込み元
img-src 画像の読み込み元
connect-src fetch、XHR、WebSocketの接続先
frame-ancestors 当該ページをiframeで埋め込める親ページ
form-action フォームの送信先
report-uri / report-to 違反レポートの送信先

基本的なCSP設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .headers(headers -> headers
                .contentSecurityPolicy(csp -> csp
                    .policyDirectives(
                        "default-src 'self'; " +
                        "script-src 'self'; " +
                        "style-src 'self'; " +
                        "img-src 'self' data:; " +
                        "font-src 'self'; " +
                        "connect-src 'self'; " +
                        "frame-ancestors 'none'; " +
                        "form-action 'self'; " +
                        "base-uri 'self'; " +
                        "object-src 'none'"
                    )
                )
            );
        return http.build();
    }
}

レスポンスヘッダー:

1
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; form-action 'self'; base-uri 'self'; object-src 'none'

CSPレポートモードの活用

新しいCSPポリシーを本番環境に適用する前に、Content-Security-Policy-Report-Onlyヘッダーを使用してテストできます。このモードでは、違反を検出してもリソースのブロックは行わず、レポートのみを送信します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .headers(headers -> headers
            .contentSecurityPolicy(csp -> csp
                .policyDirectives(
                    "default-src 'self'; " +
                    "script-src 'self' https://trusted.example.com; " +
                    "report-uri /csp-report"
                )
                .reportOnly()
            )
        );
    return http.build();
}

レスポンスヘッダー:

1
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' https://trusted.example.com; report-uri /csp-report

CSP違反レポートの受信エンドポイント

CSP違反レポートを受け取るエンドポイントを実装します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@RestController
@RequestMapping("/csp-report")
public class CspReportController {

    private static final Logger log = LoggerFactory.getLogger(CspReportController.class);

    @PostMapping(consumes = "application/csp-report")
    public ResponseEntity<Void> handleCspReport(@RequestBody String report) {
        log.warn("CSP Violation Report: {}", report);
        return ResponseEntity.ok().build();
    }
}

CSP違反が発生すると、以下のようなJSONレポートが送信されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
  "csp-report": {
    "document-uri": "https://example.com/page",
    "referrer": "",
    "violated-directive": "script-src 'self'",
    "effective-directive": "script-src",
    "original-policy": "default-src 'self'; script-src 'self'",
    "blocked-uri": "https://evil.example.com/malicious.js",
    "status-code": 200
  }
}

Nonceを使用したインラインスクリプトの許可

インラインスクリプトを安全に許可するために、nonceを使用できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .headers(headers -> headers
                .contentSecurityPolicy(csp -> csp
                    .policyDirectives(
                        "default-src 'self'; " +
                        "script-src 'self' 'nonce-{nonce}'"
                    )
                )
            );
        return http.build();
    }
}

ThymeleafなどのテンプレートエンジンでCSP nonceを使用する場合は、リクエストごとにnonceを生成し、テンプレートに渡す必要があります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@Component
public class CspNonceFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                    HttpServletResponse response, 
                                    FilterChain filterChain) 
            throws ServletException, IOException {
        
        String nonce = UUID.randomUUID().toString().replace("-", "");
        request.setAttribute("cspNonce", nonce);
        
        response.setHeader("Content-Security-Policy", 
            "default-src 'self'; script-src 'self' 'nonce-" + nonce + "'");
        
        filterChain.doFilter(request, response);
    }
}

Referrer-Policyの設定

Referrer-Policyヘッダーは、リクエスト時にRefererヘッダーにどの程度の情報を含めるかを制御します。Spring Securityはデフォルトではこのヘッダーを付与しません。

Referrer-Policyの値

説明
no-referrer Refererヘッダーを送信しない
no-referrer-when-downgrade HTTPSからHTTPへの遷移時にRefererを送信しない
same-origin 同一オリジンの場合のみRefererを送信
origin オリジン情報のみ送信(パスは含まない)
strict-origin HTTPSの場合のみオリジン情報を送信
strict-origin-when-cross-origin 同一オリジンではフルURL、クロスオリジンではオリジンのみ

設定例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .headers(headers -> headers
            .referrerPolicy(referrer -> referrer
                .policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)
            )
        );
    return http.build();
}

レスポンスヘッダー:

1
Referrer-Policy: strict-origin-when-cross-origin

Permissions-Policyの設定

Permissions-Policy(旧Feature-Policy)は、ブラウザの機能(カメラ、マイク、位置情報など)へのアクセスを制御します。

設定例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .headers(headers -> headers
            .permissionsPolicy(permissions -> permissions
                .policy("camera=(), microphone=(), geolocation=(self), fullscreen=(self)")
            )
        );
    return http.build();
}

レスポンスヘッダー:

1
Permissions-Policy: camera=(), microphone=(), geolocation=(self), fullscreen=(self)

包括的なセキュリティヘッダー設定の実装例

実際のプロジェクトで使用できる、包括的なセキュリティヘッダー設定の例を示します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/public/**").permitAll()
                .requestMatchers("/csp-report").permitAll()
                .anyRequest().authenticated()
            )
            .headers(headers -> headers
                // デフォルト設定を明示的に無効化し、必要なものだけ設定
                .defaultsDisabled()
                
                // Cache-Control(認証済みコンテンツのキャッシュ防止)
                .cacheControl(cache -> {})
                
                // X-Content-Type-Options(MIMEスニッフィング防止)
                .contentTypeOptions(contentType -> {})
                
                // X-Frame-Options(クリックジャッキング防止)
                .frameOptions(frame -> frame.deny())
                
                // Strict-Transport-Security(HTTPS強制)
                .httpStrictTransportSecurity(hsts -> hsts
                    .maxAgeInSeconds(31536000)
                    .includeSubDomains(true)
                    .preload(true)
                )
                
                // Content-Security-Policy(XSS・インジェクション防止)
                .contentSecurityPolicy(csp -> csp
                    .policyDirectives(
                        "default-src 'self'; " +
                        "script-src 'self'; " +
                        "style-src 'self' 'unsafe-inline'; " +
                        "img-src 'self' data: https:; " +
                        "font-src 'self'; " +
                        "connect-src 'self'; " +
                        "frame-ancestors 'none'; " +
                        "form-action 'self'; " +
                        "base-uri 'self'; " +
                        "object-src 'none'; " +
                        "report-uri /csp-report"
                    )
                )
                
                // Referrer-Policy(リファラー情報の制御)
                .referrerPolicy(referrer -> referrer
                    .policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)
                )
                
                // Permissions-Policy(ブラウザ機能の制限)
                .permissionsPolicy(permissions -> permissions
                    .policy("camera=(), microphone=(), geolocation=(self)")
                )
            );
        
        return http.build();
    }
}

設定のインポート

上記の設定で必要なインポート文は以下のとおりです。

1
2
3
4
5
6
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;

静的リソースへのヘッダー設定

静的リソース(CSS、JavaScript、画像など)に対しては、キャッシュを有効にしつつセキュリティヘッダーを適用する必要があります。

静的リソース用の設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    @Order(1)
    public SecurityFilterChain staticResourcesFilterChain(HttpSecurity http) throws Exception {
        http
            .securityMatcher("/static/**", "/css/**", "/js/**", "/images/**")
            .authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
            .headers(headers -> headers
                // 静的リソースはキャッシュを有効化
                .cacheControl(cache -> cache.disable())
                .contentTypeOptions(contentType -> {})
                .frameOptions(frame -> frame.deny())
            );
        return http.build();
    }

    @Bean
    @Order(2)
    public SecurityFilterChain defaultFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            .headers(headers -> headers
                // 動的コンテンツはキャッシュを無効化
                .cacheControl(cache -> {})
                .contentTypeOptions(contentType -> {})
                .frameOptions(frame -> frame.deny())
            );
        return http.build();
    }
}

カスタムヘッダーの追加

Spring Securityが提供するヘッダー以外に、独自のセキュリティヘッダーを追加することも可能です。

StaticHeadersWriterを使用する場合

1
2
3
4
5
6
7
8
9
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .headers(headers -> headers
            .addHeaderWriter(new StaticHeadersWriter("X-Custom-Security-Header", "custom-value"))
            .addHeaderWriter(new StaticHeadersWriter("X-Powered-By", ""))
        );
    return http.build();
}

条件付きでヘッダーを追加する場合

特定のURLパターンにのみヘッダーを追加する場合は、DelegatingRequestMatcherHeaderWriterを使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    RequestMatcher adminMatcher = new AntPathRequestMatcher("/admin/**");
    
    DelegatingRequestMatcherHeaderWriter headerWriter = 
        new DelegatingRequestMatcherHeaderWriter(
            adminMatcher,
            new StaticHeadersWriter("X-Admin-Area", "true")
        );
    
    http
        .headers(headers -> headers
            .addHeaderWriter(headerWriter)
        );
    return http.build();
}

セキュリティヘッダーのテスト

設定したセキュリティヘッダーが正しく付与されているかをテストする方法を紹介します。

MockMvcを使用したテスト

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@SpringBootTest
@AutoConfigureMockMvc
class SecurityHeadersTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    @WithMockUser
    void shouldIncludeSecurityHeaders() throws Exception {
        mockMvc.perform(get("/api/resource"))
            .andExpect(status().isOk())
            .andExpect(header().string("X-Content-Type-Options", "nosniff"))
            .andExpect(header().string("X-Frame-Options", "DENY"))
            .andExpect(header().exists("Content-Security-Policy"))
            .andExpect(header().string("Referrer-Policy", "strict-origin-when-cross-origin"));
    }

    @Test
    @WithMockUser
    void shouldIncludeHstsHeaderOnHttps() throws Exception {
        mockMvc.perform(get("/api/resource")
                .secure(true))
            .andExpect(status().isOk())
            .andExpect(header().exists("Strict-Transport-Security"));
    }

    @Test
    void shouldIncludeCspHeader() throws Exception {
        mockMvc.perform(get("/api/public/resource"))
            .andExpect(header().string("Content-Security-Policy", 
                containsString("default-src 'self'")));
    }
}

ブラウザ開発者ツールでの確認

ブラウザの開発者ツール(F12)のNetworkタブで、レスポンスヘッダーを確認できます。正しく設定されている場合、Response Headersセクションにセキュリティヘッダーが表示されます。

外部ツールを使用した検証

以下のオンラインツールでセキュリティヘッダーの設定を検証できます。

トラブルシューティング

よくある問題と解決策

問題 原因 解決策
CSPでインラインスクリプトがブロックされる script-src 'self'のみの設定 nonceまたはハッシュを使用するか、'unsafe-inline'を追加(非推奨)
外部CDNのリソースが読み込めない CSPで許可されていない 該当するディレクティブにCDNのドメインを追加
iframeが表示されない X-Frame-Options: DENY sameOrigin()に変更するか、CSPのframe-ancestorsを設定
HSTSヘッダーが付与されない HTTPでアクセスしている HTTPSでアクセスする

デバッグ方法

セキュリティヘッダーの問題をデバッグするには、Spring Securityのログレベルを上げます。

1
2
3
4
logging:
  level:
    org.springframework.security: DEBUG
    org.springframework.security.web.header: TRACE

まとめ

Spring Securityのセキュリティヘッダー設定について解説しました。重要なポイントをまとめます。

  • Spring Securityはデフォルトで基本的なセキュリティヘッダーを付与する
  • headers()メソッドを使用してヘッダーをカスタマイズできる
  • X-Frame-Optionsはクリックジャッキング攻撃を防止する
  • X-Content-Type-OptionsはMIMEスニッフィング攻撃を防止する
  • HSTSはHTTPS通信を強制し、中間者攻撃を防止する
  • CSPはXSS攻撃やデータインジェクション攻撃に対する強力な防御機構
  • CSPレポートモードを活用して段階的にポリシーを導入できる
  • テストを作成してセキュリティヘッダーの設定を検証する

これらのセキュリティヘッダーを適切に設定することで、Webアプリケーションの防御力を大幅に向上させることができます。

参考リンク