The Invisible Ink: How We Turn Your Hidden Fields into Open Doors
You developers, you’re a clever bunch. You build complex web applications, beautiful user interfaces, and intricate databases. But then you do something so lazy, so fundamentally naive, it’s like leaving the key under the doormat. You use hidden fields.
You think because a user can’t see the data on the screen, it’s secure. You use these invisible fields to store information you need later a product price, a user’s role, a discount code. You see it as a convenient way to maintain state between pages.
We see it as a gift-wrapped invitation. You think it’s hidden? To us, it’s written in giant, glowing letters right in the page source. Let me give you some “guidance” on how we turn your lazy shortcuts into our powerful backdoors.
What You Hide, We Find
First, a lesson for the amateurs. A hidden field in your HTML looks something like this:
<input type="hidden" name="product_id" value="12345">
The type="hidden"
attribute simply tells the browser not to display the field. That’s it. It does absolutely nothing to secure the data. The first thing any half-decent attacker does when they land on your page is right-click and “View Page Source” or open the browser’s developer tools. We see everything. All your carefully “hidden” data is laid bare for us to read and, more importantly, to modify.
Never, ever assume that information sent to the browser is safe just because it’s not visible. We own the browser. Anything that happens there is under our control, not yours.
The Classic Blunders: Price and Privilege
Misusing hidden fields leads to some of the most common and devastating vulnerabilities. We see them so often, it’s almost boring. Here are our two favorites.
1. Price Manipulation
Imagine you’re on an e-commerce site. You add a shiny new laptop to your cart and proceed to the checkout page. You don’t see the price field, but we do. We open the developer tools and find this little gem in the form that gets submitted when you click “Pay Now”:
<input type="hidden" name="item_price" value="1500.00">
<input type="hidden" name="currency" value="USD">
The developer’s logic was simple: pass the price from the product page to the payment page. Their mistake was trusting the user’s browser to be honest. What happens next is obvious. We use the browser tools to edit the value
attribute before submitting the form.
We change value=”1500.00″ to value=”1.00″.
We click “Pay Now.” The form submits the tampered data to your server. If your server-side code is as lazy as your front-end code, it won’t re-verify the price of the laptop against its own database. It will simply see a request to process a payment for $1.00. The payment goes through, and we just bought a $1500 laptop for the price of a candy bar. This is parameter tampering 101.
2. Privilege Escalation
This one is even more damaging. Let’s say you’re building a web application with different user roles—customers, managers, and administrators. When a user logs in, you might try to store their role in a hidden field to manage their session.
On a user’s profile page, we find this:
<input type="hidden" name="user_role" value="customer">
Our eyes light up. This is a developer telling us exactly how their security model works. The temptation is irresistible. We edit the field:
Change value=”customer” to value=”admin”.
We submit the form by clicking “Save Profile.” If the server doesn’t have a secure, session-based check to verify our actual permissions, it might just believe the data it’s sent. Suddenly, our account is an administrator. We have privilege escalation. We can now access the admin dashboard, view all users, delete data, and wreak havoc, all because you decided to trust a hidden field.
A Clear Guidance for You (If You’re Listening)
You want to stop us from doing this? The guidance is painfully simple, yet so many of you ignore it.
- NEVER TRUST THE CLIENT. Ever. Any data that comes from a user’s browser whether it’s from a visible field, a hidden field, a cookie, or a URL parameter can be and will be manipulated.
- VALIDATE EVERYTHING ON THE SERVER. When a user submits a purchase form, your server should ignore whatever price the form sends. It should look up the product ID in its own database and use the price stored there. When a user performs an action, the server should check their role from a secure, server-side session, not from a field they submitted.
- STATE SHOULD BE MANAGED ON THE SERVER. Don’t use hidden fields to pass critical data between pages. Store it in the user’s session on your server, where we can’t touch it.
Keep using hidden fields for non-critical data if you must. But if you put anything important in there prices, roles, identities, secrets you’re not hiding it. You’re just leaving it on the front lawn with a sign that says “free to a good home.” And we will always be there to pick it up.