WebViewでCookieを扱うとき、「とりあえずsetCookieで動いたからOK」で済ませていませんか?実はCookieにはMax-AgeやSecure、HttpOnly、SameSiteといった属性がいろいろあって、それぞれに大事な意味があります。
この記事では、Cookieの各属性が何を意味しているのかをわかりやすく解説しつつ、WebViewでのCookieの追加・削除・確認の方法をサンプルコード付きで紹介していきます。
※ CookieManagerの基本的な使い方はこちらの記事でも紹介しているので、合わせてどうぞ。
前提条件
- Android Studio(最新安定版推奨)
- minSdk 21 以上
- Kotlin
Cookieの基本構造
まず、Cookieがどんな形をしているのか見てみましょう。ブラウザやWebViewに保存されるCookieは、こんな文字列になっています。
session_id=abc123; Max-Age=3600; Path=/; Secure; HttpOnly; SameSite=Lax
セミコロン区切りで、最初のsession_id=abc123がCookieの名前と値、それ以降が属性です。これらの属性を理解しておくと、「なぜかCookieが送信されない」「いつの間にかCookieが消えてる」といった問題のデバッグがぐっと楽になります。
Cookie属性の意味を理解する
ここからが本題です。Cookieに付けられる属性を一つずつ見ていきましょう。
Max-Age と Expires — いつまで保持するか
Cookieの有効期限を決める属性です。
Max-Age=3600— 設定から3600秒(1時間)で期限切れExpires=Thu, 01 Jan 2026 00:00:00 GMT— 指定した日時に期限切れ
どちらも指定しなかった場合はセッションCookieになり、WebViewが閉じられるとCookieは消えます。ログインセッションなど「ブラウザを閉じたら消えてほしい」ケースではあえて指定しないこともあります。
両方指定された場合はMax-Ageが優先されます。今はMax-Ageを使うのが一般的ですね。
// Max-Ageを指定してCookieをセットする例(1時間有効)
val cookieManager = CookieManager.getInstance()
cookieManager.setCookie(
"https://example.com",
"session_id=abc123; Max-Age=3600; Path=/"
)
Path — どのパスに送信するか
Cookieを送信するURLのパスを制限する属性です。
Path=/— サイト全体のどのページにもCookieが送信されるPath=/api—/api以下のURLにだけCookieが送信される
特に理由がなければPath=/を使っておけばOKです。
Domain — どのドメインに送信するか
Cookieを送信する対象のドメインを指定します。
Domain=example.com—example.comと、そのサブドメイン(api.example.comなど)にもCookieが送信される- 指定なし — Cookieを発行したドメインにだけ送信される(サブドメインには送信されない)
セキュリティ的には、必要以上に広いドメインを指定しないのがベターです。
Secure — HTTPSだけに限定する
Secure属性が付いたCookieは、HTTPS通信のときだけ送信されます。HTTP(暗号化なし)では送信されません。
// Secure属性付きのCookie
cookieManager.setCookie(
"https://example.com",
"token=xyz789; Secure; Path=/"
)
ログイン情報やトークンを含むCookieには必ず付けましょう。付けないと、中間者攻撃でCookieが盗まれるリスクがあります。
HttpOnly — JavaScriptからアクセスさせない
HttpOnly属性が付いたCookieは、JavaScriptのdocument.cookieからは読み取れなくなります。HTTP通信(リクエストヘッダー)にだけ含まれます。
XSS(クロスサイトスクリプティング)攻撃でCookieが盗まれるのを防ぐための属性です。セッション情報を含むCookieには基本的に付けておくべきです。
ただし注意点として、WebViewのCookieManager.getCookie()ではHttpOnlyのCookieも取得できます。ブラウザのJavaScriptからは見えなくても、Androidのネイティブコードからはアクセスできるということですね。
SameSite — クロスサイトリクエストの制御
SameSiteはCSRF(クロスサイトリクエストフォージェリ)攻撃を防ぐための属性で、3つの値があります。
SameSite=Strict— 同一サイトからのリクエストにだけCookieを送信する。一番厳しいSameSite=Lax— 基本は同一サイトのみだが、外部サイトからのGETリクエスト(リンクのクリックなど)ではCookieを送信する。デフォルト値SameSite=None— クロスサイトでもCookieを送信する。ただしSecure属性が必須
// SameSite=None を設定する場合(Secureも必須)
cookieManager.setCookie(
"https://example.com",
"tracking=abc; SameSite=None; Secure; Path=/"
)
WebViewでサードパーティのサイトを表示する場合、SameSite=Lax(デフォルト)だとCookieが送信されないケースがあります。「ログイン状態が維持されない」という問題に遭遇したら、この属性を確認してみてください。
WebViewでのCookie操作
属性がわかったところで、WebViewでCookieを実際に操作する方法を見ていきましょう。すべてCookieManagerクラスを使います。
Cookieの追加(セット)
基本はsetCookie(url, cookieString)です。属性も含めた文字列をそのまま渡します。
val cookieManager = CookieManager.getInstance()
// Cookieの受け入れを有効にする(忘れがち!)
cookieManager.setAcceptCookie(true)
// シンプルなセッションCookie
cookieManager.setCookie(
"https://example.com",
"user_name=taro"
)
// 属性付きのCookie
cookieManager.setCookie(
"https://example.com",
"session_id=abc123; Max-Age=86400; Path=/; Secure; HttpOnly; SameSite=Lax"
)
// 変更をディスクに永続化する
cookieManager.flush()
flush()は忘れがちですが重要です。これを呼ばないと、アプリが強制終了した場合にCookieが保存されないことがあります。
Cookieの取得(確認)
getCookie(url)で、指定したURLに紐づくCookieを取得できます。
val cookies = cookieManager.getCookie("https://example.com")
// 結果例: "user_name=taro; session_id=abc123"
戻り値は名前=値がセミコロン区切りで並んだ文字列です。属性情報は含まれません。Cookieがない場合はnullが返ります。
特定のCookieだけ取り出したい場合は、文字列をパースする必要があります。拡張関数を用意しておくと便利です。
/**
* CookieManagerから特定の名前のCookieの値を取得する
*/
fun CookieManager.getCookieValue(url: String, name: String): String? {
val cookies = getCookie(url) ?: return null
return cookies.split(";")
.map { it.trim() }
.firstOrNull { it.startsWith("$name=") }
?.substringAfter("=")
}
// 使い方
val sessionId = cookieManager.getCookieValue(
"https://example.com",
"session_id"
)
// 結果: "abc123"
特定のCookieの削除
AndroidのCookieManagerには「このCookieを削除して」というメソッドがありません。ちょっとトリッキーですが、同じ名前のCookieを有効期限切れで上書きすることで削除します。
/**
* 指定した名前のCookieを削除する
*/
fun CookieManager.removeCookie(url: String, name: String) {
// Max-Age=0 で即期限切れにする
setCookie(url, "$name=; Max-Age=0; Path=/")
flush()
}
// 使い方
cookieManager.removeCookie("https://example.com", "session_id")
Max-Age=0を指定すると、Cookieは即座に期限切れになって削除されます。Pathは元のCookieと同じものを指定しないと削除されないので注意してください。
すべてのCookieの削除
全Cookieを一括削除したい場合は、専用のメソッドがあります。
// すべてのCookieを削除
cookieManager.removeAllCookies { success ->
// success: 削除が実行されたかどうか
if (success) {
Log.d("Cookie", "すべてのCookieを削除しました")
}
}
cookieManager.flush()
コールバックが非同期で返ってくる点に注意してください。ログアウト処理などで使う場合は、コールバックを待ってから次の処理に進むのが安全です。
セッションCookieだけ削除
有効期限が設定されていない(セッションCookieの)ものだけ削除したい場合は、removeSessionCookiesを使います。
// セッションCookieだけ削除(有効期限付きのCookieは残る)
cookieManager.removeSessionCookies { success ->
Log.d("Cookie", "セッションCookie削除: $success")
}
cookieManager.flush()
実践:ログインセッション管理の例
ここまでの内容を使って、よくあるログインセッション管理のパターンをまとめてみましょう。
class WebViewActivity : AppCompatActivity() {
private lateinit var webView: WebView
private val cookieManager = CookieManager.getInstance()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_webview)
webView = findViewById(R.id.webView)
setupCookies()
setupWebView()
webView.loadUrl("https://example.com/login")
}
private fun setupCookies() {
// Cookieを有効にする
cookieManager.setAcceptCookie(true)
// サードパーティCookieも許可する場合
cookieManager.setAcceptThirdPartyCookies(webView, true)
}
private fun setupWebView() {
webView.settings.javaScriptEnabled = true
webView.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
// ページ読み込み完了後にCookieを確認
url?.let { checkLoginStatus(it) }
}
}
}
private fun checkLoginStatus(url: String) {
val sessionId = cookieManager.getCookieValue(url, "session_id")
if (sessionId != null) {
Log.d("Cookie", "ログイン済み: session_id=$sessionId")
} else {
Log.d("Cookie", "未ログイン")
}
}
fun logout() {
// セッションCookieを削除
cookieManager.removeCookie(
"https://example.com",
"session_id"
)
// ログインページにリダイレクト
webView.loadUrl("https://example.com/login")
}
override fun onDestroy() {
// Cookieの変更をディスクに保存
cookieManager.flush()
webView.destroy()
super.onDestroy()
}
}
便利な拡張関数まとめ
この記事で紹介した拡張関数をまとめたユーティリティファイルです。プロジェクトにコピーして使ってください。
// CookieExtensions.kt
import android.webkit.CookieManager
/**
* 指定URLの特定のCookieの値を取得する
*/
fun CookieManager.getCookieValue(
url: String,
name: String
): String? {
val cookies = getCookie(url) ?: return null
return cookies.split(";")
.map { it.trim() }
.firstOrNull { it.startsWith("$name=") }
?.substringAfter("=")
}
/**
* 指定URLの特定のCookieを削除する
*/
fun CookieManager.removeCookie(
url: String,
name: String,
path: String = "/"
) {
setCookie(url, "$name=; Max-Age=0; Path=$path")
flush()
}
/**
* 指定URLのCookieをMap形式で取得する
*/
fun CookieManager.getCookieMap(
url: String
): Map<String, String> {
val cookies = getCookie(url) ?: return emptyMap()
return cookies.split(";")
.map { it.trim() }
.filter { it.contains("=") }
.associate { cookie ->
val (key, value) = cookie.split("=", limit = 2)
key.trim() to value.trim()
}
}
/**
* 属性付きでCookieをセットするビルダー
*/
fun CookieManager.setCookieWithAttributes(
url: String,
name: String,
value: String,
maxAge: Int? = null,
path: String = "/",
secure: Boolean = false,
httpOnly: Boolean = false,
sameSite: String? = null // "Strict", "Lax", "None"
) {
val builder = StringBuilder("$name=$value; Path=$path")
maxAge?.let { builder.append("; Max-Age=$it") }
if (secure) builder.append("; Secure")
if (httpOnly) builder.append("; HttpOnly")
sameSite?.let { builder.append("; SameSite=$it") }
setCookie(url, builder.toString())
flush()
}
特にsetCookieWithAttributesは、属性の文字列を手動で組み立てるミスを防げるので便利です。
// 使い方
cookieManager.setCookieWithAttributes(
url = "https://example.com",
name = "session_id",
value = "abc123",
maxAge = 86400, // 1日
secure = true,
httpOnly = true,
sameSite = "Lax"
)
よくあるトラブルと対処法
Cookieがセットされない
setAcceptCookie(true)を呼び忘れていませんか?デフォルトでtrueですが、明示的に呼んでおくと安心です。また、setCookieのURLにはスキーム(https://)を含める必要があります。
サードパーティCookieが送信されない
Android 5.0以降、サードパーティCookieはデフォルトで無効です。setAcceptThirdPartyCookies(webView, true)で有効にする必要があります。
cookieManager.setAcceptThirdPartyCookies(webView, true)
アプリ再起動でCookieが消える
flush()を呼んでいない可能性があります。Cookieの変更後は必ずflush()でディスクに書き込みましょう。
getCookieでnullが返る
URLの形式が間違っている可能性があります。setCookieで使ったURLと同じ形式でgetCookieを呼んでいるか確認してみてください。特にスキーム(http/https)やパスの違いに注意です。
まとめ
Cookieの属性とWebViewでの操作方法をまとめると、こんな感じです。
Max-Age/Expires— Cookieの有効期限。指定なしだとセッションCookieになるSecure— HTTPSのときだけ送信。トークン系には必須HttpOnly— JavaScriptからアクセス不可にする。XSS対策SameSite— クロスサイトリクエストの制御。デフォルトはLaxPath/Domain— 送信先の制限
WebViewでの操作はCookieManagerに集約されていて、追加はsetCookie、取得はgetCookie、削除はMax-Age=0で上書き、というパターンを覚えておけば大丈夫です。
この記事で紹介した拡張関数を使えば、Cookie操作がかなり楽になるはずです。ぜひプロジェクトに取り入れてみてくださいね。


コメント