#Cookieのセキュリティ対策とは
SpringでWebシステムを作っていてCookieのセキュリティ対策ということで以下の3点をしたかったのです。
- Secureモードにする
- httponlyにする
- セッションIDをクエリパラメータ(URLの一部)にしない
こちらのQiita記事にまとめてくださっていましたので結論だけ引用させて頂いて、詳しくはリンク先を、ということで。
SpringBootでCookie設定をする
@Bean
public ServletContextInitializer servletContextInitializer(@Value("${secure.cookie}")boolean secure) {
ServletContextInitializer servletContextInitializer = new ServletContextInitializer() {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.getSessionCookieConfig().setHttpOnly(true);
servletContext.getSessionCookieConfig().setSecure(secure);
servletContext.setSessionTrackingModes(
Collections.singleton(SessionTrackingMode.COOKIE)
);
}
};
return servletContextInitializer;
}
これで完了!なのですが、どうもうまくいかない・・・と思ったら、SpringSessionを使っている場合はちょっと変わります。
SpringSessionを使う場合はCookieの設定はCookieSerializerに書く
こんなドキュメントにたどり着きました。
Spring Session - Custom Cookie
http://docs.spring.io/spring-session/docs/current/reference/html5/guides/custom-cookie.html
Spring Session - Custom Cookie(2018/3/20 リンク修正)
セッション情報はSpringSessionに任せてるんだからCookieの設定もSpringSessionへ・・・
というわけで、私の場合はRedisを使うためにSessionConfigというクラスを作っていたのでこんな感じに。
@EnableRedisHttpSession
@Configuration
public class SessionConfig {
@Bean
public CookieSerializer cookieSerializer(){
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
// HTTPSで動作している場合はHTTPSでないとCookieを返さないようにする
serializer.setUseSecureCookie(true);
// CookieへのアクセスはHTTPプロトコルに限定(SSHとかはNG)
serializer.setUseHttpOnlyCookie(true);
return serializer;
}
}
(@EnableRedisHttpSessionのあるクラスじゃないとダメ?⇒試しましたが別のクラスでも大丈夫。)
これでHTTPSでないとCookieを返さないセキュアモード、HTTPプロトコル以外はCookieにアクセスさせないHttpOnlyの設定ができました!
ローカルデバッグ用の設定を追加
めでたしめでたしなのですが、HTTPSでないとCookie返さないということは、ローカルデデバッグできなくなってしまうわけで。
これでは面倒なので判定を入れておきましょう。
環境変数やSpringProfileで判定してももちろんいいのですが、今回はapplication.ymlに設定を追加することにしました。
syukai.security:
useHttps: true
@Configuration
@EnableWebSecurity
@ConfigurationProperties("syukai.security")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private Boolean useHttps;
public Boolean getUseHttps(){return this.useHttps;}
public void setUseHttps(Boolean usehttps){this.useHttps=usehttps;}
//※もちろんこれをSessionConfigに書いてもいいのですが、セキュリティ関係の設定ということでこちらにしました。
}
@EnableRedisHttpSession
@Configuration
public class SessionConfig {
@Autowired
SecurityConfig securityConfig;
@Bean
public CookieSerializer cookieSerializer(){
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
// HTTPSで動作している場合はHTTPSでないとCookieを返さないようにする
if(securityConfig.getUseHttps()){
serializer.setUseSecureCookie(true);
}
// CookieへのアクセスはHTTPプロトコルに限定(SSHとかはNG)
serializer.setUseHttpOnlyCookie(true);
return serializer;
}
}
##Cookie名を変更する
デフォルトのCookie名だとよろしくない・・・というセキュリティ対策というよりは、実はIE・Edge対策でCookie名を変更しました。
というのも、IE、EdgeではルートドメインのCookie名とサブドメインのCookie名を混同してくださるそうでして、詳しくは以下のリンク先記事をご参照ください。(ルートドメインに置くなよ、というのはさておき)
WEBマスターの知恵ブログ - 【ie】サブドメインとセッション・クッキーの不具合【PHP】
記事ではie9のときのことが書かれていましたが、IE11、Edgeでも同様でした。
ですので、こんな感じでルートドメインとサブドメインでCookie名を分けておきます。
if(isRootDomain()){
serializer.setCookieName("ROOTSESSION");
}else{
serializer.setCookieName("SUBSESSION");
}
isRootDomainの中身は先ほどと同じく環境変数なりSpringProfileなりapplication.ymlの値なりで判定します。
以上!