Files
webmail/lib/security/request-protocol.ts

42 lines
1.6 KiB
TypeScript

/**
* Whether the browser reached us over HTTPS.
*
* The app itself only ever speaks plain HTTP: TLS terminates at the reverse
* proxy in front of it, which is expected to say so in `X-Forwarded-Proto`.
* Without a proxy the request URL's own scheme is the truth. Both the setup
* wizard and the admin session derive the cookie `Secure` flag from this, so
* the answer is the same for both.
*/
export function isHttpsRequest(request: Request): boolean {
const forwarded = request.headers.get('x-forwarded-proto');
if (forwarded) {
return forwarded.split(',')[0]!.trim().toLowerCase() === 'https';
}
try {
return new URL(request.url).protocol === 'https:';
} catch {
return false;
}
}
/**
* Whether the site is served over HTTPS, for decisions a missing proxy header
* must not weaken: which cookie name to trust, whether to send HSTS. A TLS
* proxy that sends no X-Forwarded-Proto makes every request look like plain
* HTTP, so COOKIE_SECURE=true counts as the operator saying it is HTTPS.
*/
export function isServedOverHttps(request: Request): boolean {
return process.env.COOKIE_SECURE === 'true' || isHttpsRequest(request);
}
/**
* The `Secure` flag for a cookie the server sets. An explicit COOKIE_SECURE
* wins, as it does for the mailbox cookies; otherwise it follows the request,
* because the browser drops Secure cookies on plain HTTP. Without a request
* the production default stands.
*/
export function secureCookieFlag(request?: Request): boolean {
if (process.env.COOKIE_SECURE !== undefined) return process.env.COOKIE_SECURE === 'true';
return request ? isHttpsRequest(request) : process.env.NODE_ENV === 'production';
}