Purge cookies once they exceed 24kB, closes #1832 (#1833)

This commit is contained in:
José Valim 2023-03-28 17:11:44 +02:00 committed by GitHub
parent 5431c36643
commit f52ff1e9ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 37 additions and 12 deletions

View file

@ -1,9 +1,9 @@
import { decodeBase64, encodeBase64 } from "./utils"; import { decodeBase64, encodeBase64 } from "./utils";
const USER_DATA_COOKIE = "user_data"; const USER_DATA_COOKIE = "lb:user_data";
/** /**
* Stores user data in the `"user_data"` cookie. * Stores user data in the `"lb:user_data"` cookie.
*/ */
export function storeUserData(userData) { export function storeUserData(userData) {
const json = JSON.stringify(userData); const json = JSON.stringify(userData);
@ -12,7 +12,7 @@ export function storeUserData(userData) {
} }
/** /**
* Loads user data from the `"user_data"` cookie. * Loads user data from the `"lb:user_data"` cookie.
*/ */
export function loadUserData() { export function loadUserData() {
const encoded = getCookieValue(USER_DATA_COOKIE); const encoded = getCookieValue(USER_DATA_COOKIE);

View file

@ -8,7 +8,7 @@ import Config
config :livebook, LivebookWeb.Endpoint, config :livebook, LivebookWeb.Endpoint,
# Binding to loopback ipv4 address prevents access from other machines. # Binding to loopback ipv4 address prevents access from other machines.
# Change to `ip: {0, 0, 0, 0}` to allow access from other machines. # Change to `ip: {0, 0, 0, 0}` to allow access from other machines.
http: [ip: {127, 0, 0, 1}, port: 4000], http: [ip: {127, 0, 0, 1}, port: 4000, protocol_options: [max_header_value_length: 32768]],
code_reloader: true, code_reloader: true,
debug_errors: true, debug_errors: true,
check_origin: false, check_origin: false,

View file

@ -2,7 +2,7 @@ import Config
# Default bind and port for production # Default bind and port for production
config :livebook, LivebookWeb.Endpoint, config :livebook, LivebookWeb.Endpoint,
http: [ip: {127, 0, 0, 1}, port: 8080], http: [ip: {127, 0, 0, 1}, port: 8080, protocol_options: [max_header_value_length: 32768]],
server: true server: true
config :livebook, :iframe_port, 8081 config :livebook, :iframe_port, 8081

View file

@ -6,7 +6,7 @@ defmodule LivebookWeb.Endpoint do
# Set :encryption_salt if you would also like to encrypt it. # Set :encryption_salt if you would also like to encrypt it.
@session_options [ @session_options [
store: :cookie, store: :cookie,
key: "_livebook_key", key: "lb:session",
signing_salt: "deadbook" signing_salt: "deadbook"
] ]
@ -78,6 +78,7 @@ defmodule LivebookWeb.Endpoint do
plug Plug.MethodOverride plug Plug.MethodOverride
plug Plug.Head plug Plug.Head
plug :session plug :session
plug :purge_cookies
# Run custom plugs from the app configuration # Run custom plugs from the app configuration
plug LivebookWeb.ConfiguredPlug plug LivebookWeb.ConfiguredPlug
@ -103,6 +104,30 @@ defmodule LivebookWeb.Endpoint do
end end
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,
# we clear other cookies to make sure we don't go
# over the limit.
def purge_cookies(conn, _opts) do
cookie_size =
conn
|> Plug.Conn.get_req_header("cookie")
|> Enum.map(&byte_size/1)
|> Enum.sum()
if cookie_size > 24576 do
conn.cookies
|> Enum.reject(fn {key, _value} -> String.starts_with?(key, "lb:") end)
|> Enum.take(10)
|> Enum.reduce(conn, fn {key, _value}, conn ->
Plug.Conn.delete_resp_cookie(conn, key)
end)
else
conn
end
end
def access_struct_url() do def access_struct_url() do
base = base =
case struct_url() do case struct_url() do

View file

@ -40,14 +40,14 @@ defmodule LivebookWeb.UserPlug do
end end
defp ensure_user_data(conn) do defp ensure_user_data(conn) do
if Map.has_key?(conn.req_cookies, "user_data") do if Map.has_key?(conn.req_cookies, "lb:user_data") do
conn conn
else else
user_data = user_data(User.new()) user_data = user_data(User.new())
encoded = user_data |> Jason.encode!() |> Base.encode64() encoded = user_data |> Jason.encode!() |> Base.encode64()
# Set `http_only` to `false`, so that it can be accessed on the client # Set `http_only` to `false`, so that it can be accessed on the client
# Set expiration in 5 years # Set expiration in 5 years
put_resp_cookie(conn, "user_data", encoded, http_only: false, max_age: 157_680_000) put_resp_cookie(conn, "lb:user_data", encoded, http_only: false, max_age: 157_680_000)
end end
end end
@ -60,7 +60,7 @@ defmodule LivebookWeb.UserPlug do
# Copies user_data from cookie to session, so that it's # Copies user_data from cookie to session, so that it's
# accessible to LiveViews # accessible to LiveViews
defp mirror_user_data_in_session(conn) do defp mirror_user_data_in_session(conn) do
user_data = conn.cookies["user_data"] |> Base.decode64!() |> Jason.decode!() user_data = conn.cookies["lb:user_data"] |> Base.decode64!() |> Jason.decode!()
put_session(conn, :user_data, user_data) put_session(conn, :user_data, user_data)
end end
end end

View file

@ -33,7 +33,7 @@ defmodule LivebookWeb.UserPlugTest do
|> fetch_cookies() |> fetch_cookies()
|> call() |> call()
assert conn.cookies["user_data"] != nil assert conn.cookies["lb:user_data"] != nil
end end
test "keeps user_data cookie if present" do test "keeps user_data cookie if present" do
@ -43,10 +43,10 @@ defmodule LivebookWeb.UserPlugTest do
conn = conn =
conn(:get, "/") conn(:get, "/")
|> init_test_session(%{}) |> init_test_session(%{})
|> put_req_cookie("user_data", cookie_value) |> put_req_cookie("lb:user_data", cookie_value)
|> fetch_cookies() |> fetch_cookies()
|> call() |> call()
assert conn.cookies["user_data"] == cookie_value assert conn.cookies["lb:user_data"] == cookie_value
end end
end end