Why AI-Generated Code Has a Secrets Problem — And How to Avoid It

Scott Fisher · ·8 min read

AI-assisted development has made building software faster than it has ever been. What used to take weeks can now be done in days. But speed has a shadow side: AI coding tools optimise for working code, not secure code. And the most common security mistake in software — hardcoded credentials — has become significantly easier to make.

This article covers how I handle credentials, secrets, and identity in the bespoke systems I build for UK businesses: Azure Managed Identity, certificate-based authentication, Key Vault, and encryption. None of it is exotic. All of it is standard practice that AI tools frequently skip.

The Problem: AI Coding Tools Default to the Insecure Path

Ask an AI to generate a .NET connection string and you will almost certainly get something like this:

Server=myserver.database.windows.net;Database=mydb;User ID=admin;Password=mysecretpassword123;

It works. It is also a security incident waiting to happen. That password is now in your source code. If your repository is on GitHub — even a private one — it is one misconfigured permission, one compromised account, or one accidental public push away from being exposed. Services like GitGuardian and GitHub's own secret scanning actively detect these patterns. Attackers actively look for them.

The "vibe coding" approach — generating code quickly and shipping it — produces this pattern constantly. Not because the AI is malicious, but because it is optimising for the shortest path to a working result. The developer is responsible for the security review the AI skips.

Azure Managed Identity: No Password to Steal

For most bespoke Windows applications connecting to Azure SQL, the right answer is Managed Identity — and it is simpler to set up than most developers expect.

Managed Identity gives your application an identity in Azure Active Directory without requiring any credentials to be stored anywhere. The application authenticates using that identity, and Azure handles the token exchange behind the scenes. There is no password to rotate, no secret to expire, and nothing to accidentally commit to source control.

The connection string changes from the insecure version above to:

Server=myserver.database.windows.net;Database=mydb;Authentication=Active Directory Default;

No password. No username. Azure resolves the identity automatically.

On the database side, you grant the Managed Identity the appropriate SQL role:

CREATE USER [MyApp-ManagedIdentity] FROM EXTERNAL PROVIDER;
ALTER ROLE db_datareader ADD MEMBER [MyApp-ManagedIdentity];
ALTER ROLE db_datawriter ADD MEMBER [MyApp-ManagedIdentity];

I use this pattern across all current client projects where the application is running in an Azure-managed environment. NTLabsManager, BuxtedManager, and DugardManager all authenticate to Azure SQL this way. The connection string in the config file contains no secrets whatsoever.

For local development, Active Directory Default falls back to the developer's own Azure credentials via the Azure CLI or Visual Studio sign-in — so the same connection string works in both environments without any modification.

Certificate-Based Authentication: When Managed Identity Is Not Enough

Managed Identity works well for cloud-hosted applications and Azure-adjacent workloads. It is less straightforward for applications running on end-user machines that need to authenticate to non-Azure services, or for machine-to-machine scenarios without a managed environment.

In these cases, certificate-based authentication is the right tool. Rather than a shared password, each client machine holds a certificate with a private key. The application presents the certificate to authenticate; the server validates it against the expected certificate thumbprint. No shared secret is transmitted.

BuxtedManagerV2 uses this pattern. Each client machine has a PFX certificate installed with its private key. The application loads it from the Windows Certificate Store at runtime:

var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var cert = store.Certificates
    .Find(X509FindType.FindByThumbprint, thumbprint, validOnly: true)
    .Cast<X509Certificate2>()
    .FirstOrDefault();

The thumbprint is stored in configuration — not the certificate itself, not the private key. If the configuration file is exposed, the attacker has a thumbprint and nothing else.

Certificates do expire. The operational discipline required is a renewal calendar reminder set at the point of installation — typically 90 days before expiry for a one-year certificate. I set these as standard for every deployment.

Azure Key Vault: For Everything That Cannot Use Managed Identity

Some secrets genuinely need to exist somewhere: third-party API keys, Stripe keys, SMS gateway credentials, OAuth client secrets. These cannot use Managed Identity because they are credentials issued by an external service, not Azure.

Azure Key Vault is the right place for them. Key Vault is a managed secrets store with access control, audit logging, and automatic rotation support for certain secret types. Secrets are never in source code, never in config files deployed to client machines, and never in environment variables that get logged accidentally.

In a .NET 8 application, Key Vault integrates cleanly with the standard IConfiguration system:

builder.Configuration.AddAzureKeyVault(
    new Uri($"https://{keyVaultName}.vault.azure.net/"),
    new DefaultAzureCredential());

DefaultAzureCredential uses Managed Identity in production and the developer's Azure CLI login locally — the same pattern as the SQL connection string. Secrets are then accessed identically to any other configuration value:

var apiKey = configuration["MyService:ApiKey"];

No code change required between local and production. The secret never appears in any file.

Encryption: At Rest, in Transit, and at Column Level

Azure SQL databases have Transparent Data Encryption (TDE) enabled by default — data is encrypted at rest on disk without any configuration required. This covers the scenario where someone physically accesses the storage; they cannot read the database files without the encryption key, which Azure manages.

Encryption in transit is handled by TLS, which is enforced by Azure SQL by default. All connections over the standard SQL port use TLS 1.2 or higher. The connection string should include Encrypt=True to make this explicit and prevent any fallback:

Server=myserver.database.windows.net;Database=mydb;Authentication=Active Directory Default;Encrypt=True;

