Skip to main content

Application-Layer Payload Encryption: Why HTTPS Isn't Enough for Fintech & How Framework M Solves It

· 7 min read
Revant Nandgaonkar
Maintainer of Framework M

Picture this scenario: Your team has built a high-velocity web application on a modern async stack. You have enforced strict HTTPS/TLS across all routes, enabled HSTS, configured HttpOnly SameSite session cookies, and set up a Web Application Firewall (WAF). Everything looks rock-solid.

Then the external security audit report lands on your desk with a high-severity finding:

"Sensitive credentials and personally identifiable information (PII) including email and phone numbers are transmitted as clear-text JSON payloads across the application boundary. Authentication and mutating payloads should follow banking-grade Application-Layer Payload Encryption (ALPE)."

At first, engineers often push back: "Isn't TLS already encrypting traffic over the wire?"

In this article, we'll examine why HTTPS alone is insufficient for modern zero-trust architectures and strict compliance standards (PCI-DSS 4.0, SOC 2, HIPAA), the hidden traps of hand-rolling client-side encryption, and how Framework M delivers seamless, end-to-end Application-Layer Payload Encryption with zero extra network round-trips.


The Illusion of TLS: Where Cleartext Payloads Leak

TLS (Transport Layer Security) encrypts packets traveling across the public internet between a user's browser and your infrastructure.

However, in real-world enterprise deployments, TLS almost always terminates at your outer network perimeter:

  • Edge Ingress & Reverse Proxies: (e.g., Cloudflare, AWS CloudFront, Envoy, Nginx) terminate TLS and inspect or forward unencrypted HTTP bodies internally.
  • Web Application Firewalls (WAFs) & API Gateways: parse JSON payloads in memory to scan for injection patterns.
  • Application Performance Monitoring (APM) & Log Aggregators: (e.g., Datadog, Sentry, CloudWatch, Kibana) capture request bodies, stack traces, and access logs that can accidentally store clear-text passwords, PANs, phone numbers, or emails.
  • Internal Service Meshes & Sidecars: forward clear-text HTTP traffic between internal pods unless strict internal mTLS is configured everywhere.

TLS Termination vs Application-Layer Payload Encryption

If an ingress layer, log pipeline, or reverse proxy is misconfigured or compromised, all sensitive request payloads are exposed in plain text.

To prevent this, high-security organizations (government institutions, financial institutions, and core banking platforms) implement Application-Layer Payload Encryption (ALPE): data is encrypted inside the client application memory before network dispatch and is decrypted only inside the application process memory.


The Hand-Rolled Crypto Trap

When teams attempt to implement ALPE from scratch, they typically run into three major engineering roadblocks:

1. The Per-Request Handshake Penalty

Many custom implementations perform a cryptographic handshake before each request (e.g. requesting a single-use token or session key). This doubles the round-trip latency (2× RTT) for every single API mutation, degrading the user experience on mobile networks.

2. Key Desynchronization & Rotation Failures

If the backend key rotates while a client has a page open, subsequent requests fail with decryption errors unless a sophisticated multi-key set and retry protocol is established.

3. API Contract & Developer Experience Breakdown

Custom encryption usually breaks standard OpenAPI/Swagger generators, type-safe RPC clients, and automated testing suites because request bodies become opaque binary blobs that controllers must manually parse.


How Framework M Solves ALPE (Zero Cliff Architecture)

In Framework M, we designed payload encryption as a native, framework-level concern spanning both the Backend ASGI Request Pipeline (framework-m-standard) and the Frontend Client SDK (@framework-m/desk).

Framework M Application-Layer Payload Encryption Architecture Flow

1. Zero Extra Round-Trips via Cached JWKS (RFC 7517 & RFC 7638)

Instead of performing an expensive per-request handshake, Framework M exposes a centralized, cacheable JSON Web Key Set endpoint:

GET /api/v1/security/jwks

The response includes the server's public key with standard RFC 7517 parameters and an RFC 7638 deterministic SHA-256 thumbprint kid:

{
"keys": [
{
"kty": "RSA",
"alg": "RSA-OAEP-256",
"use": "enc",
"kid": "bL-M63aBf5X9t8e2L4k_12rQ...",
"n": "joM6jDmrPOxCS-xFYbLgWLbYchkbo1...",
"e": "AQAB"
}
]
}

The client SDK fetches this key set once upon application initialization and caches it with HTTP max-age=86400. Subsequent API mutations encrypt data instantly using the browser's native window.crypto.subtle with 0ms extra network latency.

2. Standard JWE Hybrid Encryption

Client payloads are encrypted using standard JSON Web Encryption (JWE):

  1. Client generates an ephemeral 256-bit AES key.
  2. The payload is encrypted with AES-256-GCM (providing authenticated confidentiality and integrity).
  3. The AES key is wrapped with the server's RSA public key using RSA-OAEP-256.
  4. The resulting JWE envelope is sent with Content-Type: application/jose.

3. Transparent Backend Pipeline (Priority 15 Middleware)

On the server side, PayloadDecryptionMiddleware executes in the ASGI pipeline at Priority 15 (right after rate limiting, but before CSRF, session evaluation, and controller dispatch).

The middleware unwraps the AES key, decrypts the payload in memory, and replaces the raw ASGI request stream with standard JSON bytes. Downstream controllers (such as Pydantic LoginRequest models) process validated objects without needing a single line of custom crypto code:

# Controller receives normal Pydantic model — zero decryption boilerplate required!
class LoginRequest(BaseModel):
email: str | None = None
password: str

Progressive Security: The 4-Tier Mode Strategy

Framework M adheres to the philosophy of Progressive Complexity: "Start Indie, Scale Enterprise without a rewrite."

Security is configurable via framework_config.toml or environment variables across four distinct operational modes:

ModeBehaviorIdeal Environment
disabled (Default)No payload encryption. Standard JSON over HTTPS. Full inspectability in Chrome DevTools.Local development (m dev), CI/CD unit tests.
selectiveAutomatically encrypts high-risk Auth & Identity routes (/auth/login, /auth/otp, etc.) + configurable custom routes.Standard SaaS applications satisfying external security audits.
metadataSchema-driven: Encrypts payloads for DocTypes or Fields flagged with json_schema_extra={"encrypted": True}.Applications handling targeted PII (SSNs, PANs, patient records).
strictEnforces 100% payload encryption across all mutating (POST, PUT, PATCH) endpoints globally.Core Banking, Fintech, and Zero-Trust Enterprise environments.

Configuration Example

Option A: framework_config.toml

[security.payload_encryption]
mode = "selective"
routes = [
"/api/v1/auth/login",
"/api/v1/auth/register",
"/api/v1/payments/charge", # Custom app route
]

Option B: Environment Variables

export FRAMEWORK_M_PAYLOAD_ENCRYPTION_MODE="selective"
export FRAMEWORK_M_PAYLOAD_ENCRYPTION_ROUTES='["/api/v1/auth/login", "/api/v1/payments/charge"]'

Universal Cross-Platform Support

Because Framework M's client utilities rely on the standard W3C WebCrypto API (crypto.subtle), the same client tools work across all modern JavaScript runtimes:

1. React Web & Desk Apps

import { fetchWithCredentials } from "@framework-m/desk";

// Automatically checks security policy and encrypts body before fetch
await fetchWithCredentials("/api/v1/auth/login", {
method: "POST",
body: JSON.stringify({ email: "user@fintech.com", password: "SecretPassword123!" }),
});

2. Node.js BFFs & Backend Microservices

Node.js 18+ includes native globalThis.crypto.subtle and globalThis.fetch. Node developers can import fetchWithCredentials or encryptPayload directly:

import { fetchWithCredentials } from "@framework-m/desk";

await fetchWithCredentials("https://api.domain.com/api/v1/kyc/verify", {
method: "POST",
body: JSON.stringify({ ssn: "000-12-3456" }),
});

3. React Native & Mobile Apps

In React Native (Expo SDK 49+ / RN 0.70+), standard WebCrypto bindings allow mobile banking apps to encrypt credentials directly on the mobile device before sending packets over cellular networks or public Wi-Fi.


Conclusion: Start Indie, Scale to Banking

Security compliance shouldn't force development teams to rewrite their API architectures or sacrifice developer velocity.

With Framework M:

  • During local development, developers retain cleartext inspectability in Chrome DevTools.
  • In production, flipping mode = "selective" or mode = "strict" enables banking-grade Application-Layer Payload Encryption without altering a single business controller or frontend component.

Build once. Scale forever. Secure by default.