ProductionReady
productionready
FROM PROTOTYPE TO PRODUCTION
← Blog

Hardcoded API keys, client-side auth, and other things Claude doesn't warn you about

By Herbert Samuels · Founder of ProductionReady.co. Thirty years in software development, architecture, cloud infrastructure, and IT management across marketing technology, advertising, healthcare, and financial services.

Claude can produce very good application code. Clean structure, sensible components and working features are common in the projects we review.

Security issues usually appear around the assumptions that were never made explicit in the build request.

A prompt may describe the feature in detail without saying anything about credential handling, server-side authorization, input validation, request throttling or audit logging. Claude solves the feature problem it was given, and those surrounding controls can remain absent.

The patterns below are the ones we see most often in Claude-built applications, along with the reason each one matters and the practical remediation.

Hardcoded API keys

This is the most common finding across every AI-built app we audit, and it's one of the most dangerous.

When you tell Claude to connect your app to Stripe, or OpenAI, or a database, it needs credentials to make that connection. Claude will generate the code, and the credentials will be right there in the source file. Sometimes as a constant at the top of the file. Sometimes embedded in a configuration object. Sometimes as a default fallback alongside a process.env reference, which gives the appearance of security while still exposing the key.

Claude doesn't warn you about this because, from its perspective, it solved the problem you described. You asked it to connect to Stripe. It connected to Stripe. The key is in the code because the code needs the key to work, and putting it directly in the file is the simplest way to make it work.

Move every credential to environment variables, add the .env file to .gitignore, and never commit secrets to the repository. If a live credential has already been committed, rotate it immediately; deleting it from the current file doesn't remove it from git history.

Client-side authentication

When you ask Claude to add user authentication, it will. You'll get a login page, a registration form, maybe even a forgot-password flow. It will look complete and function correctly in testing.

But look at where the authentication logic runs. In many Claude-built apps, critical auth checks happen in the browser: in React components, in client-side route guards, in JavaScript that decides whether to show or hide content based on a locally stored token.

The issue is that client-side logic is advisory, not enforceable. A user can open browser developer tools, modify the JavaScript, change a token value, or simply call the API endpoints directly. If the server doesn't independently verify authentication and authorization on every request, the client-side checks are decorative.

We frequently find apps where the entire admin panel is hidden behind a client-side role check, but every admin API endpoint responds to any authenticated user who knows the URL. The UI says you can't access it. The server says you can.

No server-side validation

Claude generates excellent frontend validation. Form fields that require email format, password strength indicators, character limits on text inputs, dropdown menus that constrain options. From a user experience perspective, it's thorough.

From a security perspective, it's irrelevant.

Server-side validation (checking that incoming data meets expected formats, types, lengths, and constraints before processing it) is almost always absent in Claude-built applications. The server receives whatever the client sends and processes it without question.

This means an attacker bypassing the frontend (using a tool like Postman, curl, or a browser's network tab) can send anything they want to any endpoint. Strings where numbers are expected. Megabyte-sized payloads where a name field should be. Script tags where plain text is expected. The application will attempt to process all of it.

Missing rate limiting

Claude doesn't add rate limiting unless you specifically ask for it. This means every endpoint in your application will process unlimited requests, at any speed, from any source.

The immediate risk is brute-force attacks on authentication endpoints, an attacker trying thousands of password combinations automatically. But the exposure is broader. Without rate limiting, an attacker can scrape your entire database through a listing endpoint, consume server resources by triggering expensive operations repeatedly, or abuse any functionality that costs you money on a per-request basis (like API calls to OpenAI or Stripe).

Rate limiting is typically a few lines of middleware configuration. It's one of the highest-impact, lowest-effort security controls available. Its absence in AI-generated code is especially striking because it's so easy to add and so consequential to skip.

Overly permissive CORS

When Claude builds an API backend, it usually configures CORS (Cross-Origin Resource Sharing) to allow requests from anywhere: Access-Control-Allow-Origin: *. This is the permissive default that makes development easy: your frontend at localhost:3000 can talk to your API at localhost:4000 without any configuration friction.

In production, this means any website on the internet can make authenticated requests to your API on behalf of your users. If a user is logged into your application and visits a malicious website, that website can silently interact with your API using the user's session.

Proper CORS configuration restricts allowed origins to your actual frontend domain. It's a one-line change, but it's a one-line change that Claude won't make because it doesn't know your production domain and the permissive default never causes visible problems during development.

No audit logging

Claude-built applications typically log errors (when explicitly handling them) and nothing else. There's no record of who logged in and when, who accessed what data, who modified or deleted records, or what administrative actions were taken.

For applications handling sensitive data, this is an operational concern, not just a security one. When something goes wrong (and eventually it will), you need to reconstruct what happened. Without audit logging, you're left guessing. In regulated industries, the absence of audit logs is a compliance violation, not just a gap.

Why these omissions happen

Claude is an extraordinarily capable language model. It generates code that matches patterns in its training data, and most of that training data consists of tutorials, documentation, and example code, material that prioritizes clarity and functionality over production security.

This behavior reflects what Claude is optimized to do. It's highly capable at implementing requested functionality. Controls such as authentication hardening, validation, rate limiting, CORS restrictions and logging are production-engineering concerns that may never be mentioned in the feature prompt.

LLM-based security scanners are getting better at catching some of these. A scanning tool can flag a hardcoded API key or a missing helmet header. What it can't do is look at a client-side auth check and reason about whether the server trusts it, or trace a CORS misconfiguration through to its actual blast radius given the application's session model. That kind of judgment requires understanding how the pieces fit together, not just whether individual pieces follow a known-bad pattern.

That's why an AI-assisted build should include an explicit production review. The code can be clean and functional while still assuming that important security boundaries exist somewhere else.

A practical review list

For a Claude-built application that's already handling real users or real data, start with six checks:

Search your codebase for hardcoded credentials and move them to environment variables. Check whether authentication and authorization checks happen server-side, not just in the frontend. Add input validation to every API endpoint. Implement rate limiting on authentication and any expensive operations. Restrict CORS to your actual domain. Add basic audit logging for authentication events and data modifications.

Most of these changes are technically routine. The important part is making them an explicit part of delivery instead of assuming they will appear automatically.

Free Production Readiness Check

See where your app stands in 5 minutes.

16 questions across 8 categories: authentication, secrets, data layer, API security, and more. Get a scored report with prioritized recommendations.

ProductionReady
productionready
FROM PROTOTYPE TO PRODUCTION
Security audits, hardening, and backend builds for vibe-coded applications.
Company