fix: H11 — load session salts from env vars instead of hardcoded 8-byte values

SESSION_SIGNING_SALT, SESSION_ENCRYPTION_SALT, and LIVE_VIEW_SIGNING_SALT
are now loaded from environment variables in production. Dev/test keep the
previous defaults via config.exs. k8s/secrets.yaml has placeholder entries;
the user fills in real values before applying.
This commit is contained in:
Graham McIntire 2026-05-12 14:03:31 -05:00
parent 095c5d3236
commit a9579b84f0
5 changed files with 60 additions and 14 deletions

12
bugs.md
View file

@ -30,18 +30,6 @@
---
### H11. Hardcoded Session Salts (8 bytes)
**File:** `lib/towerops_web/endpoint.ex:11-12`
**File:** `config/config.exs:135`
**Severity:** HIGH — Weak entropy for cookie signing; identical across all environments
**Description:** `signing_salt: "hrDZxLhd"` and `encryption_salt: "vK3p8mNx"` are hardcoded 8-byte salts. Phoenix recommends 16+ bytes. Also baked into source code — can't rotate without deploy.
**Fix:** Load from env vars, increase to 16+ bytes.
---

View file

@ -138,6 +138,7 @@ config :towerops, ToweropsWeb.Endpoint,
# to "/data/coverage" (the shared NFS mount) in config/prod.exs so all
# replicas can serve the rasters and pod restarts don't lose them.
config :towerops, :coverage_storage_dir, {:towerops, "priv/static/coverage"}
config :towerops, :live_view_signing_salt, "Uh1ABfdI"
# SNMP MIB directories for production (in Docker image)
# MIB files are included in the release at /app/priv/mibs
@ -161,6 +162,9 @@ config :towerops, :scopes,
test_setup_helper: :register_and_log_in_user
]
config :towerops, :session_encryption_salt, "vK3p8mNx"
config :towerops, :session_signing_salt, "hrDZxLhd"
# Agent Docker Image
# Override this in runtime.exs or environment-specific config
config :towerops,

View file

@ -58,6 +58,27 @@ if config_env() == :prod do
# want to use a different value for prod and you most likely don't want
# to check this value into version control, so we use an environment
# variable instead.
session_signing_salt =
System.get_env("SESSION_SIGNING_SALT") ||
raise """
environment variable SESSION_SIGNING_SALT is missing.
You can generate one by calling: openssl rand -base64 16
"""
session_encryption_salt =
System.get_env("SESSION_ENCRYPTION_SALT") ||
raise """
environment variable SESSION_ENCRYPTION_SALT is missing.
You can generate one by calling: openssl rand -base64 16
"""
live_view_signing_salt =
System.get_env("LIVE_VIEW_SIGNING_SALT") ||
raise """
environment variable LIVE_VIEW_SIGNING_SALT is missing.
You can generate one by calling: openssl rand -base64 16
"""
secret_key_base =
System.get_env("SECRET_KEY_BASE") ||
raise """
@ -411,6 +432,7 @@ if config_env() == :prod do
"//towerops.net",
"//*.towerops.net"
],
live_view: [signing_salt: live_view_signing_salt],
http: [
# Enable IPv6 and bind on all interfaces.
# Set it to {0, 0, 0, 0, 0, 0, 0, 1} for local network only access.
@ -436,6 +458,8 @@ if config_env() == :prod do
# Set default sender for all emails
config :towerops, :mailer_from, {"Towerops", "hi@towerops.net"}
config :towerops, :redis, redis_config
config :towerops, :session_encryption_salt, session_encryption_salt
config :towerops, :session_signing_salt, session_signing_salt
# Cloudflare API credentials for brute force protection
config :towerops,

View file

@ -67,6 +67,21 @@ spec:
secretKeyRef:
name: towerops-secrets
key: CLOAK_KEY
- name: SESSION_SIGNING_SALT
valueFrom:
secretKeyRef:
name: towerops-secrets
key: SESSION_SIGNING_SALT
- name: SESSION_ENCRYPTION_SALT
valueFrom:
secretKeyRef:
name: towerops-secrets
key: SESSION_ENCRYPTION_SALT
- name: LIVE_VIEW_SIGNING_SALT
valueFrom:
secretKeyRef:
name: towerops-secrets
key: LIVE_VIEW_SIGNING_SALT
- name: STRIPE_SECRET_KEY
valueFrom:
secretKeyRef:
@ -188,6 +203,21 @@ spec:
secretKeyRef:
name: towerops-secrets
key: CLOAK_KEY
- name: SESSION_SIGNING_SALT
valueFrom:
secretKeyRef:
name: towerops-secrets
key: SESSION_SIGNING_SALT
- name: SESSION_ENCRYPTION_SALT
valueFrom:
secretKeyRef:
name: towerops-secrets
key: SESSION_ENCRYPTION_SALT
- name: LIVE_VIEW_SIGNING_SALT
valueFrom:
secretKeyRef:
name: towerops-secrets
key: LIVE_VIEW_SIGNING_SALT
- name: STRIPE_SECRET_KEY
valueFrom:
secretKeyRef:

View file

@ -12,8 +12,8 @@ defmodule ToweropsWeb.Endpoint do
@session_options [
store: :cookie,
key: "_towerops_key",
signing_salt: "hrDZxLhd",
encryption_salt: "vK3p8mNx",
signing_salt: Application.compile_env(:towerops, :session_signing_salt, "default-signing-salt-missing-env"),
encryption_salt: Application.compile_env(:towerops, :session_encryption_salt, "default-encryption-salt-missing-env"),
same_site: "Lax",
http_only: true,
secure: Application.compile_env(:towerops, :secure_cookies, false)