Android WebView file:// 带有 SameSite cookie 的 url

发布于 2025-01-16 16:30:31 字数 4002 浏览 3 评论 0原文

我在将应用程序升级到 Android API 级别 31 (Android 12) 时遇到 cookie 问题。我的 file:// URL 无法访问远程 cookie,即使我将它们设置为 SameSite=None 也是如此。

API 级别 31 中记录的主要更改之一是 更改Cookie 行为

WebView 中的现代 SameSite cookies

Android 的 WebView 组件基于 Chromium,后者是为 Google Chrome 浏览器提供支持的开源项目。 Chromium 对第三方 cookie 的处理进行了更改,以提供更高的安全性和隐私性,并为用户提供更多的透明度和控制力。从 Android 12 开始,当应用面向 Android 12(API 级别 31)或更高版本时,这些更改也会包含在 WebView 中。

cookie 的 SameSite 属性控制它是否可以与任何请求一起发送,或者仅与同一站点请求一起发送。以下隐私保护更改改进了第三方 cookie 的默认处理,并有助于防止意外的跨站点共享:

  • 没有 SameSite 属性的 Cookie 被视为 SameSite=Lax。
  • SameSite=None 的 Cookie 还必须指定安全属性,这意味着它们需要安全上下文并且应通过 HTTPS 发送。
  • 站点的 HTTP 和 HTTPS 版本之间的链接现在被视为跨站点请求,因此不会发送 Cookie,除非它们被适当标记为 SameSite=None;安全。

我的应用程序在 assets 文件夹中包含嵌入的 HTML 文件,我通过 file:///android_asset/myfile.html 等 URL 使用 WebView 显示这些文件。

在 API 级别 31 之前,WebView 能够与我的远程服务器通信、接收 cookie 并将这些 cookie 作为响应发回,但是当我定位 API 级别 31 时,WebView 拒绝重新传输我的服务器发送的 cookie,即使我设置了 SameSite=None

这是一个重现该问题的简单 PHP 文件示例。

<?php
header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']);
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400');    // cache for 1 day

// Access-Control headers are received during OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {

    if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
        header("Access-Control-Allow-Methods: GET, POST, OPTIONS");

    if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
        header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");

    exit(0);
}

setcookie('x', "1", [
    'expires' => time() + 365*24*60*60,
    'path'=> '/',
    'secure' => true,
    'httponly' => true
]);

Header( "Content-Type: application/json");
echo "{\"hello\": \"".$_COOKIE['x']."\"}\n"
?>

这是一个示例 HTML 文件,暂时指向我的域 www.choiceofgames.com(但您应该使用自己的域进行测试):

<!DOCTYPE html>
<html>
<body></body>
<script type="module">
const url = 'https://www.choiceofgames.com/test/test.php';

let response;
response = await fetch(url, { credentials: "include" });
console.log(await response.text());

response = await fetch(url, { credentials: "include" });
console.log(await response.text());

</script>
</html>

