Improve cookie access check (#1889)

This commit is contained in:
Jonatan Kłosko 2023-04-28 15:38:17 +01:00 committed by GitHub
parent 52b71b8a97
commit d0b4f16b19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 35 additions and 31 deletions

View file

@ -17,6 +17,7 @@ import { loadUserData } from "./lib/user";
import { loadAppAuthToken } from "./lib/app";
import { settingsStore } from "./lib/settings";
import { registerTopbar, registerGlobalEventHandlers } from "./events";
import { cookieOptions } from "./lib/utils";
function connect() {
const csrfToken = document
@ -83,9 +84,8 @@ function connect() {
//
// * Brave also implements storage partitioning (3)
//
// To detect whether cookies are allowed, we check for the user data cookie,
// which should be set by the server on the initial request and is accessible
// from JavaScript (without HttpOnly).
// To detect whether cookies are allowed, we check if we can programmatically
// set a cookie.
//
// Also see the proposal (4), which may streamline this in the future.
//
@ -94,7 +94,9 @@ function connect() {
// (3): https://brave.com/privacy-updates/7-ephemeral-storage
// (4): https://github.com/privacycg/CHIPS
if (loadUserData() === null) {
if (hasCookiesAccess()) {
connect();
} else {
const overlayEl = document.createElement("div");
overlayEl.innerHTML = `
@ -121,6 +123,12 @@ if (loadUserData() === null) {
overlayEl.querySelector("#open-app").href = window.location;
document.body.appendChild(overlayEl);
} else {
connect();
}
function hasCookiesAccess() {
document.cookie = `lb:probe_cookie=;path=/${cookieOptions()}`;
return document.cookie
.split("; ")
.some((cookie) => cookie.startsWith(`lb:probe_cookie=`));
}

View file

@ -1,4 +1,4 @@
import { decodeBase64, encodeBase64 } from "./utils";
import { cookieOptions, decodeBase64, encodeBase64 } from "./utils";
const USER_DATA_COOKIE = "lb:user_data";
@ -41,11 +41,3 @@ function setCookie(key, value, maxAge) {
const cookie = `${key}=${value};max-age=${maxAge};path=/${cookieOptions()}`;
document.cookie = cookie;
}
function cookieOptions() {
if (document.body.hasAttribute("data-within-iframe")) {
return ";SameSite=None;Secure";
} else {
return ";SameSite=Lax";
}
}

View file

@ -235,3 +235,11 @@ export function isFeatureFlagEnabled(feature) {
return features.split(",").includes(feature);
}
}
export function cookieOptions() {
if (document.body.hasAttribute("data-within-iframe")) {
return ";SameSite=None;Secure";
} else {
return ";SameSite=Lax";
}
}

View file

@ -104,6 +104,14 @@ defmodule LivebookWeb.Endpoint do
end
end
def cookie_options() do
if Livebook.Config.within_iframe?() do
[same_site: "None", secure: true]
else
[same_site: "Lax"]
end
end
# Because we run on localhost, we may accumulate
# cookies from several other apps. Our header limit
# is set to 32kB. Once we are 75% of said limit,

View file

@ -46,22 +46,10 @@ defmodule LivebookWeb.UserPlug do
user_data = user_data(User.new())
encoded = user_data |> Jason.encode!() |> Base.encode64()
put_resp_cookie(
conn,
"lb:user_data",
encoded,
# We disable HttpOnly, so that it can be accessed on the client
# and set expiration to 5 years
[http_only: false, max_age: 157_680_000] ++ cookie_options()
)
end
end
defp cookie_options() do
if Livebook.Config.within_iframe?() do
[same_site: "None", secure: true]
else
[same_site: "Lax"]
# We disable HttpOnly, so that it can be accessed on the client
# and set expiration to 5 years
opts = [http_only: false, max_age: 157_680_000] ++ LivebookWeb.Endpoint.cookie_options()
put_resp_cookie(conn, "lb:user_data", encoded, opts)
end
end