AI-generated web code fails in predictable ways: string-concatenated SQL, unencoded output, and permissive defaults. Better prompts help, but the fix we rely on is a control set that makes the insecure pattern unrepresentable. Roslyn analysers and banned-API rules turn raw SQL into a build error, and hooks check every edit. Continuous integration (CI) security gates and risk-weighted human review complete the set. For regulated UK teams, the same harness doubles as audit evidence for ISO 27001 secure development controls and for whatever assurance questions your sector regulator asks.
- Use prompts to steer the first draft, and deterministic controls to guarantee the merged result.
- Ban the escape hatches, so that raw SQL concatenation fails to compile and the model has no way to ship it.
- Gate CI on security findings from day one, before a backlog builds up.
- Aim human review at the areas tools cannot see: authorisation logic, trust boundaries, and abuse cases.
Our risks of AI-augmented development guide covers the wider risk set, and this guide covers the security controls in depth.
Teams worry about AI writing internet-facing code with customer data behind it
Teams moving desktop or internal systems onto the web with AI assistance tend to share one concern. How do we stop the model writing SQL injection, cross-site scripting, or sloppy state handling into code that will face the internet? For any organisation handling personal or commercially sensitive data, it is a fair question. It also comes up in supplier assessment, where the organisation’s own clients, auditors, and regulators will ask it.
The concern is well-founded when generation is unguarded. Security vendors’ published analyses of unguarded AI output consistently find injection and encoding weaknesses at high rates (Veracode’s analysis and Vidoc’s vulnerability teardown are representative examples). Models reproduce the most common patterns in their training data, and in decades of public code the most common patterns are often not the secure ones.
We do not think the answer is to keep AI away from web code. Instead, we treat security as a property of the pipeline the code passes through rather than of whoever wrote it, and the rest of this guide describes that pipeline.
Why are prompts and rules files not enough?
Prose guidance improves the average result, but it does not limit the worst case. In our experience a security section in CLAUDE.md (‘always use parameterised queries, always encode output’) does improve first drafts, and you should have one. An instruction is still only context, though, and context degrades. Long sessions, competing rules, and unusual code shapes all produce moments where the model emits a pattern its instructions forbid.
Our harness engineering guide draws a distinction between inferential and computational controls. Inferential controls (prompts, rules, model judgement) steer the model, while computational controls (compilers, analysers, tests, gates) guarantee an outcome. Security is a domain where guarantees count, so every security rule that can be enforced deterministically should be.
Making SQL injection unrepresentable
The strongest control is structural. All data access goes through parameterised stored procedures behind a repository layer, and the escape hatches are banned at compile time. We recommend this access pattern for performance as well as security, because query plans stay close to the engine and the application identity holds EXECUTE permissions only, with no direct table access. Even a misgenerated or compromised component then cannot run arbitrary SQL that the database will accept.
Layer rule. No type outside the data access project may reference Microsoft.Data.SqlClient. Enforce this with an ArchUnitNET test, so an agent that tries to bypass a repository with a direct connection breaks the build. Our tightly coupled code post shows the pattern.
Banned APIs. The BannedApiAnalyzers package flags dangerous members at compile time through a BannedSymbols.txt file. The file is checked into the repository and scoped to every project except data access:
T:Microsoft.Data.SqlClient.SqlConnection; Data access goes through the repository layer
T:Microsoft.Data.SqlClient.SqlCommand; Data access goes through the repository layerInside the data access project, one further convention completes the control. Every command invokes a named stored procedure (CommandType.StoredProcedure) with typed parameters, enforced by an architecture test or a small analyser, so string-built SQL has nowhere to live in the codebase.
With TreatWarningsAsErrors turned on (our .NET migration guide sets this up), a banned symbol fails the build. The compiler applies the rule in the same way to a model and to a developer working under time pressure.
Security analysers in the build. Enable the security rules in the .NET analyser set (AnalysisMode with security rules elevated to errors). Add SecurityCodeScan or an equivalent tool for taint-style findings, which trace untrusted input through to a dangerous call. The agent can read analyser output, so a PostToolUse hook that builds after every edit feeds each finding straight back. The agent then fixes it in the same session, rather than waiting for a security review three weeks later.
Making cross-site scripting hard to write
Razor encodes output by default, so the goal is to protect the places where developers (and models) opt out of that safety:
- Ban
Html.Rawoutside a sanctioned helper. Add it toBannedSymbols.txt, and provide one reviewed helper for the rare legitimate case that wraps sanitisation with a well-maintained library. Any diff that touches the helper gets senior review by policy. - Treat
dangerouslySetInnerHTMLthe same way in React front ends, with an ESLint rule set to error and one sanctioned wrapper component. - Set the headers centrally. A Content Security Policy,
X-Content-Type-Options, and frame options belong in middleware configured once, rather than in generated page code. Generated code cannot then leave them out, because they are set outside it. - Validate at the boundary. Use model binding with explicit view models, server-side validation as the source of truth, and anti-forgery tokens on state-changing endpoints. The agent applies these patterns consistently once CLAUDE.md names them and a code review skill checks for them.
The same approach covers the rest of the Open Worldwide Application Security Project (OWASP) Top 10. Choose the secure-by-default mechanism, centralise it, and ban the ways of opting out of it.
State, secrets, and configuration
Three further control areas need attention in a desktop-to-web transition specifically, because the legacy codebase was not written with web threats in mind.
State management. Desktop code trusts in-memory state, whereas web code must not trust anything the client can touch. The rules that need enforcing are no sensitive data in cookies or hidden fields, session state held server-side, and authorisation checked on every request rather than only at page entry. These rules are semantic rather than syntactic, so they become review-checklist items backed by tests.
Secrets. Deny rules in the agent configuration keep production configuration and secret files out of the agent’s reach entirely (our .NET configuration guide includes the deny list). Secret scanning in CI then catches anything that slips into a diff by another route.
Dependencies. Generated code pulls in packages, and choosing a package is a supply-chain decision. Pin the approved libraries for cryptography, sanitisation, and authentication in CLAUDE.md, put new package additions behind an ask-level permission, and run dependency scanning continuously. The governance framework for the agent-side supply chain is in governing the AI agent supply chain.
The pipeline: where each control runs
Each control runs at a different point, and the order is important because feedback closest to generation is the cheapest to act on:
- In-session (seconds). CLAUDE.md security conventions steer generation.
PostToolUsehooks run the build with analysers and banned-API rules after every edit, so violations are corrected in the turn where they occur. - Pre-merge (minutes). CI runs the full analyser set, security-focused tests, secret scanning, and a dependency audit, and new findings fail the build. Static analysis platforms (GitHub Advanced Security or equivalent) add taint tracking across the codebase, and our supported-software security guide covers that tooling layer.
- Review (risk-weighted). People review the areas where tools are blind: authorisation and access-control logic, trust boundaries, payment handling, and abuse cases. The blast radius of a change decides how deep the review goes. AI-generated code gets the same scrutiny as human code, which is a policy we hold across all our delivery (see why we do not let AI ship code unsupervised).
- Continuous (post-merge). Dynamic scanning against deployed environments and dependency monitoring continue after merge, because some classes of flaw only show up in a running system.
What does this give a regulated business, concretely?
It gives you evidence. Auditors, clients, and sector regulators ask a regulated UK organisation how it ensures the security of the software it runs, whoever or whatever wrote it. A harness of documented, automated controls lets you answer it with records:
- The analyser and banned-API configuration lives in source control, so you can prove the control exists and show its history.
- CI gate results and review records form an audit trail that maps onto ISO 27001 secure development controls. Our ISO 27001 and AI post covers the policy side.
- Provenance records for AI-generated changes support procurement and assurance conversations, as covered in AI code attribution for enterprise procurement.
- Supplier-side assurance over the model provider itself (terms, training exclusions, retention, residency) is a separate workstream, covered in the Claude GDPR guide.
An assurance that developers are careful with AI output gives an auditor nothing to inspect, while a gated pipeline produces records they can check.
Next steps
If AI-assisted delivery of web-facing code is on your roadmap, put the security harness into the environment before the first feature is built. That means banned APIs, analysers set as errors, hooks, CI gates, and a risk-weighted review policy. The setup cost is small, and it turns a difficult assurance question into one you can answer with evidence.
For the wider control framework, see harness engineering for coding agents and the risks of AI-augmented development. For the Claude Code project setup that the hooks, permissions, and CLAUDE.md conventions above sit in, see our Claude Code for .NET developers guide. If the trigger is a desktop-to-web modernisation, the WinForms to web guide covers the delivery side.
Want the harness reviewed or built? Book a free consultation.
Frequently asked questions
Is AI-generated code less secure than human-written code?
How do you stop AI writing SQL injection vulnerabilities?
What is wrong with relying on security prompts and rules files?
How does this map to ISO 27001 and regulator expectations?
Do we still need human security review?
Does generating code faster make us less secure?
Related guides
Is Claude GDPR Compliant? Anthropic Assurance for UK Businesses
How UK businesses get GDPR and ISO 27001 assurance over Anthropic's Claude: commercial terms, the DPA, certifications, EU data residency options, retention settings, and a supplier due diligence checklist.
Claude Code vs GitHub Copilot for .NET Teams: A UK CTO's Guide
Claude Code vs GitHub Copilot for .NET teams: autocomplete versus agentic delivery, what Copilot running Claude models changes, and how UK CTOs decide.
Claude on Azure: Choosing Between Claude and Azure OpenAI in Microsoft Foundry
Claude vs Azure OpenAI in Microsoft Foundry: what Claude's GA on Azure changes for procurement, governance, and residency, and how UK teams should choose.