当定位 API 级别 30 时,第一个 console.log 将返回 {"hello": ""},第二个 console.log 将返回 {"hello": "1 “}。检查 WebView,我可以看到 cookie 在第二个请求中发送。

当以 API 级别 31 为目标时,它会两次记录 {"hello": ""} ;第二次请求时不会发送 cookie。

“好吧,”我想。我只需在我的 cookie 上设置 SameSite=None 即可。”我是这样做的:(

setcookie('x', "1", [
    'expires' => time() + 365*24*60*60,
    'path'=> '/',
    'secure' => true,
    'httponly' => true,
    'samesite' => None
]);

我已将此版本提供为 https://www.choiceofgames.com/test/ test2.php 暂时。)

添加 SameSite=None 使我的 Android WebView 问题变得更糟。它并没有解决我的问题API 级别 31 中的 file:///android_asset/myfile.html WebView,但添加 SameSite=None 确实破坏了 API 级别 30 中的 WebView; code> 破坏了我的旧版本,并且在我的新版本中没有修复任何内容,

据我所知,SameSite=None 在 Android WebViews 中根本不起作用。 file:// URLs。

这让我想到了我的问题:

  1. 其他人可以重现我遇到的问题吗?Android WebViews 中的 file:// URLs 真的吗?不要在 API 级别 30 和 API 级别 31 中使用 SameSite=None 发送 cookie?(这是一个可归档的错误吗?有人阅读或修复像我这样的普通人提交的 Android 错误吗?)
  2. 有没有网页视图WebSetting 或者我可以用来解决这个问题的东西? (我目前正在使用 setAllowUniversalAccessFromFileURLs(true)
  3. 您能否建议我可以解决此问题的另一种方法?

I'm having a cookie issue upgrading my app to Android API level 31 (Android 12). My file:// URLs are unable to access remote cookies, even when I set them to SameSite=None.

One of the major documented changes in API Level 31 is a change in cookie behavior.

Modern SameSite cookies in WebView

Android’s WebView component is based on Chromium, the open source project that powers Google’s Chrome browser. Chromium introduced changes to the handling of third-party cookies to provide more security and privacy and offer users more transparency and control. Starting in Android 12, these changes are also included in WebView when apps target Android 12 (API level 31) or higher.

The SameSite attribute of a cookie controls whether it can be sent with any requests, or only with same-site requests. The following privacy-protecting changes improve the default handling of third-party cookies and help protect against unintended cross-site sharing:

  • Cookies without a SameSite attribute are treated as SameSite=Lax.
  • Cookies with SameSite=None must also specify the Secure attribute, meaning they require a secure context and should be sent over HTTPS.
  • Links between HTTP and HTTPS versions of a site are now treated as cross-site requests, so cookies are not sent unless they are appropriately marked as SameSite=None; Secure.

My app includes embedded HTML files in the assets folder, which I display using a WebView via URLs like file:///android_asset/myfile.html.

Prior to API level 31, the WebView was able to communicate to my remote server, receive cookies, and send those cookies back in responses, but when I target API Level 31, the WebView refuses to retransmit the cookies that my server sends, even when I set SameSite=None.

Here's a trivial sample PHP file reproducing the issue.

<?php
header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']);
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400');    // cache for 1 day

// Access-Control headers are received during OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {

    if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
        header("Access-Control-Allow-Methods: GET, POST, OPTIONS");

    if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
        header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");

    exit(0);
}

setcookie('x', "1", [
    'expires' => time() + 365*24*60*60,
    'path'=> '/',
    'secure' => true,
    'httponly' => true
]);

Header( "Content-Type: application/json");
echo "{\"hello\": \"".$_COOKIE['x']."\"}\n"
?>

And here's a sample HTML file, pointing to my domain www.choiceofgames.com for the time being (but you should use your own domain for testing):

<!DOCTYPE html>
<html>
<body></body>
<script type="module">
const url = 'https://www.choiceofgames.com/test/test.php';

let response;
response = await fetch(url, { credentials: "include" });
console.log(await response.text());

response = await fetch(url, { credentials: "include" });
console.log(await response.text());

</script>
</html>

When targeting API Level 30, the first console.log would return {"hello": ""}, and the second console.log would return {"hello": "1"}. Inspecting the WebView, I can see that the cookie gets sent in the second request.

When targeting API Level 31, it logs {"hello": ""} both times; the cookie isn't sent on the second request.

"OK," I thought. I'll just set SameSite=None on my cookie." I did it like this:

setcookie('x', "1", [
    'expires' => time() + 365*24*60*60,
    'path'=> '/',
    'secure' => true,
    'httponly' => true,
    'samesite' => None
]);

(I've made this version available as https://www.choiceofgames.com/test/test2.php for the time being.)

Adding SameSite=None made my Android WebView problem worse. It didn't fix my file:///android_asset/myfile.html WebView in API Level 31, but it did break my WebView in API Level 30; adding SameSite=None broke my old version, and fixed nothing in my new version.

As far as I can tell, SameSite=None just doesn't work at all in Android WebViews from file:// URLs.

That brings me to my questions:

  1. Can others repro the problem I'm having? Is it true that file:// URLs in Android WebViews just don't send cookies with SameSite=None, in both API Level 30 and in API Level 31? (Is this a fileable bug? Does anyone read or fix Android bugs filed by ordinary mortals like myself?)
  2. Is there a WebView WebSetting or something I can use to workaround this issue? (I'm currently using setAllowUniversalAccessFromFileURLs(true).
  3. Can you suggest another way I can workaround this issue?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

也只是曾经 2025-01-23 16:30:31

https://chromium.googlesource.com/chromium/src/+/1d127c933a4a39c65dc32cbd35bd511fd68ea452/android_webview/browser/cookie_manager.cc#317

// There are some unknowns about how to correctly handle file:// cookies,
// and our implementation for this is not robust.  http://crbug.com/582985

看起来在 Android 中加载资源文件的“最佳”方法不是使用file:///android_asset/myfile.html,但要使用 WebViewAssetLoader

WebViewAssetLoader 拦截 WebView 请求,使您的所有资源文件出现在虚假的 HTTPS 域 URL https://appassets.androidplatform.net/assets 上。您可以从 https://appassets.androidplatform.net/assets/myfile.html 加载,而不是 file:///android_asset/myfile.html

浏览器会将其视为“真正的”HTTPS 域。 SameSite=None 将正常工作,CORS 将具有传统的非空 Origin,并且在 file:// URL 之间共享 cookie 不会有任何奇怪的情况。

(但是,比 SameSite=None 更好的方法是使用实​​际域的假子域。WebViewAssetLoader 有一个构建器参数,允许您将域设置为您控制的域,例如,如果您拥有 < code>example.com,您可以在 https://appassets.example.com 上托管资产,从而允许您与您的网站共享 Cookie,即使使用 SameSite=Strict.)

https://chromium.googlesource.com/chromium/src/+/1d127c933a4a39c65dc32cbd35bd511fd68ea452/android_webview/browser/cookie_manager.cc#317

// There are some unknowns about how to correctly handle file:// cookies,
// and our implementation for this is not robust.  http://crbug.com/582985

It looks like the "best" way to load asset files in Android is not to use file:///android_asset/myfile.html, but to use a WebViewAssetLoader.

WebViewAssetLoader intercepts WebView requests, making all of your asset files appear on a fake HTTPS domain URL https://appassets.androidplatform.net/assets. Instead of file:///android_asset/myfile.html you'd load from https://appassets.androidplatform.net/assets/myfile.html.

The browser will treat that like a "real" HTTPS domain. SameSite=None will work normally, CORS will have a conventional non-null Origin, and there won't be any weirdness around sharing cookies between file:// URLs.

(But, even better than SameSite=None would be to use a fake subdomain of your actual domain. WebViewAssetLoader has a builder parameter allowing you to set the domain to a domain you control, e.g. if you own example.com, you could host assets on https://appassets.example.com, allowing you to share cookies with your website even with SameSite=Strict.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文