Skip to content
BLOG

Classic ASP in 2026: The Real Risks of Keeping It Running

By · October 6, 2026

legacy modernizationsecurity

Classic ASP applications are still running in production at a lot of organizations, and usually for a defensible reason: they work, the business process they encode has not changed, and nobody wants to be responsible for breaking something that has run reliably for twenty years.

That is a fair position. It is worth being precise about what you are carrying in exchange, because the risks are not evenly distributed and some of them get worse without anything changing on your side.

The runtime and the components underneath it

The first thing to get right is that “is Classic ASP supported?” is not one question. There are several layers, each with its own lifecycle: the ASP runtime itself, the IIS version hosting it, the Windows Server underneath that, the database server, and any third-party COM components the application depends on.

Those dates do not move together, and the answer for your installation depends on exactly which versions you are running. Microsoft publishes lifecycle information per product, so check the current lifecycle documentation for your specific versions rather than relying on a general statement about ASP. The practical question is narrower anyway: when a vulnerability is found in any layer of that stack, will a patch exist, and can you apply it without breaking the application above it?

Third-party COM components are often the weakest link, and not because of code quality. A component whose vendor no longer exists, or whose source you no longer have, cannot be patched by anyone. That is a risk you cannot remediate at all while the dependency remains.

SQL injection from inline queries

Classic ASP applications typically build SQL by concatenating strings, because that was the normal way to do it when they were written. The pattern looks harmless in isolation and is unsafe in aggregate: any input that reaches a concatenated query is a potential injection point, and in an application of any size there are many such paths.

ADO supports parameterized commands, so this is fixable in place without migrating anything, and it is usually the highest-value hardening work available on a Classic ASP codebase. The difficulty is coverage rather than technique. Finding every query means finding every page and every include file, including the ones nobody remembers, which is why this work tends to stall without an inventory first.

Worth noting: an application behind a login is not safe by default. Injection through an authenticated screen is still injection, and internal applications are frequently the ones with the least input validation, because the original authors reasonably assumed a trusted user.

XSS from Response.Write

The other systemic pattern is output. Classic ASP writes to the page with Response.Write and inline expressions, with no automatic encoding. Whatever is in the variable goes to the browser as-is.

That means any value rendered back to a page is a potential cross-site scripting vector: user-entered fields, data from an integration, anything stored by an earlier version of the application and rendered now. Older applications often hold data that predates any validation they now have, and that data is still being rendered.

This is one of the clearer arguments for migration specifically, rather than hardening in place. Modern Razor views encode output by default, so the whole class of problem is addressed by the rendering model rather than by remembering to encode at every call site. Getting there through hardening means auditing every write; getting there through migration means the default changes.

The skills problem

The risk that creeps up quietly is staffing. The pool of developers who know VBScript and ADO well is not growing, and the people who know your application specifically are a smaller group still. That shows up as slower changes, longer incident response, and a growing dependence on a small number of individuals.

It also distorts decisions. When changes are expensive and risky, teams stop making them, and the application drifts further from what the business actually needs. The cost appears as process workarounds rather than as a line item, which is why it tends to go unmeasured.

Why incremental migration lowers risk rather than raising it

The usual response to all of this is a full rewrite, and the usual outcome is that it does not start, because the risk and the wait are both unacceptable. Meanwhile the old system keeps running with all of the exposure above.

A module-by-module migration changes the risk profile. Old and new pages run against the same database and the same login, so a converted module can go live while everything else stays as it is. Each piece is verified against the live system before the old version is switched off, which means a problem is a routing change away from being reverted rather than an emergency. And the highest-risk pages, the ones handling untrusted input or sensitive data, can go first instead of waiting for a complete rewrite to finish.

That last point is the main security argument. A rewrite addresses everything eventually. An incremental migration lets you address the worst exposure in the first few modules.

Components you cannot rebuild get wrapped and called from the new application for as long as they keep working, and replaced once their behaviour has been reproduced and checked. That does not remove the dependency immediately, but it stops it blocking everything else.

We have run this pattern for thirteen years of continuous ASP Classic to ASP.NET migration on live production systems, since 2013 and still running. The approach is described on Classic ASP to ASP.NET migration services, and the step-by-step version, from inventory through to redirecting old URLs, is in our Classic ASP to ASP.NET migration guide.

What to do first

If you are carrying a Classic ASP application, three things are worth doing regardless of whether a migration is funded.

Inventory it, including the include files and the COM dependencies. You cannot assess the risk without knowing what the application is made of, and this is the step that tells you which components have no available source.

Establish the support position for each layer of your actual stack, from the lifecycle documentation rather than from memory. That turns a vague worry into a dated fact you can take to a budget conversation.

Parameterize the queries on the pages handling untrusted input. It is unglamorous, it does not require a migration, and it closes the most exploitable class of problem on the pages most exposed to it.

If keeping the system patched and supported is the immediate concern rather than converting it, that is IT support and maintenance work and can run alongside a migration rather than instead of it. If you want to know whether your system can be converted incrementally, get in touch with what it runs on and what it connects to.

Common questions

Is Classic ASP still supported?

Support status differs between the ASP runtime, the IIS version hosting it, the Windows Server underneath and any third-party components in use. Each has its own lifecycle, so check Microsoft's current lifecycle documentation for the exact versions you run.

Can we just put a web application firewall in front of it?

A firewall buys time and is worth having, but it filters traffic rather than fixing the code. It does not help with components you cannot rebuild or with skills you cannot hire.

Where should a migration start if the code is undocumented?

With an inventory rather than with code. Knowing which pages are actually reached, which include files hold business logic and which components have no available source tells you what order is safe.