The Problem the Cowork Audit Established

The cowork architecture page documented that the service managing the Claude Desktop sandbox installs its own CA certificate inside the guest VM and performs full TLS interception on traffic to *.anthropic.com. Every API call from inside the VM is decrypted, inspectable, and re-encrypted before forwarding.

This breaks HTTPS's security guarantee in a specific and important way.

HTTPS does not encrypt traffic in an absolute sense. It encrypts traffic to a verified party. The verification step — confirming the server's certificate was signed by a trusted authority — is what prevents a man-in-the-middle from substituting their own endpoint. When an attacker installs their own CA into the trust store and uses it to issue certificates for the target domain, they become a trusted authority. The verification step passes. The attacker is in the middle.

The question this page answers is: given that this interception is present, how can a process verify that it is communicating with the real endpoint?

Why Standard TLS Verification Fails Here

The normal TLS verification path consults the operating system's certificate trust store — or, in runtimes like Node.js, a bundled trust store that can be extended by environment variable. If the intercepting CA's certificate is present in either location, standard verification will succeed against the intercepted connection. The process has no way to distinguish the real endpoint from the proxy.

Live Observation This exact behavior was confirmed when fetching the cowork page itself. The connection to cowork.exponential-systems.net was intercepted by O=Anthropic; CN=sandbox-egress-production TLS Inspection CA, yet the TLS library reported SSL certificate verify ok. The page documenting the interception was itself being intercepted.

The interception is not a bug in the proxy's implementation. It is the intended behavior of a CA-based trust model when the CA is controlled by a party in the network path.

The Countermeasure: Certificate Pinning Against a Live-Fetched Fingerprint

Certificate pinning replaces the CA-based trust question ("was this cert signed by a trusted authority?") with an identity question ("does this cert match the specific cert I expect?"). A pinned fingerprint is a SHA-256 hash of the certificate's raw bytes. An attacker who substitutes a different certificate — even one signed by a CA in the trust store — produces a different hash. The substitution is detectable.

1
Establish the real fingerprint out-of-band. Before any interception is possible, connect to the target endpoint from a clean network context and record the SHA-256 fingerprint of its leaf certificate. This must happen on a machine or in a context where the intercepting CA has not yet been installed. The fingerprint cannot be forged: no CA can sign a certificate that produces the same SHA-256 hash as the original without controlling the original's private key.
2
Strip proxy and CA injection variables before the connection. A pinning check that routes through a proxy before establishing TLS verifies the proxy's certificate, not the endpoint's. The following environment variables must be explicitly cleared before any verification connection. HTTPS_PROXY (redirects the entire connection) and NODE_EXTRA_CA_CERTS (silently extends the trust store at runtime) are the highest-impact, but the full set must be cleared:
HTTPS_PROXY NODE_EXTRA_CA_CERTS HTTP_PROXY ALL_PROXY http_proxy https_proxy all_proxy REQUESTS_CA_BUNDLE SSL_CERT_FILE SSL_CERT_DIR NODE_TLS_REJECT_UNAUTHORIZED
These variables are the software equivalent of the CA installation attack: they redirect or weaken TLS at the application layer. Stripping them must happen before the connection, not after.
3
Compare the live fingerprint against the pinned value. Connect to the endpoint with proxy variables cleared. Extract the SHA-256 fingerprint of the leaf certificate returned. Compare against the stored pin. A mismatch means either the endpoint's certificate has legitimately rotated (requiring a pin update) or an active interception is present.
4
Maintain a pin update process. Real endpoints rotate their certificates on schedules measured in months. A pinning system that cannot update its pins eventually breaks legitimate connections. The update process must follow the same proxy-stripping procedure: fetch the new cert in a clean context, verify the domain, replace the stored pin.

The Node.js-Specific Bypass

In Node.js, the standard https module reads HTTPS_PROXY at module load time, not at connection time. An application that unsets HTTPS_PROXY at runtime and then calls https.request() may still route through the proxy if the module was already initialized with that variable set. Environment variable manipulation at runtime is not a reliable defense in runtimes with module-level initialization.

