Ask an AI to write a web app without mentioning security, and you get code with hardcoded passwords, forgeable tokens, and wide-open access to other people's data — but just tacking a ready-made security requirements block onto your prompt can wipe out every critical and high-severity vulnerability in the output. A researcher generated six different web apps (a blog, an online store, a file-sharing tool, and others) two ways — a plain request and the same request plus a security section — then compared the results.
By default, a model writes code that works, but it doesn't think about security on its own: passwords stored in plain text, secret keys baked right into the code, a JWT token signed with a hardcoded password — meaning anyone can forge someone else's session and hijack their account. This isn't because the model "can't" write secure code — it's because, without being explicitly asked, it simply doesn't treat security as part of the job.
The fix turned out to be almost laughably simple: append a ready-made list of requirements to the end of the prompt (check access on every resource, hash passwords, pull secrets from environment variables, use parameterized database queries, and so on) — all in a single pass, with zero iteration, zero review, zero fixes. The versions with this block ended up with zero critical or high-severity vulnerabilities — versus two critical and eleven high-severity ones in the plain versions.
Method Blueprint
STEP 1: Describe the app as usual (features, pages, data) → plain prompt
STEP 2: Append a fixed security-requirements block to the end (see template below) → security prompt
STEP 3: Send both versions to the same model in one shot, no edits → compare the results
Both steps happen inside a single prompt — no back-and-forth or multiple requests needed.
Example use case
Task: A small business owner with no coding experience asks Claude or ChatGPT to build a site with an inquiry form and an admin panel, where a manager can see all inquiries and mark them as resolved.
Prompt:
Write a Python (Flask) web app for collecting inquiries from a website.
Features:
- a form on the site where the customer enters name, phone number, and inquiry text
- a login page for the manager (username and password)
- an admin panel where the manager sees all inquiries and can mark them "in progress" or "closed"
- a database for storing inquiries and users
Additionally, follow these security requirements during development:
- Check access permissions on every request: a regular user must never see the admin panel.
- Protect forms and any data-changing actions against request forgery (CSRF).
- Store passwords only as salted hashes (bcrypt), never in plain text.
- Store all passwords, keys, and secrets in environment variables — never hardcode them.
- Use parameterized database queries and validate all user input.
- Restrict file uploads (if any) by type and size, and never trust the client-supplied filename.
- Disable debug mode and detailed error messages.
- Configure session cookies with HttpOnly, Secure, SameSite flags, and limit token lifetime.
- Generally follow the OWASP Top 10 web application security guidelines.
Result: The model produces fully functional app code where passwords are hashed, secrets aren't baked into the files, forms are protected against CSRF, and the manager's sessions have a limited lifetime. According to the study, not established; one model, one run per app like "forged token = access to someone else's account," but minor configuration details (security headers, server run permissions) still need separate review — the prompt doesn't cover those.
Why this works
A model optimizes for code that works — and "works" and "is secure" are different goals. Hardcoding a password into a JWT token or storing passwords in plain text both "work" too — just badly.
Where the model really shines is at following explicit written instructions. When you spell out "store passwords only as a hash" or "read secrets from environment variables," the model does exactly that, because it's a concrete, unambiguous requirement rather than a vague "make it secure."
This method leverages that strength: instead of asking the model to "think about security" (way too fuzzy), you hand it a concrete checklist. A single explicit list of requirements, with zero edits after generation, turned out to be enough to eliminate the most dangerous categories of problems.
The control lever: the requirements list can and should be adapted to your specific task. Building a public API? Add a line about rate limiting. Handling payments? Add a line about logging financial transactions. The closer the list matches your actual scenario, the more complete the coverage.
Prompt template
{App description: what it does, what pages and features it has, what data it stores}
Additionally, follow these security requirements during development:
- Check access permissions on every request — a user can only see and modify their own data; everything else is denied by default.
- Protect forms and any data-changing actions against request forgery (CSRF).
- Store passwords only as a salted hash (bcrypt or equivalent), never in plain text.
- Store all passwords, keys, and secrets in environment variables — never hardcode them.
- Use parameterized database queries, and validate and sanitize all user input.
- Restrict file uploads by type, size, and save path — never trust the client-supplied filename.
- Disable debug mode and detailed error messages; show only generic error messages.
- Configure session cookies with HttpOnly, Secure, SameSite flags, and limit token lifetime.
- If the app makes requests to a user-supplied URL, validate and restrict it to an allow-list of addresses.
- Generally follow the OWASP Top 10 web application security guidelines.
Drop your task description into the first part — the rest of the block is universal and works unchanged for any web app.
🚀 Quick start — paste this into the chat:
Here's a prompt template for secure code generation. Adapt it to my task: {your task}.
Ask me questions to fill in the fields.
[paste the template above]
The LLM will ask about your app's specifics (payments, file uploads, a public API) — because that determines which checklist items matter most and what needs to be added on top.
Limitations
⚠️ Doesn't eliminate everything: minor configuration issues (security headers, server run permissions) remain — the checklist says nothing about deployment; that's a separate area of responsibility.
⚠️ Tested on a single generation, no back-and-forth: there's no data on what happens if you ask the model to revise the code after critique — iterative fixes might improve things further, or might make them worse (in other studies, repeated edits sometimes introduced new vulnerabilities).
⚠️ Tiny sample size: six apps, one model, one run per variant. This is an observation, not a statistic — you can't claim "reduces risk by X%," only "this is what happened in this experiment."
⚠️ A prompt doesn't replace review: even with the checklist, the most serious vulnerability (a forged token → full account takeover) was found only through manual testing — no automated scanner caught it. For real production use, especially anything involving money or personal data, you still need human review on top.
How it was studied
The researcher generated six different apps (a blog, a task API, a file-upload tool, a store with comments, a form with link previews, an admin panel) — each one twice: once with a plain prompt, once with the security block added. That's 12 programs total, all written by the same assistant (Claude Code) and the same model, in a single pass with no edits. Each program was checked four different ways: static code analysis, dependency scanning for known vulnerabilities, dynamic scanning of the running app (OWASP ZAP), and manual testing of hacker-style attack scenarios.
Result: 75 confirmed vulnerabilities out of 85 candidate findings. The plain versions had 51 findings, the security-prompt versions had 24 — and more importantly, zero critical or high-severity issues in the secured versions (versus 2 critical and 11 high-severity in the plain ones).
The most interesting part: the most serious hole — token forgery for account takeover — was found only by hand, and no scanner caught it. Meanwhile, the flashiest automated finding (a SQL injection flagged by the scanner) turned out to be a false positive. This makes the point plainly: you can't rely on a single tool — you need to combine methods of verification — exactly the same way you can't rely on a single prompt without reviewing the code it produces.
Resources
Darko Andročec, Vibe Coding and Web Application Security: A Twin-Prompt Study, University of Zagreb Faculty of Organization and Informatics, accepted at CECIIS 2026 (37th Central European Conference on Information and Intelligent Systems). References: OWASP Top 10 (2021), Claude Code (Anthropic); the term "vibe coding" was popularized by Andrej Karpathy (2025).