Web 開発再入門 #18 ― セキュリティ対策（レスポンス・ヘッダー処理）

# Web 開発再入門 #18 ― セキュリティ設定（レスポンス・ヘッダー処理）

fmockup

## ★ 本ページは工事中 ★

## はじめに

Web アプリケーションのサーバー・サイドを開発します。

レスポンス・ヘッダーに著名なパラメーターを設定しておきます。SpringBoot Security で設定するものを参考にしました。
これらにより、ハッカーがブラウザーの挙動を悪用するのを防止します。

また、今回の Web アプリケーションは、SPA として HTML を返却しますので、ブラウザーのキャッシュを無効にしておきます。これにより、ブラウザーは、キャッシュを使用せずに、毎回、Web サーバーから最新の HTML を取得するようになるため、開発段階で頻繫に Vue の HTML を更新する状況において便利です。

SPA：Single Page Application
HTML：Hypr Text Markup Language

## レスポンスヘッダーに設定するもの

1. 今回、Tomcat にて設定するもの。
    ```:レスポンス・ヘッダー
    Cache-Control: private, no-store, no-cache, max-age=0, must-revalidate
    Expires: 0
    Set-Cookie: ...; Secure; HttpOnly;
    Strict-Transport-Security: max-age=31536000; includeSubDomains
    Pragma: no-cache
    X-Content-Type-Options: nosniff
    X-Frame-Options: SAMEORIGIN
    X-XSS-Protection: 1; mode=block
    ```
2. application.properties にて設定するもの。ThymeLeaf で生成するページ用。
    ```:レスポンス・ヘッダー
    Content-Type: text/html; charset=utf-8
    ```

## フォルダー・ファイル構成

D:\\
└ Development\\
&nbsp;&nbsp;&nbsp;&nbsp;└ Workspace\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;└ fmockup\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ build\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ sql\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ src\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;└ main\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ java\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;└ cn\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;└ com\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;└ xxxx\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;└ fmockup\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ action\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ controller\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ customizer\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;├ RequestInterceptor.java ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;├ ServletInitializer.java ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;├ TomcatConfiguration.java ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;├ TomcatFilter.java ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;└ WebMvcConfiguration.java ← コレ
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ entity\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ mapper\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ response\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ service\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ util\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ validator\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;└ validator_order\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;└ resources\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├ vue-vite\\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;└ WinSW.NET-nnn\\

## ファイルの文字コード

基本的に Eclipse でファイルを作成するので、あまり意識したことがありません。
多分、Unix 改行（LF）なのだと思います。

## ファイルの作成

