fix(security): harden hook and device token auth

This commit is contained in:
Peter Steinberger
2026-02-13 01:23:26 +01:00
parent 54513f4240
commit 113ebfd6a2
9 changed files with 190 additions and 12 deletions

View File

@@ -1,7 +1,7 @@
import type { IncomingMessage } from "node:http";
import { timingSafeEqual } from "node:crypto";
import type { GatewayAuthConfig, GatewayTailscaleMode } from "../config/config.js";
import { readTailscaleWhoisIdentity, type TailscaleWhoisIdentity } from "../infra/tailscale.js";
import { safeEqualSecret } from "../security/secret-equal.js";
import {
isLoopbackAddress,
isTrustedProxyAddress,
@@ -37,13 +37,6 @@ type TailscaleUser = {
type TailscaleWhoisLookup = (ip: string) => Promise<TailscaleWhoisIdentity | null>;
function safeEqual(a: string, b: string): boolean {
if (a.length !== b.length) {
return false;
}
return timingSafeEqual(Buffer.from(a), Buffer.from(b));
}
function normalizeLogin(login: string): string {
return login.trim().toLowerCase();
}
@@ -253,7 +246,7 @@ export async function authorizeGatewayConnect(params: {
if (!connectAuth?.token) {
return { ok: false, reason: "token_missing" };
}
if (!safeEqual(connectAuth.token, auth.token)) {
if (!safeEqualSecret(connectAuth.token, auth.token)) {
return { ok: false, reason: "token_mismatch" };
}
return { ok: true, method: "token" };
@@ -267,7 +260,7 @@ export async function authorizeGatewayConnect(params: {
if (!password) {
return { ok: false, reason: "password_missing" };
}
if (!safeEqual(password, auth.password)) {
if (!safeEqualSecret(password, auth.password)) {
return { ok: false, reason: "password_mismatch" };
}
return { ok: true, method: "password" };