iframe无法在Chrome中阅读Cookie

ax6ht2ek  于 12个月前  发布在  Go
关注(0)|答案(2)|浏览(141)

Chrome不允许儿童iframe读取自己的Cookie。

我有一个带有子iframe的父网页:

  • 父节点位于https://first-site.com
  • <iframe src="https://second-site.com">处的子项(父项内部)
  • Cookie设置
  • 路径:'/'
  • 安全:true
  • httpOnly:false
  • 域名:'. second-site.com'

我控制这两个站点,我希望iframe在iframe内执行一个操作,该操作需要.second-site.com的阅读cookie。外面的父母不需要知道这件事。

适用于除Chrome以外的所有浏览器。

Chrome只是不将子页面自己的Cookie提供给孩子。
在子页面自己的窗口中访问子页面并执行操作在所有浏览器中都有效,包括Chrome。
我已经尝试了这两个选项的所有排列:

  • 为cookie设置secure:falsesecure:true
  • 为iframe设置sandbox="allow-same-origin allow-scripts",或删除sandbox属性

Chrome有什么不同之处,Chrome中的iframe如何访问自己的Cookie?

xytpbqjk

xytpbqjk1#

有一个相对较新的cookie属性,名为SameSite,由我的服务器自动设置。禁用此功能(同时保留问题中列出的设置)将允许iframe访问Chrome中自己的Cookie。
参见Chrome feature statusIETF draft

更新2020年8月

Chrome现在在没有设置SameSite的情况下阻止cookie,因此您需要显式地将其设置为samesite=nonesecure=true

igsr9ssn

igsr9ssn2#

这个问题很久以前就被问到了,但它仍然是相关的。
我遇到了同样的问题,并得到了部分解决方案。
您可以尝试在服务器上创建代理,将请求重定向到目标域。这样,您就可以从响应中拦截Cookie用所需的参数覆盖它们,并将它们发送给客户端
范例:
您的 index.html

<html>
    <head>
        <meta http-equiv="Content-Security-Policy" content="default-src * 'unsafe-eval' 'unsafe-inline' data: gap: https://ssl.gstatic.com 'unsafe-eval'; frame-src *; connect-src *; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content:;">
    </head>
    <body>
        <script>
            const uuidv4 = () => ([1e7]+1e3+4e3+8e3+1e11).replace(/[018]/g, c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16));
            const id = localStorage.getItem('id') || uuidv4();
            localStorage.setItem('id', id);
            const iframe = document.createElement('iframe');
            iframe.setAttribute('width', '100%');
            iframe.setAttribute('height', '100%');
            iframe.setAttribute('src', `https://proxy.domain/?proxy_layer_id=${id}`);
            document.body.appendChild(iframe);
        </script>
    </body>
</html>

您的代理服务器 * index.php *

<?php

error_reporting(E_ALL);
ini_set("display_errors", "1");

$defaultTargetDomain = "target.domain";
$proxyDomain = "proxy.domain/";

$ck = $_COOKIE;
$userAgent = $_SERVER["HTTP_USER_AGENT"] ?? null;
$id = $_GET["proxy_layer_id"] ?? null;
$remoteDomain = $_GET["proxy_layer_domain"] ?? $defaultTargetDomain; // Use the default target domain if no other domain has been specified
$remotePath = $_GET["proxy_layer_path"] ?? null;
$sw = $_GET["proxy_layer_sw"] ?? false;

/* Decode remote domain */
if ($remoteDomain != null) {
    $remoteDomain = urldecode($remoteDomain);
}

/* Decode and parse remote path */
if ($remotePath != null) {
   $_url = parse_url($remotePath);
   $remotePath = $_url["path"]."?".($_url["query"] ?? "");
}

/* Generate service worker if requested */
if ($sw) {
    $sw = file_get_contents("./sw.js");
    $sw = str_replace("%id%", $id, $sw);
    $sw = str_replace("%target%", $remoteDomain, $sw);
    $sw = str_replace("%proxy%", $proxyDomain, $sw);
    header("Content-Type: application/javascript; charset=UTF-8");
    print($sw);
    return;
}

function processCookies(&$cookies = []) {
    foreach ($cookies as $cookie) {
        $values = trim(explode(":", $cookie)[1] ?? "");
        if (strlen($values) < 1) continue;
        $values = explode("; ", $values);
        foreach ($values as $value) {
            list($key, $value) = explode("=", urldecode($value));
            $ck[$key] = $value;
        }
    }
}

/* Generate final target URL */
$site = "https://" . $remoteDomain . $remotePath;

$request = $_SERVER["REQUEST_URI"];

$ch = curl_init();

/* If there was a POST request, then forward that as well*/
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $_POST);
}
curl_setopt($ch, CURLOPT_USERAGENT, $userAgent);
curl_setopt($ch, CURLOPT_URL, $site . $request);
curl_setopt($ch, CURLOPT_HEADER, true);

$headers = getallheaders();

/* Translate some headers to make the remote party think we actually browsing that site */
$extraHeaders = [];
if (isset($headers["Referer"])) {
    $extraHeaders[] = "Referer: " . str_replace($proxyDomain, $remoteDomain, $headers["Referer"]);
}
if (isset($headers["Origin"])) {
    $extraHeaders[] = "Origin: " . str_replace($proxyDomain, $remoteDomain, $headers["Origin"]);
}

