0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2024-04-28

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:\
└ Developments\
    └ Workspace\
         └ fmockup\
             ├ build\
             ├ sql\
             ├ src\
             │ └ main\
             │     ├ java\
             │     │ └ cn\
             │     │     └ com\
             │     │         └ xxxx\
             │     │             └ fmockup\
             │     │                  ├ action\
             │     │                  ├ controller\
             │     │                  ├ customizer\
             │     │                  │ ├ RequestInterceptor.java ← コレ
             │     │                  │ ├ ServletInitializer.java ← コレ
             │     │                  │ ├ TomcatConfiguration.java ← コレ
             │     │                  │ ├ TomcatFilter.java ← コレ
             │     │                  │ └ WebMvcConfiguration.java ← コレ
             │     │                  ├ entity\
             │     │                  ├ mapper\
             │     │                  ├ response\
             │     │                  ├ service\
             │     │                  ├ util\
             │     │                  ├ validator\
             │     │                  └ validator_order\
             │     └ resources\
             ├ vue-vite\
             └ WinSW.NET-nnn\

ファイルの文字コード

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

ファイルの作成

  1. Eclipse で、ファイル “RequestInterceptor.java”、“ServletInitializer.java”、“TomcatConfiguration.java”、“TomcatFilter.java”、“WebMvcConfiguration.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;
    	}
    }
    
    上記の設定により、各種レスポンス・ヘッダーを設定する。
    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;” となる。
    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 のスタック・ログを出力して終了してしまう。上記の指定により、そのスタック・ログの出力を抑止する。
    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 を実装(インプリメント)することでレスポンス・ヘッダーを設定する場合があるらしい。
    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 に “/” を指定した場合に、××××を出力して終了してしまう。上記の指定により、その××××の出力を抑止する。

その他

レスポンス・ヘッダー

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?