Dansday

The 37MB binary surgery

The 37MB binary surgery

Published on Apr 25, 2026

A recurring theme in application security is the gap between how secure a system looks and how secure it actually is. Developers invest significant effort in the visible parts — choosing strong algorithms, minifying and obfuscating JavaScript, designing carefully structured token payloads — while leaving a deeper structural weakness unexamined.

That weakness has a simple name: client-side trust. It is the assumption, baked into the architecture of many applications, that code running on a user's machine can be relied upon to behave as intended. In most threat models, that assumption is wrong.

The illusion of security through obfuscation

Code obfuscation is a legitimate tool. Mangled variable names, encoded string literals, and restructured control flow all raise the cost of reverse engineering. For certain deployment contexts — particularly protecting proprietary algorithms in compiled binaries — obfuscation can form a meaningful part of a defence-in-depth strategy.

The problem arises when obfuscation is treated as a security boundary rather than a speed bump. Obfuscated JavaScript, no matter how thoroughly transformed, still runs in a runtime environment that can be inspected, paused, and modified. Any determined analyst with access to the running process can observe what the code actually does. Obfuscation buys time; it does not provide a guarantee.

This distinction matters enormously. If a security mechanism depends on an attacker being unable to read the source, then the security mechanism is only as strong as the obfuscation — and obfuscation has a known ceiling.

"Obfuscation raises the cost of analysis. It does not change what is fundamentally possible. Any code the machine can run, a sufficiently motivated person can understand."

When cryptography is in the right place for the wrong reason

Cryptographic signing — the use of algorithms like RSA or ECDSA to produce tokens whose authenticity can be verified — is genuinely strong when applied correctly. The mathematical hardness of forging a valid signature without the private key is well established. This is not the weakness.

The weakness is in where the verification step lives. A JWT (JSON Web Token), for example, is signed by a server that holds a private key. Any party with the corresponding public key can verify that the token was issued by that server and has not been tampered with. This is secure — provided the verification actually happens.

When the verification step is performed by client-side code, a structural problem emerges. The client is the very entity whose trustworthiness is in question. Asking a process to verify its own authorization is a logical circularity. If an attacker can modify the client — or simply instruct it to skip the verification — the cryptographic strength of the signature becomes irrelevant. The lock is strong, but it is installed on the inside of the door.

Many token libraries expose both a verify function and a decode function. The verify function checks the cryptographic signature; the decode function simply parses the payload. If client-side code can be coerced into calling decode where it should call verify, the entire signing infrastructure provides no protection. The token is read, not validated.

Network identity and the problem of interceptable calls

A natural response to client-side weakness is to move the authoritative check to a remote server: the client must contact a backend endpoint, and the backend decides whether the request is legitimate. This design is fundamentally sounder — but only if the client cannot impersonate that backend to itself.

In containerised environments and networked systems, DNS resolution can often be influenced at the infrastructure layer. If an application resolves a hostname to make its license or authentication call, and an attacker controls the DNS environment in which that application runs, the application's request can be silently redirected to a server the attacker controls. The application performs what looks to it like a legitimate remote check — and receives a response crafted to pass all local validation.

This is not a novel attack class. DNS spoofing and server impersonation are well-documented. The relevant point for application architects is that any verification call whose destination can be redirected must be treated with the same scepticism as a local check. If the response from that call is processed by client-side code without independent verification — via certificate pinning, for instance, or a server-side relay — the check provides limited assurance.

"Moving validation to a remote server only helps if the client cannot intercept its own outbound call and substitute a different response."

The three-layer failure mode

What emerges from these observations is a recognisable pattern of compounded weaknesses. Each layer appears secure in isolation. Together, they create a system that can be bypassed without touching any of the genuinely hard problems.

Layer one is obfuscation treated as a barrier. It slows analysis but does not prevent it. Once the code is understood, the logical structure is visible regardless of how the variable names have been transformed.

Layer two is client-side signature verification. Strong cryptography is present, but it is being checked by code the attacker controls. A small modification — substituting one function call for another — removes the check entirely.

Layer three is a remote validation call whose destination is resolvable by the client. The application contacts what it believes to be an authoritative server. If the DNS environment can be influenced, the application talks to an attacker-controlled endpoint instead and processes whatever response it receives.

None of these weaknesses requires breaking RSA. None requires a vulnerability in the JWT specification. They are all consequences of architectural decisions about where trust is placed and where validation happens.

What secure architectures do differently

The corrective principle is straightforward: authoritative decisions must be made by authoritative parties. Validation that matters should happen on infrastructure the application vendor controls, not on hardware the user controls.

For token-based systems, this means server-side verification as the norm. The client presents a token; the server verifies it against a key it holds and responds accordingly. The client never performs the verification step itself. An attacker modifying client code gains nothing, because the client's code is not part of the trust chain.

For remote validation calls, certificate pinning raises the bar significantly. Rather than trusting whatever certificate is presented by the resolved hostname, the application validates that the certificate matches a known, embedded fingerprint. Redirecting DNS to a different server does not help if that server cannot present the expected certificate.

Behavioural server-side checks add a further layer. Even if a client presents a valid token, the server can evaluate whether the request pattern makes sense — unusual timing, impossible concurrency, geographic anomalies. These checks are invisible to client-side modification.

The broader principle is defence in depth applied correctly: each layer should provide independent assurance, not merely the appearance of it. Obfuscation that genuinely slows analysis. Cryptographic verification that happens where the attacker cannot reach it. Remote calls whose destinations cannot be silently redirected.