Column-level encryption is the next step — encrypting specific sensitive fields (national insurance numbers, medical data, financial account details) so that even a user with full database access cannot read them without the encryption key. This is appropriate for regulated industries: financial services, healthcare, legal. For most business applications, TDE plus proper access controls is sufficient.

Azure Static Web Apps — which hosts swfconsults.co.uk and several client portals — provisions free TLS certificates automatically for custom domains. HTTPS is not optional; it is the default.

What This Looks Like in Practice

A typical SWF client system has this security stack:

  • Application to Azure SQL — Managed Identity, no password in any config file
  • Application to third-party APIs — credentials in Azure Key Vault, accessed via Managed Identity, never in source code
  • Machine-to-machine auth — certificate in Windows Certificate Store, thumbprint in config, private key never leaves the machine
  • Data at rest — Azure SQL TDE, enabled by default
  • Data in transit — TLS enforced on all connections
  • Audit trail — Key Vault access logged, SQL logins logged via Azure Monitor

None of this is expensive. Managed Identity is free. Key Vault costs pennies per month for typical secret volumes. Certificate management is operational discipline, not cost. The expensive part is a breach — and the regulatory, reputational, and client relationship damage that follows one.

The AI Coding Tools Caveat

I use AI throughout my development workflow. It genuinely accelerates delivery — requirements analysis, code generation, test writing, edge case detection. The 80% faster delivery I offer clients is real, and AI is a significant part of why.

But I treat every credential-adjacent line an AI generates as a first draft requiring review. AI tools are trained on the entire internet, which includes millions of tutorials, Stack Overflow answers, and example projects that hardcode credentials for simplicity. The AI is not doing something wrong — it is reproducing patterns it has seen. The developer is responsible for applying security judgement the AI cannot.

If you have had bespoke software built recently — by an agency, a freelancer, or via a low-code tool — it is worth asking: where are the credentials stored? If the answer involves a password in a config file, a connection string in the application's install directory, or an API key in source code, it is worth addressing before it becomes a problem.

Frequently Asked Questions

Do I need all this for a small business application?

Yes — but the complexity scales with the risk. A small internal tool connecting to a cloud database should at minimum use Azure Managed Identity (free, zero config overhead) rather than a hardcoded password. Key Vault and certificate auth are for higher-risk scenarios: external-facing APIs, multi-tenanted systems, regulated industries.

What happens if a certificate expires?

The application fails to authenticate and stops working — often with a cryptic error rather than a clear 'certificate expired' message. The fix is straightforward: renew the certificate and reinstall it. The prevention is a calendar reminder set when the certificate is issued. I set these for every client as standard.

Is Azure Managed Identity difficult to set up?

No — it is one of the easier Azure security features to configure. For a WPF application connecting to Azure SQL, it is roughly: enable Managed Identity on the App Registration, grant the identity db_datareader/db_datawriter on the SQL database, and change the connection string to use Active Directory Default authentication. No passwords, no renewal, no rotation.

How does this compare to storing passwords in a config file?

A config file password is one git commit, one screenshot, one shoulder-surf away from being compromised. Managed Identity has no password to steal. Key Vault secrets are access-controlled, audited, and rotatable without touching application code. The config file approach has a failure mode; the Managed Identity approach does not.

Can AI coding tools be used securely?

Yes, with discipline. AI tools generate code that works — but they optimise for the path of least resistance, which is often the insecure path. The developer is responsible for reviewing every credential-adjacent line an AI generates. I use AI throughout my workflow but treat any connection string, API key, or authentication pattern it produces as a first draft requiring security review.

What is Azure Key Vault and when should it be used?

Azure Key Vault is Microsoft's managed secrets store — a cloud service that holds API keys, passwords, certificates, and connection strings with access control, audit logging, and rotation support. It should be used for any credential that cannot use Managed Identity: third-party API keys, Stripe credentials, OAuth secrets, SMS gateway tokens. Secrets in Key Vault are never in source code, never in config files, and never logged accidentally.

How does encryption work in Azure SQL databases?

Azure SQL has Transparent Data Encryption (TDE) enabled by default — data is encrypted at rest on disk without any configuration required. All connections use TLS 1.2 or higher, enforced by Azure SQL. The connection string should include Encrypt=True to make this explicit. For regulated industries handling particularly sensitive data, column-level encryption can be applied to specific fields so that even a user with database access cannot read them without the encryption key.

What should I check if my business already has bespoke software?

Ask where the credentials are stored. If the answer involves a password in a config file in the application's install directory, a connection string with a username and password, or an API key in source code, it is worth addressing. These are common patterns in older bespoke systems and in AI-generated code that has not been reviewed by a developer with a security background. The fix is usually straightforward — Managed Identity for Azure services, Key Vault for external credentials.

What is certificate-based authentication and when is it the right choice?

Certificate-based authentication uses a digital certificate — rather than a shared password — to prove identity. Each client machine holds a certificate with a private key; the application presents it to authenticate, and the server validates against the expected thumbprint. No shared secret is transmitted. It is the right choice for applications running on end-user machines that need to authenticate to non-Azure services, or for machine-to-machine scenarios without a managed environment. Certificates expire and require operational discipline around renewal.

How do I get started with secure bespoke software development?

The first step is a conversation about what you're building or what you currently have. If you're commissioning new software, security requirements should be part of the initial scoping. If you have an existing system and want to understand its security posture, a review can identify the key risks and what it would take to address them. Get in touch to start the conversation.

Want to talk through your situation?

No pressure, no jargon. Just a practical conversation about what's possible.

Get in Touch