The bypass is to avoid the https module entirely for sensitive connections and construct the TCP path manually:

1.  net.createConnection(proxyHost, proxyPort)
    // open TCP to proxy (or directly to target if no proxy)

2.  write: "CONNECT targetHost:443 HTTP/1.1\r\nHost: targetHost\r\n\r\n"
    // HTTP tunnel request

3.  on 200 response: tls.connect({ socket, servername: targetHost, ... })
    // TLS handshake over the raw socket

4.  extract certificate fingerprint from tls.getPeerCertificate()
    // compare SHA-256 against stored pin

This path gives the application direct control over every step of the connection. No environment variable at any layer can redirect it. The TLS handshake occurs directly against the target endpoint, and the resulting certificate is the one to pin against.

The same approach is necessary even in non-proxy scenarios when NODE_EXTRA_CA_CERTS may have been injected: by constructing the TLS context explicitly with a specific ca option, the application trusts only the CAs it names rather than inheriting whatever the environment has added.

The Spawn Contract: Env Inheritance as Attack Surface

The cowork audit documented that the sandbox management process is spawned with the full parent process environment. Every child process spawned with {...process.env} or equivalent inherits all proxy variables and CA injection variables the parent acquired — regardless of whether the child's own code is otherwise clean.

The correct spawn contract for any process that must maintain a clean network context is explicit environment construction:

// Only enumerate what the child requires.
// Proxy and CA injection variables are absent by construction.
const childEnv = {
  PATH: process.env.PATH,
  HOME: process.env.HOME,
  // ... additional variables the child specifically needs
};

const child = spawn(command, args, {
  env:   childEnv,
  stdio: ['ignore', 'pipe', 'pipe'],  // see next section
});

Absence is the safe default. Any variable not explicitly granted to the child is not present. The parent cannot accidentally propagate an injected proxy variable it is not aware of.

The Accountability Gap: stdio and Process Lifecycle

The cowork audit documented that the sandbox management process is spawned with stdio: 'ignore' and immediately unref()'d. Both choices have security implications beyond their operational convenience.

stdio: 'ignore' means the parent process has no channel to observe what the child writes. A child that logs credentials, exfiltrates data, or signals errors does so invisibly. The parent cannot distinguish a child that is functioning correctly from one that is not.

unref() means the parent's event loop does not wait for the child. A child that crashes, restarts, or continues running after the parent exits does so without the parent's knowledge or control.

The alternative is a ref'd child with piped stdio:

child.stdout.on('data', d => log('[child:out]', d.toString().trim()));
child.stderr.on('data', d => log('[child:err]', d.toString().trim()));
child.on('exit', (code, signal) => {
  log('[child] exited', code, signal);
  // implement restart policy, parent alert, or clean shutdown here
});

// Tie child lifecycle to parent lifecycle
process.on('exit', () => child.kill('SIGTERM'));
app.on('before-quit', () => child.kill('SIGTERM'));

This pattern makes the child's behavior visible and ties its lifecycle to the parent's. A child that is doing something unexpected surfaces it through the accountability channel rather than into a void.

Summary

The cowork audit documents an interception architecture that operates at the CA trust layer. Standard TLS verification cannot detect it because standard verification trusts the installed CA. None of the following countermeasures require trusting any particular tooling or infrastructure. Each can be implemented from scratch using standard library primitives.

Attack vector Countermeasure
CA cert installed in trust store Pin the real endpoint's leaf certificate SHA-256 fingerprint
Proxy env vars redirect the connection before TLS Explicitly clear all proxy and CA injection env vars before connecting
Node.js https module reads env at init time Bypass via raw net.createConnectionCONNECTtls.connect
{...process.env} spawn inherits injected vars Explicit env construction — vars absent by default
stdio: 'ignore' + unref() hides child behavior Piped stdio, ref'd lifecycle, explicit shutdown on parent exit
Implementation Note The verification approach described here is local and self-contained. It does not depend on a third party, a certificate transparency log, or any external service to tell you whether the connection is clean. The fingerprint comparison happens in your process, against a value you established independently.