1. Eclipse で、ファイル “RequestInterceptor.java”、“ServletInitializer.java”、“TomcatConfiguration.java”、“TomcatFilter.java”、“WebMvcConfiguration.java” を作成する。
    ```java:RequestInterceptor.java
    ・・・
    
    package cn.com.xxxx.fmockup.customizer;
    
    import org.springframework.lang.Nullable;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    /**
     * Request Interceptor
     */
    
    @Component
    public class RequestInterceptor implements HandlerInterceptor {
    
    	@Override
    	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    		return true;
    	}
    
    	@Override
    	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    
    		// Browser should control cache.
    		response.addHeader("Cache-Control", "private, no-store, no-cache, max-age=0, must-revalidate");
    		// Browser cache time is zero.
    		response.addHeader("Expires", "");
    		// Browser should inspect cache. (For old browser)
    		// Browser should keep https conneciton.
    		response.addHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
    		response.addHeader("Pragma", "no-cache");
    		// Browser should check content-type (should not check contents characters), prevent XSS.
    		response.addHeader("X-Content-Type-Options", "nosniff");
    		// Browser should prevent Click-Jacking. <iframe> only can use in same-origin (Ex. https://xxxx_IT_Technologies.com.cn:8080).
    		response.addHeader("X-Frame-Options", "SAMEORIGIN");
    		// Browser should prevent XSS after found XSS.
    		response.addHeader("X-XSS-Protection", "1; mode=block");
    
    		// Response Header (Spring Security Does Not Sets Value)
    		// response.addHeader("Content-Security-Policy", "default-src 'self'"); <- Please Do Not Use It.
    
    		return;
    	}
    
    	@Override
    	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    		return;
    	}
    }
    ```
    上記の設定により、各種レスポンス・ヘッダーを設定する。
    ```java:ServletInitializer.java
    ・・・
    
    package cn.com.xxxx.fmockup.customizer;
    
    import java.util.Collections;
    
    import org.springframework.boot.web.servlet.ServletContextInitializer;
    import org.springframework.context.annotation.Configuration;
    
    import jakarta.servlet.SessionTrackingMode;
    
    /**
     * Servlet Initializer
     */
    @Configuration
    public class ServletInitializer {
    
    	public ServletContextInitializer servletContextInitializer() {
    
    		ServletContextInitializer initializer = servletContext -> {
    
    			// Cookie (HttpOnly) default is true
    //			servletContext.getSessionCookieConfig().setHttpOnly(true);
    //			servletContext.getSessionCookieConfig().setHttpOnly(false);
    
    			// Cookie (Secure) default is true
    //			servletContext.getSessionCookieConfig().setSecure(true);
    //			servletContext.getSessionCookieConfig().setSecure(false);
    
    			//
    			// Remove the JSESSIONID from URL
    			//
    			// <link href="./bootstrap/css/bootstrap.min.css;jsessionid=1E304E1B77F604CA856F7A4E7333FF5E" type="text/css" rel="stylesheet" />
    			//                                               -------------------------------------------
    			//                                               This process can remove this jsessoinid
    			//
    			servletContext.setSessionTrackingModes(Collections.singleton(SessionTrackingMode.COOKIE));
    		};
    
    		return initializer;
    	}
    }
    ```
    説明：
    デフォルトで “Set-Cookie: ...; Secure; HttpOnly;” となる。
    ```java:TomcatConfiguration.java
    ・・・
    
    /**
     * Error Resolver
     *   It Sets Relaxed URL Query Characters Attribute.
     */
    package cn.com.xxxx.fmockup.customizer;

    import org.apache.catalina.connector.Connector;
    import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;

    @Configuration
    public class TomcatConfiguration {

    /*
     * If a URL is specified as a special character, the Spring Boot does not become an error.
     * 
     * If this is not specified. Spring boot outputs a Java error stack log.
     * It gives a hint to attackers, so it is not secured...
     */

    	@Bean
    	public TomcatServletWebServerFactory webServerFactory() {
    		TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
    		factory.addConnectorCustomizers((Connector connector) -> {
    			connector.setProperty("relaxedPathChars", "\"<>[\\]^`{|}");
    			connector.setProperty("relaxedQueryChars", "\"<>[\\]^`{|}");
    		});
    		return factory;
    	}
    }
    ```
    説明：
    URL に特殊文字を指定した場合に、Java のスタック・ログを出力して終了してしまう。上記の指定により、そのスタック・ログの出力を抑止する。
    ```java:TomcatFilter.java
    ・・・
    
    package cn.com.xxxx.fmockup.customizer;
    
    import java.io.IOException;
    
    import org.springframework.stereotype.Component;
    
    import jakarta.servlet.Filter;
    import jakarta.servlet.FilterChain;
    import jakarta.servlet.FilterConfig;
    import jakarta.servlet.ServletException;
    import jakarta.servlet.ServletRequest;
    import jakarta.servlet.ServletResponse;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    /*
     * Tomcat Filter
     */
    
    @Component
    public class TomcatFilter implements Filter {
    
    	public void init(FilterConfig filterConfig) throws ServletException {
    		return;
    	}
    
    	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
    		HttpServletRequest request = (HttpServletRequest) servletRequest;
    		HttpServletResponse response = (HttpServletResponse) servletResponse;
    
    		String hostFqdn = request.getHeader("host");
    		if(! hostFqdn.equals(cn.com.xxxx.fmockup.util.PropertyUtil.getHostFqdn())) {
    			response.setStatus(403);
    		}
    
    /*
    		// Browser should control cache.
    		response.addHeader("Cache-Control", "private, no-store, no-cache, max-age=0, must-revalidate");
    		// Browser cache time is zero.
    		response.addHeader("Expires", "");
    		// Browser should inspect cache. (For old browser)
    		// Browser should keep https conneciton.
    		response.addHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
    		response.addHeader("Pragma", "no-cache");
    		// Browser should check content-type (should not check contents characters), prevent XSS.
    		response.addHeader("X-Content-Type-Options", "nosniff");
    		// Browser should prevent Click-Jacking. <iframe> only can use in same-origin (Ex. https://xxxx_IT_Technologies.com.cn:8080).
    		response.addHeader("X-Frame-Options", "SAMEORIGIN");
    		// Browser should prevent XSS after found XSS.
    		response.addHeader("X-XSS-Protection", "1; mode=block");
    
    		// Response Header (Spring Security Does Not Sets Value)
    		// response.addHeader("Content-Security-Policy", "default-src 'self'"); <- Please Do Not Use It.
    */
    
    		filterChain.doFilter(request, response);
    		return;
    	}
    
    	@Override
    	public void destroy() {
    		return;
    	}
    }
    ```
    説明：
    HandlerInterceptor を実装（インプリメント）することでレスポンス・ヘッダーを設定する場合と、上記のように Filter を実装（インプリメント）することでレスポンス・ヘッダーを設定する場合があるらしい。
    ```java:WebMvcConfiguration.java
    ・・・
    
    package cn.com.xxxx.fmockup.customizer;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    /**
     * MVC Configuration
     *   It sets screen name. "/**" means catch all request.
     *
     */
    
    @Configuration
    public class WebMvcConfiguration implements WebMvcConfigurer {
    
    	@Autowired
    	private RequestInterceptor handlerInterceptor;
    
    	@Override
    	public void addInterceptors(InterceptorRegistry registry) {
    
    		registry.addInterceptor(handlerInterceptor).addPathPatterns("/**");
    		return;
    	}
    }
    ```
    説明：
    URL に “/” を指定した場合に、××××を出力して終了してしまう。上記の指定により、その××××の出力を抑止する。

## その他

**レスポンス・ヘッダー**

https://spring.pleiades.io/spring-security/reference/features/exploits/headers.html

https://qiita.com/opengl-8080/items/4e800eb6990653b0395c

https://qiita.com/syukai/items/92d7dfc22b6ac34f9b87