Quantum-Safe Migration for your NGINX Web Application
I spent the past 16 weeks expanding pQCee's SafeQuard offering to support post-quantum end-to-end encryption for NGINX web applications.
Why TLS is not enough?
Man-in-the-middle (MITM) interception
This "man" could be more than the hacker that first comes to our mind. It could be an organisation, or a state which has mandated the use of a root certificate on your device. Such certificates can be installed when:
- an organisation pushes a Certificate Authority (CA) through Windows Group Policy or MDM on managed laptops/phones;
- an antivirus/security suite installs a local inspection CA;
- a government requires manufacturers or telecom providers to ship devices with a particular CA;
- malware gains sufficient privileges to alter the trust store;
- a modified OS/browser image already includes that CA.
In such cases, though you might see an icon in your browser suggesting an encrypted connection, the third party which holds the private key to the root certificate can still terminate TLS, i.e. intercept, decrypt and read the contents of your traffic before re-encrypting and forwarding it to your intended destination.
When Content Delivery Networks (CDNs) are used to serve content
This scenario is similar to the above. Your traffic is intercepted, decrypted, and read by the CDN before it is re-encrypted and forwarded upstream. In this situation, the CDN becomes your single point of trust: not only must it handle your data responsibly and securely, it must also be immune to any data breach.
To maintain a zero-trust architecture, data should remain encrypted by a separate protocol even after TLS is terminated at the CDN.
So I gave myself a challenge to build an application-level security protocol
These ensure that the solution meets standards concerning modularity, quantum resilience and customer experience.
The solution must implement quantum-safe end-to-end encryption
This can be accomplished by incorporating post-quantum cryptography (PQC) algorithms such as MLKEM. Unlike standard key-exchange protocols like RSA and ECDH, MLKEM is resistant to cryptanalysis by quantum computers. Such algorithms offer defense against Harvest-Now-Decrypt-Later (HNDL) attacks, i.e. even if the encrypted data is collected today, it cannot be decrypted by a quantum computer in the near future.
For the cryptographic operations, I use the existing SafeQuard libraries offered by pQCee. They offer fuss-free access to those PQC algorithms, allowing me to focus on other technicalities of the project.
Both a) request traffic leaving the client and b) response traffic leaving NGINX must be encrypted by the protocol.
When used in tandem with HTTPS, this provides defense-in-depth. Even when TLS is terminated (as seen in the CDN example at beginning of the article), the data remains encrypted by this protocol. This protocol remains immune to TLS termination spying (be it via MITM or a compromised CDN), as encryption will happen before entering the TLS tunnel, and the keys are possessed by only the client and NGINX.
The protocol must decrypt incoming traffic received on both client and NGINX.
All incoming traffic, with decryption handled by the protocol, should be ready for the client and server to consume as-is. This removes the need for the client and app server to maintain their own bespoke decryption logic.
All components should be drop-in, requiring minimal to no modification to the application source code.
The goal is to make the solution work with as many NGINX webapp deployments as possible, without impacting the customer experience.
There will be edge cases where the server component is running, but the client component is either outdated or yet to be retrieved. There must exist some logic to handle cases like these, such that the protocol does not break (e.g. server encrypts data but the client cannot decrypt it).
Here is how I implemented SafeQuard on NGINX to secure a user login operation:
This video shows what happens behind-the-scenes during a login operation. It compares what is visible to an intermediary before SafeQuard is used, to what is visible after SafeQuard is implemented.
Before implementing SafeQuard
Transmitted data (.html files and .json messages) is not secured with quantum-safe encryption. An intermediary with the right tools can harvest and read it.
After implementing SafeQuard
All transmitted data is now secured with quantum-safe encryption.
Some implementation details
The client-side component is the SafeQuard frontend Javascript module served by NGINX. This component handles the request and response data before they leave or as they reach the browser, then performs the necessary encryption/decryption operations on the HTTP body in accordance to the protocol.
The server-side component is a bespoke SafeQuard NGINX module, written in C and packaged as a .so file. Using a mixture of custom NGINX structs, handlers and filters, it injects itself into NGINX's request and response processing pipelines to perform key management, encryption and decryption operations matching those of the SafeQuard frontend component.
To provide more flexibility to the NGINX application, I added encryption for a specific endpoint to be toggled on or off with a single “safequard” directive in the nginx.conf file:
Final words/personal thoughts
Robustness, modularity and complexity had to be constantly juggled while building this solution. Making SafeQuard work seamlessly across various deployments means multiplying the amount of use cases. Additional components may have to be introduced to ensure these new use cases perform as expected, which then risks complicating onboarding and maintenance.
With a novel, component-based project like this, there are no comprehensive tutorials or existing counterparts to refer to. What a component does, how it interacts with other components - these are decisions that have to be planned thoroughly, as they have the power to tilt the aforementioned balance of robustness, modularity and complexity.
There is also the precarity of building a custom C module for NGINX. These modules interface directly with NGINX's internal functions, data structures and pipelines; all of which have to be thoroughly understood. Any improper function/data structure usages, off-by-one errors, buffer overflows or poor memory management can cause the entire NGINX process to crash silently. During testing and debugging, all execution flows need to be thoroughly traced with the help of NGINX's logging capabilities to ensure that the solution works as expected.
Alas, these challenges are merely part and parcel of building something novel yet scalable; something which addresses the threats of TLS termination spying and HNDL attacks while remaining versatile enough to be used across various deployments. I am excited to see how much further this project can be taken.
Author
Chua Xing Han
Xing Han is a Software Engineer Intern at PQCee. Once a fiercely competitive racer who dreamt of going professional, he is now an aspiring polymath in philosophy, the arts, and of course software engineering and motorsports. In whatever he does, he finds joy in trying new things, understanding difficult concepts, and passing on his knowledge.