ServletでSameSite Cookieを設定する

3 min read読了の目安(約3400字

概要

2021年4月現在、Servletの仕様範囲内ではCookieのSameSite属性を設定できません。
そのため各アプリケーションサーバー(サーブレットコンテナー)が用意している独自の方法に頼る必要があります。

この記事ではTomcat 9、WildFly 23.0.0.Final、Jetty 9を使ってそれぞれどのようにしてSameSite属性を設定するのか見てみます。

事前準備として次のリポジトリをcloneしておいてください。

サーブレットが1つ入ったWebアプリケーションとなっています。
サーブレットではセッションの取得を行なってセッションID入りのCookieが発行されるようにしています。
また、名前がdemoで値がfoobarのCookieを明示的に作っています。
つまり、2つのCookieが発行されるようになっています。

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {

    // Cookieを返すためにセッションを作る
    req.getSession();

    // 明示的なCookieの追加もしておく
    Cookie cookie = new Cookie("demo", "foobar");
    resp.addCookie(cookie);

    resp.setContentType("text/plain");
    try (PrintWriter out = resp.getWriter()) {
        out.print("Hello, world!");
        out.flush();
    }
}

Tomcat

TomcatはCookieを読み書きするorg.apache.tomcat.util.http.CookieProcessorというインターフェースを持っています。
これの実装クラスであるorg.apache.tomcat.util.http.Rfc6265CookieProcessorを設定することでSameSite属性を書き出せます。

次のような内容でWARファイル内にMETA-INF/context.xmlを作ります(つまりMavenでのビルド前はsrc/main/webapp/META-INF/context.xmlです)。

<?xml version="1.0" encoding="UTF-8"?>
<Context>
  <CookieProcessor
      className="org.apache.tomcat.util.http.Rfc6265CookieProcessor"
      sameSiteCookies="none"/>
</Context>

mvn packageでWARファイルを作って、次のコマンドでTomcatを実行してWARファイルをデプロイします。

docker run --rm -p 8080:8080 \
       -v $PWD/target/demo.war:/usr/local/tomcat/webapps/demo.war \
       tomcat:9

curlで動作確認します。

curl -vL localhost:8080/demo

レスポンスからSet-Cookieヘッダを抜粋します。

< Set-Cookie: JSESSIONID=310DBCB36C68017E053650569D93D431; Path=/demo; HttpOnly; SameSite=None
< Set-Cookie: demo=foobar; SameSite=None

SameSite属性が設定されていますね。

WildFly

WildFlyはバージョン19からSameSiteのサポートが入ったようです。

次の内容でWEB-INF/undertow-handlers.confを作ります。

samesite-cookie(mode=none)

Tomcatの場合と同じく、mvn packageでWARファイルを作って動作確認です。

docker run --rm -p 8080:8080 \
       -v $PWD/target/demo.war:/opt/jboss/wildfly/standalone/deployments/demo.war \
       jboss/wildfly:23.0.0.Final

Tomcatの場合と同じく、curlを実行してSet-Cookieレスポンスヘッダを確認します。

< Set-Cookie: JSESSIONID=4WORL56grEURqybqfuSqRSnwkRxnzUCt516a1iGm.7b70a942134b; path=/demo; secure; SameSite=None
< Set-Cookie: demo=foobar; secure; SameSite=None

SameSite属性が設定されていますね。

Jetty

最後はJettyです。

web.xmlcomment要素(cookie-config要素の子要素)に特定のコメントを設定する、という不思議な方法で実現しています(正直、あまり好みの方法ではありません)。

<session-config>
  <cookie-config>
    <comment>__SAME_SITE_NONE__</comment>
  </cookie-config>
</session-config>

mvn packageでWARファイルを作って動作確認です。

docker run --rm -p 8080:8080 \
       -v $PWD/target/demo.war:/var/lib/jetty/webapps/demo.war \
       jetty:9

curlを実行して得たSet-Cookieレスポンスヘッダが次の通りです。

< Set-Cookie: JSESSIONID=node07t0uojz6ahpu17u2eknzti9dl0.node0; Path=/demo; SameSite=None
< Set-Cookie: demo=foobar

このようにセッションIDだけにSameSiteが付いています。

明示的に作るCookieにはコードでコメントを設定する必要がありそうです。

cookie.setComment("__SAME_SITE_NONE__");

これでもう一度動作確認したところ、demo=foobarにもSameSite属性が付きました。

< Set-Cookie: JSESSIONID=node02lu7plc1tgx51vwiwqigcr75n0.node0; Path=/demo; SameSite=None
< Set-Cookie: demo=foobar; SameSite=None

以上です。