简体   繁体   中英

Chrome extension doesn't send SameSite=Lax cookies

I have some trouble working with cookies via chrome extension from popup script.

popup.js content:

document.addEventListener('DOMContentLoaded', () => {
    function cookieinfo() {
        chrome.cookies.getAll({url: 'http://localhost:8080'}, function(cookie) {
            console.log('Found cookie: ', cookie)
            if (cookie == null)
                return;

            fetch('http://localhost:8080', {credentials: 'include'}).then((response) => {
                // do some stuff
                return response;
            });
        });
    }
    window.onload=cookieinfo;
}, false);

Steps that I perform:

  1. Log into my application on localhost (So I get the cookies)
  2. Open the popup (so popup.js is executed)
  3. I see in the console log that chrome found necessary cookies
  4. Server says that ingoing request has empty cookies
  5. I refresh page of localhost application
  6. I am logged out now

Maybe someone knows what I'm doing wrong?

Edit:

It seems that the reason is that my cookie has parameters HttpOnly=true and SameSite=Lax ( related link ). I can see another cookies in the server log. But due to this thread all cookies will be sent if credentials parameter is set to include , even httpOnly cookies. Also I tried to send it to 127.0.0.1 instead of localhost due to this answer with the same result.

I can't set httpOnly to false. This is forced by framework. Somebody know how to fix it?

Edit2:

I finally installed Cookie editor and found out that the SameSite=Lax is the reason. If I set it to No Restriction then I will see it on the server side. Unfortunately, the framework I'm using only allows Lax and Strict options (Chrome extension fails with both). Does anyone know how to send Lax cookies from the Chrome extension?

This was the issue with extensions in Chromium till version 77. When cross-site cookie was set to SameSite=Lax or SameSite=Strict , the cookie was not sent with the cross-site request.

This has been fixed in version 78 in all platforms. Now chrome extension sends cookies when SameSite=Lax or SameSite=Strict .

References:

https://bugs.chromium.org/p/chromium/issues/detail?id=1007973

https://chromium-review.googlesource.com/c/chromium/src/+/1827503

https://bugs.chromium.org/p/chromium/issues/detail?id=617198

The content script is 100% the solution.

You basically have two individual browsers, the regular browser and also the extension popup browser. But they are entirely separate and can only send messages back and forth. So what you need to do is have the extension context send a message to the browser context that instructs some code in that context to get the document.cookies and send them back to the extension context.

Here's an example of me getting cookies from each separate browser context.

manifest.json

{
  "manifest_version": 2,
  "name": "Cookie Monster",
  "description": "Nom nom nom nom",
  "version": "1.0",
  "browser_action": {
    "default_popup": "html/extension.html",
    "default_title":"Cookie Monster"
  },
  "permissions": [
    "activeTab",
    "tabs",
    "http://*/*",
    "https://*/*"
 ],
  "content_scripts": [{
    "js":["/js/client.js"],
    "matches":["http://*/*","https://*/*"]
  }]
}

extension.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Cookies</title>
    <style>
      body {
        display: block; 
        min-height: 250px; 
        width: 250px; 
        padding: 5px; 
      }
      button {
        display: block; 
        margin: 0 0 10px 0; 
      }
    </style>
  </head>
  <body class="container">
    <h1>Cookies</h1>
    <button id="extension_cookies" type="button">Get PopUp Cookies</button>
    <button id="browser_cookies" type="button">Get Browser Cookies</button>
    <p id="result"></p>

    <script src="/js/extension.js" type="text/javascript"></script>
  </body>
</html>

extension.js

'use strict';
(function(){
    // cache import DOM elements
    const extension_btn = document.querySelector('#extension_cookies');
    const browser_btn = document.querySelector('#browser_cookies'); 
    const result = document.querySelector('#result');


    // runs in the popup window of the extension, 
    // which is it's own browser context 
    // and has it's own set of cookies
    extension_btn.addEventListener('click', () => {
        if (document.cookie === ''){
            result.innerText = 'No Cookies...';
        } else {
            result.innerText = document.cookie;
        }
    })

    // send message to browser context
    // message will inform browser client of what to do
    // the browser then needs to pass data to the callback function
    // then we can display results
    browser_btn.addEventListener('click', () => {
        chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
            chrome.tabs.sendMessage(tabs[0].id, {message: 'GET_COOKIES'}, (data) => {
                result.innerText = data.cookies
            });
        });
    })
}());

client.js

'use strict';
(function(){

  // receive a callback function so I can pass data to extension
  // get document cookies, put into an object
  // use callback to send response to extension
  const get_browser_cookies = (sendResponse) => {
    const cookies = document.cookie; 
    console.clear(); 
    console.log(cookies);
    sendResponse({ cookies: cookies }); 
  }


  // listen for messages from extension
  // a switch statement can help run only the correct function
  // must pass the function a reference to the sendResponse function
  // so I can pass data back to extension
  chrome.runtime.onMessage.addListener(function(data_from_extension, sender, sendResponse){
    switch (data_from_extension.message){
      case 'GET_COOKIES': {
        get_browser_cookies(sendResponse); 
        break; 
      }
      default: null; 
    }
  });
}())

What I found out is the cookie's path is crucial. Any mismatch results in misleading behaviour.

This is my setup:

  • backend server running at localhost:8081
  • chrome manifest permission has "http://localhost:8081/"
  • backend returns cookie with path=/ , eg. this is a sample response header Set-Cookie: refresh_token=bar; Path=/; SameSite=Lax; HttpOnly Set-Cookie: refresh_token=bar; Path=/; SameSite=Lax; HttpOnly
  • chrome extension can query the cookie manually: chrome.cookies.get({ url: 'http://localhost:8081/', name: 'refresh_token' }...
  • chrome extension automatically attaches the cookie when you sending to other url paths under localhost:8081 , eg:
     fetch('http://localhost:8081/v1/meh').then((response) => { console.log(response); })
    Server side will see the refresh_token cookie.

To summarise: a cookie set at path /a won't be sent to a url at path /b ; a cookie set at path / will be sent to all urls under same domain.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM