October 20, 2026
Your Repo Scanner Is Green. Your Production DOM Is Leaking Live Keys.
Open any modern web application in your browser, right-click the page, and view the raw HTML source. Scroll past the meta tags and…
By bhupesh patil
6 min read
Open any modern web application in your browser, right-click the page, and view the raw HTML source. Scroll past the meta tags and stylesheet links until you hit the bottom of the document. If the application uses server-side rendering with React, Next.js, Remix, or a hydrated Redux store, you will almost certainly find a massive inline
Sometimes that blob holds harmless UI state, like a user's theme preference or a list of product IDs. Other times, buried three levels deep inside an initial configuration object, sits a live Stripe secret key, an internal microservice bearer token, or an AWS session credential.
If you check the Git repository for that exact application, you will find a clean commit history. The engineering team has pre-commit hooks installed. Their pull requests run through automated secret scanners on every push. Every static check in their CI pipeline shows a green checkmark.
I spent Saturday at BSides New York, and without planning my schedule around a single track, three separate sessions walked through this exact blind spot from different angles. We have spent the last five years hardening the static perimeter of software development and authentication. We rolled out MFA and passkeys to lock down the login screen, and we deployed shift-left scanners across every source repository. In response, the actual exposure quietly migrated to runtime.
Here is how that shift plays out across authentication, frontend architecture, and secret discovery, along with what I am changing in my own workflow as a result.
1. Authorization Phishing: Why Steal a Password When You Can Ask for a Token?
For years, the defensive playbook against phishing focused on the login prompt. Security teams pushed hardware security keys, FIDO2 passkeys, and phishing-resistant MFA to ensure that even if an employee clicked a malicious link, an attacker running a reverse-proxy toolkit could not replay their credentials.
Luke Jennings opened the thread for me during his session on authorization phishing, breaking down device code phishing, ConsentFix, and OAuth abuse. When defenders made the primary login ceremony too expensive to compromise, attackers stopped trying to steal passwords. Instead, they moved one step later in the lifecycle and targeted the authorization grant.
In a device code phishing flow, the attacker initiates a login request on a legitimate identity provider and generates a short user code. They send a message convincing the target to visit Microsoft or Google's real verification URL and type in that code. Because the domain is legitimate, the user's browser and password manager see nothing suspicious. Their passkey or MFA prompt fires normally. Once the user approves the prompt, the identity provider hands a valid OAuth access token and refresh token directly to the attacker's waiting session.
ConsentFix pushes this further by abusing legitimate OAuth redirect flows. A user signs into a real first-party application through their normal, hardware-backed authentication flow, and the attacker tricks them into copying a localhost redirect URL or authorization code generated after login. No password ever crosses the wire to an untrusted host. Every pre-authentication control functions exactly as designed, yet the attacker walks away with persistent API access to the user's account.
Our monitoring pipelines are built to flag anomalous login attempts, impossible travel on authentication events, and brute-force password sprays. Very few teams monitor post-authentication OAuth token grants or inspect which client applications are quietly minting refresh tokens after a clean passkey login.
2. SSR State Hydration and the Build-Time Injection Gap
Later in the day, Hemanth Gorijala gave a talk on runtime secret exposure that hit close to home for me. Because my background is in software development, I have watched teams wire up environment variables, Redux stores, and server-side state objects dozens of times under deadline pressure. When you are debugging a broken staging deployment, your primary goal is getting data from the server into the client component tree so the page renders cleanly.
Looking at that exact same architecture through an attacker's lens changes the picture completely.
Shift-left security tools inspect what developers commit to version control. Those scanners have zero visibility into what your infrastructure actually serves to a browser after the build pipeline finishes running.
Consider how a typical full-stack JavaScript deployment works in production:
- A developer writes clean code that references
process.env.INTERNAL_API_SECRETinside a server-side data fetching function or configuration module. - Because the secret itself lives in the CI/CD secret manager or Kubernetes secret store rather than in Git, every repo scanner passes the commit.
- During the CI/CD build step or at request time on the SSR server, the runtime environment injects the real secret into memory.
- A developer passes the entire configuration object or server response into the page props so a child component can read one public field from it, such as an API base URL or feature flag.
- The SSR framework serializes the entire props tree into the HTML document so the client bundle can hydrate state in the browser.
When any visitor requests the page, the server returns HTML that looks like this:
<!DOCTYPE html>
<html>
<head><title>Customer Portal</title></head>
<body>
<div id="__next">...</div>
<script id="__NEXT_DATA__" type="application/json">
{
"props": {
"pageProps": {
"userSession": { "id": "usr_9921", "role": "viewer" },
"appConfig": {
"apiBaseUrl": "https://api.example.com/v2",
"featureFlags": { "newCheckout": true },
"internalServiceKey": "sk_live_51Nz892JkL9012mPqR8812xYz",
"searchAdminToken": "algolia_admin_9981237491823"
}
}
}
}
</script>
</body>
</html><!DOCTYPE html>
<html>
<head><title>Customer Portal</title></head>
<body>
<div id="__next">...</div>
<script id="__NEXT_DATA__" type="application/json">
{
"props": {
"pageProps": {
"userSession": { "id": "usr_9921", "role": "viewer" },
"appConfig": {
"apiBaseUrl": "https://api.example.com/v2",
"featureFlags": { "newCheckout": true },
"internalServiceKey": "sk_live_51Nz892JkL9012mPqR8812xYz",
"searchAdminToken": "algolia_admin_9981237491823"
}
}
}
}
</script>
</body>
</html>No static scanner running on your GitHub organization can catch this leak, because the string _sk_live_51Nz892JkL9012mPqR8812xYz_ never existed in your source code. It was assembled at build time or runtime by the pipeline itself and handed directly to the public internet inside a <script>tag.
The same exposure happens when webpack or Vite performs build-time string substitution on environment variables, or when a Redux store gets hydrated with an unfiltered backend user object that includes internal tokens. Unless you are inspecting the compiled JavaScript bundles and the hydrated DOM as they actually render in a real browser session, you are blind to an entire class of credential leaks.
3. Moving Beyond Repo Scanning: Betterleaks, Common Crawl, and Kaggle
The day wrapped up with a hands-on secret scanning workshop led by Zach Rice and Joe Leon. Their premise matched everything from the earlier sessions: basic regex scanning against Git repositories is a saturated problem. Every major platform runs default checks for standard AWS keys and GitHub personal access tokens on push.
The real leverage now sits in two places: writing custom detection logic for proprietary token formats, and pointing scanners at non-obvious runtime data sources.
Getting hands-on time with Betterleaks during the workshop made it clear how much control you gain when you move past generic default rules. Every organization has internal bearer tokens, custom webhook signing keys, or niche SaaS credentials that follow predictable entropy and prefix patterns but never trigger off-the-shelf scanners. Writing custom rules tailored to your own architecture lets you catch internal credentials before an external researcher does.
Even more interesting was where Zach and Joe pointed their scanners. Instead of just scanning Git history, we watched a workflow query Common Crawl archive data and surface API keys that were still live and valid in production. Common Crawl continuously snapshots billions of web pages, storing the raw HTTP responses, headers, and rendered HTML payloads. When a web app accidentally leaks a secret via SSR hydration or a misconfigured debug response even for a few days, Common Crawl captures that runtime artifact.
During the session, they also mentioned running the exact same crawl-and-validate pipeline against public Kaggle datasets. Think about how data science and machine learning datasets get created in the real world. An engineer exports raw application logs, database tables, or scraped JSON payloads into a CSV or Parquet file and uploads it to Kaggle for a benchmark or competition. No one runs an AppSec secret scanner against a 4GB CSV file full of training rows, yet those rows routinely contain live session tokens, webhook URLs with embedded credentials, and internal API keys.
What I Am Changing in My Own Workflow
Walking out of BSides New York, the connection between all three sessions was impossible to ignore. Every time we lock down a static artifact, whether that is a password in a database or a source file in a Git repository, the exposure shifts toward live execution.
I am focusing on three practical experiments over the next few weeks:
First, I am building custom Betterleaks rules specifically tuned for non-standard token structures and internal configuration objects, and testing that crawl-and-validate workflow against public dataset corpora like Kaggle to see what kind of live artifacts show up in uninspected data formats.
Second, I am pairing static rule logic with passive runtime inspection in the browser using tools like SecretSifter. If a developer or security engineer can browse staging and production applications while a passive listener inspects every hydrated HTML response, Redux state tree, and dynamically loaded JavaScript chunk, you catch build-injected secrets the moment they hit the DOM.
Third, on the identity side, I am looking closer at how we log and audit post-authentication OAuth grants. Relying on passkeys at the front door solves credential stuffing, but we need detection rules around unusual device code flows and newly authorized OAuth client applications after a session is already established.
If your security posture relies entirely on pre-commit hooks and login-page MFA, take twenty minutes this week to inspect what your production servers actually render into the browser. Thank you to the BSides New York organizers, volunteers, and speakers for putting together a lineup that connected so cleanly to real engineering problems.