Problem
Two related gaps from the security review (journal/issues/2, item 1):
- Cookies lack the
Secure flag. Set-Cookie lines in src/routes/routes.c:126,131 and src/routes/session_mgmt.c:91,100,120,124,161 emit HttpOnly; Path=/; SameSite=Lax only — anyone on the same network can sniff active_session over plain HTTP.
make TLS=1 is a documentation stub. It links -lssl -lcrypto but lib/chttp.{c,h} contains zero OpenSSL code (no SSL_CTX, no SSL_accept, no SSL_read/SSL_write). The server only speaks plain HTTP today.
Plan
Reverse-proxy mode rather than implementing native TLS in the C framework. Full plan in journal/plans/require-https.md.
Three pieces:
- Append
; Secure to all 7 Set-Cookie callsites. (Browsers treat http://localhost as a secure context, so Vite dev still works.)
- Inject
Strict-Transport-Security: max-age=31536000; includeSubDomains in lib/chttp.c::chttp_write_response — the single chokepoint every response (including those from forked-child handlers) flows through.
- New env var
IMAGINARY_REQUIRE_HTTPS=1 (off by default) makes the server refuse plain HTTP with 400 from non-loopback peers. Optional IMAGINARY_TRUSTED_PROXIES for proxies on another host. We do not trust X-Forwarded-Proto — trust is anchored to peer IP because X-F-P is forge-able.
Native TLS in chttp can land later as its own issue.
Acceptance
make clean rebuild, zero warnings.
- Default (env unset): existing dev flow still works;
curl -i on /login shows Secure on Set-Cookie and Strict-Transport-Security on every response.
IMAGINARY_REQUIRE_HTTPS=1: localhost still works, non-loopback peer gets 400 HTTPS required.
- Behind a real TLS proxy (caddy/nginx/traefik): browser sees
Secure cookies, login works.
Branch: fix/require-https.
Problem
Two related gaps from the security review (
journal/issues/2, item 1):Secureflag.Set-Cookielines insrc/routes/routes.c:126,131andsrc/routes/session_mgmt.c:91,100,120,124,161emitHttpOnly; Path=/; SameSite=Laxonly — anyone on the same network can sniffactive_sessionover plain HTTP.make TLS=1is a documentation stub. It links-lssl -lcryptobutlib/chttp.{c,h}contains zero OpenSSL code (noSSL_CTX, noSSL_accept, noSSL_read/SSL_write). The server only speaks plain HTTP today.Plan
Reverse-proxy mode rather than implementing native TLS in the C framework. Full plan in
journal/plans/require-https.md.Three pieces:
; Secureto all 7Set-Cookiecallsites. (Browsers treathttp://localhostas a secure context, so Vite dev still works.)Strict-Transport-Security: max-age=31536000; includeSubDomainsinlib/chttp.c::chttp_write_response— the single chokepoint every response (including those from forked-child handlers) flows through.IMAGINARY_REQUIRE_HTTPS=1(off by default) makes the server refuse plain HTTP with 400 from non-loopback peers. OptionalIMAGINARY_TRUSTED_PROXIESfor proxies on another host. We do not trustX-Forwarded-Proto— trust is anchored to peer IP because X-F-P is forge-able.Native TLS in chttp can land later as its own issue.
Acceptance
makeclean rebuild, zero warnings.curl -ion/loginshowsSecureon Set-Cookie andStrict-Transport-Securityon every response.IMAGINARY_REQUIRE_HTTPS=1: localhost still works, non-loopback peer gets400 HTTPS required.Securecookies, login works.Branch:
fix/require-https.