fix: validate ws tls fingerprint

This commit is contained in:
Peter Steinberger
2026-01-20 12:31:09 +00:00
parent 06c17a333e
commit dcb8d16591
5 changed files with 227 additions and 3 deletions

View File

@@ -128,7 +128,17 @@ export class GatewayClient {
}
this.ws = new WebSocket(url, wsOptions);
this.ws.on("open", () => this.queueConnect());
this.ws.on("open", () => {
if (url.startsWith("wss://") && this.opts.tlsFingerprint) {
const tlsError = this.validateTlsFingerprint();
if (tlsError) {
this.opts.onConnectError?.(tlsError);
this.ws?.close(1008, tlsError.message);
return;
}
}
this.queueConnect();
});
this.ws.on("message", (data) => this.handleMessage(rawDataToString(data)));
this.ws.on("close", (code, reason) => {
const reasonText = rawDataToString(reason);
@@ -139,6 +149,9 @@ export class GatewayClient {
});
this.ws.on("error", (err) => {
logDebug(`gateway client error: ${String(err)}`);
if (!this.connectSent) {
this.opts.onConnectError?.(err instanceof Error ? err : new Error(String(err)));
}
});
}
@@ -340,6 +353,25 @@ export class GatewayClient {
}, interval);
}
private validateTlsFingerprint(): Error | null {
if (!this.opts.tlsFingerprint || !this.ws) return null;
const expected = normalizeFingerprint(this.opts.tlsFingerprint);
if (!expected) return new Error("gateway tls fingerprint missing");
const socket = (
this.ws as WebSocket & {
_socket?: { getPeerCertificate?: () => { fingerprint256?: string } };
}
)._socket;
if (!socket || typeof socket.getPeerCertificate !== "function") {
return new Error("gateway tls fingerprint unavailable");
}
const cert = socket.getPeerCertificate();
const fingerprint = normalizeFingerprint(cert?.fingerprint256 ?? "");
if (!fingerprint) return new Error("gateway tls fingerprint unavailable");
if (fingerprint !== expected) return new Error("gateway tls fingerprint mismatch");
return null;
}
async request<T = unknown>(
method: string,
params?: unknown,