/* Forward cookie as it came.  */
curl_setopt($ch, CURLOPT_HTTPHEADER, $extraHeaders);

$cookie = [];

foreach ($ck as $key => $value) {
    $cookie[] = "{$key}={$value}";
}

if (count($cookie)) {
    curl_setopt($ch, CURLOPT_COOKIE, implode("; ", $cookie));
}

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);

$response = curl_exec($ch);

$lastUrl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);

$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);

curl_close($ch);

$headers = substr($response, 0, $header_size);
$body = substr($response, $header_size);

$headerArray = explode(PHP_EOL, $headers);

$cookies = [];
$setCookies = [];

/* Process response headers */
foreach($headerArray as $header) {
    $skip = false;

    $colonPos = strpos($header, ":");

    if ($colonPos !== false) {
        $headerName = strtolower(substr($header, 0, $colonPos));

        /* Serialize html response */
        if (trim($headerName) == "content-type" && strpos($header, "text/html") !== false) {
            $query = http_build_query([
                "proxy_layer_id" => $id,
                "proxy_layer_domain" => $remoteDomain,
                "proxy_layer_sw" => true
            ]);

            /* Generate injection */
            $injection = file_get_contents("./injection.js");
            str_replace("%query%", $query, $injection);

            /* Add your own js to install the service worker */
            $body = str_replace("<head>", "<head><script>$injection</script>", $body);

            $dom = new DOMDocument();
            @$dom->loadHTML($body);

            /* Get all hyperlinks */
            $links = $dom->getElementsByTagName('a');

            /* Proxy all target hyperlinks */
            foreach ($links as $link) {
                $href = $link->getAttribute('href');
                $scheme = parse_url($href, PHP_URL_SCHEME);
                if (!$href || $href[0] === "#" || $scheme) continue;
                $prefix = $href[0] == "/" ? "" : "/";
                $path = $prefix . $href;
                $query = [
                    "proxy_layer_id" => $id,
                    "proxy_layer_domain" => $remoteDomain,
                    "proxy_layer_path" => $path
                ];
                $newLink = "https://" . $proxyDomain . "?" . http_build_query($query);
                $body = str_replace("\"$href\"", "\"$newLink\"", $body);
            }
        }

        /* Ignore content headers, let the webserver decide how to deal with the content */
        //if (trim($headerName) == "content-encoding") continue;
        //if (trim($headerName) == "content-length") continue;
        //if (trim($headerName) == "transfer-encoding") continue;
        //if (trim($headerName) == "location") continue;

        /* Ignore and get all cookie headers */
        if (trim($headerName) == "cookie") {
            $cookies[] = $header;
            $skip = true;
        }

        /* Change cookie domain for the proxy */
        if (trim($headerName) == 'set-cookie') {
            $header = str_replace('domain=' . $remoteDomain, 'domain=' . $proxyDomain, $header);
            $setCookies[] = $header;
            $skip = true;
        }
    }

    if ($skip) continue;

    header($header, false);
}

/* Merge all cookies */
processCookies($cookies);
processCookies($setCookies);

/* Set cookies */
foreach ($ck as $key => $value) {
    setcookie($key, $value, ["secure" => true, "samesite" => "None"]);
}

echo $body;

您的 sw.js

/**
This service worker intercepts all http requests and redirects them to the proxy server
*/

const id = '%id%';
const target = '%target%';
const proxy = '%proxy%';

self.addEventListener('install', (event) => {
    event.waitUntil(self.skipWaiting());
    self.skipWaiting();
    console.log('Installed', event);
});

self.addEventListener('activate', (event) => {
    event.waitUntil(self.clients.claim());
    console.log('Activated', event);
});

self.addEventListener('fetch', (event) => {
    const url = decodeURI(event.request.url);
    if (!url.startsWith(location.origin) && !url.startsWith(`https://${proxy}`) && !url.startsWith(location.host) && (url.startsWith('http') || url.includes(target))) return; // Filter
    const [path, params] = (() => {
        if (url.startsWith('http')) {
            return url.split('/').slice(location.origin.split('/').length).join('/').split('?');
        } else {
            return url.split('?');
        }
    })();
    const request = new Request(encodeURI(`https://${proxy}?proxy_layer_id=${id}&proxy_layer_domain=${target}${typeof path === 'string' && path.length > 1 ? `&proxy_layer_path=/${path}` : ''}${typeof params === 'string' && params.length > 0 ? `&${params}` : ''}`), event.request);
    event.respondWith(new Promise((resolve, reject) => fetch(request).then(resolve).catch(e => {
        console.log("e", e);
        reject(e);
    })));
});

您的 injection.js

const serwiceWorkerPath = '/';
const query = '%query%';

navigator.serviceWorker.register(`${serviceWorkerPath}?${query}`).then(() => {
    return navigator.serviceWorker.ready;
}).then((reg) => {
    console.log('Service Worker is ready', reg);
    if (!localStorage.getItem('sw')) { // Reload if the service worker installed for the first time
        localStorage.setItem('sw', true);
        location.reload();
    }
}).catch((error) => {
    console.log('Error: ', error);
});

因此,您的iframe的内容将在document.cookie中拥有实际的cookie。

**PS:**在我的例子中,所有端点都在不同的域上

相关问题