Back to Case Studies

Professional Services

Hardening a Ten-Years-Unpatched Classic ASP Application Without a Rewrite

A film production services company runs its business on a Classic ASP site with a member portal and a location database on a Microsoft Access backend, untouched for more than ten years on a Windows hosting account the provider would not support when it went down. The client asked for stronger passwords. A code review found unauthenticated admin access, public export and delete endpoints, and unrestricted file upload. A rewrite was out of budget, so we scoped an emergency containment release, tested it with the client's staff for two months, and shipped it on a Sunday in November 2026.

Result

Emergency containment release deployed November 1, 2026, with the client's six-step smoke test passing on the first run: hashed passwords with a forced reset, hardened sessions, rate limiting, a replacement for plaintext passwords in the admin panel, PHP upgraded two major versions, and the site behind Cloudflare, all without funding a rewrite

Greg DuffieUpdated

TL;DR

  • A film location services company, a client since 2020, depends on a Classic ASP site: public location search, a member portal, a project workflow its coordinators run every day, and an Access database behind all of it. Nothing in it had been patched in more than a decade
  • The original request was stronger password requirements and a forced reset. A code review found much bigger holes: an admin login that could be bypassed, export and delete endpoints reachable without authentication, unrestricted file upload, and plaintext passwords shown in the control panel
  • We presented three options: emergency containment at a fixed price, a full hourly cleanup, or a new build. The client approved containment
  • The release migrated stored passwords to a modern scheme with a lazy upgrade on login, hardened sessions and credential handling, added rate limiting that answers abuse with HTTP 429, and replaced the plaintext password display with a "sign in as this client" feature for administrators
  • Alongside it we put the site behind Cloudflare, traced a months-long slowness complaint through IIS logs to the hosting layer rather than the application, had the provider disable a per-request firewall rule and scope its antivirus, and upgraded PHP from 7.4 to 8.3
  • Deploy day started with a backup that filled the disk. We moved the backups off the server, deployed, verified, and the client's coordinator passed all six smoke-test steps that evening

"Can We Make the Passwords Stronger?"

The company scouts and manages filming locations. Its website is not a brochure; it is the system. Members search locations, coordinators create projects, clients receive galleries, and all of it runs on Classic ASP pages written long ago against a Microsoft Access database on a Windows server the client rents from a hosting provider. When the site went down in late 2025, the provider declined to help with the application. That is how it came to us for more than hosting questions.

The request in 2026 was modest: stronger password rules and a forced reset for members. Reading the code to do that showed why modest was not enough.

What the review found:

  • An administrative login that could be bypassed
  • Export and delete operations reachable without being signed in
  • A file upload with no restrictions on type or destination
  • Member passwords stored and displayed in plaintext in the control panel
  • No rate limiting anywhere, so every weakness could be tried at machine speed

What the client could afford: not a rewrite. We laid out three paths with prices: an emergency containment release at a fixed cost, a full cleanup billed hourly at several times that, or a new build. The company's principal approved containment.

Contain, Test, Then Ship on a Sunday

The containment release. Stored passwords migrated to a modern hashing scheme with a lazy upgrade, so each member's password is re-hashed the first time they sign in and everyone is forced through a reset. Session handling rewritten so revoked or displaced sessions cannot be restored. Credential and action tokens that fail safe: if the salt or the provider is unavailable, nothing is written. Rate limiting on password updates and sign-ins that collapses rapid clicks into one in-flight request and answers a bypassing client with HTTP 429 within three seconds. The plaintext password display replaced with a "sign in as this client" feature, so administrators can help a member without ever seeing a password. Three abandoned WordPress installs that were still reachable on the same site were removed.

Two months of client testing. The release went to a staging server behind a Cloudflare email-PIN gate. The client's digital technician and two coordinators tested it in parts over September and October: sign in and out, forgot password with a real mailbox, project creation, location search with full-size photos, administrator impersonation, and the contact form. Their change requests, a clearer "Forgot Password?" label, a longer idle timeout, and a fix so administrator impersonation did not sign the member out, went into the same release.

The slowness that was not the application. While testing ran, the live site was intermittently slow and sometimes returned connection errors on PDF pages. The provider's firewall module was adding cost to every request; we had it disabled for the site and the pages were instantly fast. When slowness returned a month later we pulled the IIS logs and showed the stalls were static files failing at the connection layer in afternoon episodes, which is the provider's layer, not the code's. The provider scoped its antivirus exclusions and installed PHP 8.3, which the site's photo gallery scripts needed.

Deploy day, November 1. Preparation from 11 AM, cutover from 1 PM. The first full database backup failed after two hours; the second succeeded but left too little free disk for the deployment. We copied the backups off the server, deleted them to reclaim space, updated the server configuration so the deployment would not revert the PHP handler, deployed, and verified HTTPS, member login, and admin login. At 5:13 PM we cleared the client to test, with a warning that production writes were real. By 5:50 PM all six steps had passed.

After. A report two days later of the site being "extremely slow" and a user "kicked out" got a written root-cause analysis the same evening: no outage, no server event, the application's single-session rule plus a project page with 831 thumbnails. Two side findings were fixed that night: the application pool had been recycling on a drifting 29-hour schedule that logged every member out, now fixed at 4 AM, and a log forwarder was consuming a core. The site's IIS logs now stream to the client's own monitoring account, so the next performance phase is measurable the day it ships.

What the Company Has

  • A legacy application the business runs on, behind a WAF with hashed credentials, shipped without a rewrite
  • A smoke test that passed first time on the client's own six-step checklist
  • A root cause for every slowness report since September, three of them infrastructure and one application, each in writing
  • PHP two major versions newer and three abandoned WordPress installs gone
  • Production latency numbers for every page, so the performance work that comes next is a quote against evidence

Containment was the right scope. The rewrite conversation can happen with the site no longer on fire.


Frequently Asked Questions

Can a Classic ASP application be secured without rewriting it? The worst problems can be contained: credential storage, session handling, unauthenticated endpoints, upload restrictions, rate limiting, and a WAF in front. That is what this release did. What containment does not fix is the architecture, and we said so when we scoped it.

What is a lazy password migration? Existing passwords are hashed in a legacy way or not at all. On the first sign-in after the release, the application verifies the old credential, re-hashes it with the modern scheme, and stores that. Combined with a forced reset, every active account ends up on the new scheme without a bulk operation the staff would have to support.

Why was the site slow if the code did not change? Three of the four slowness episodes were the hosting layer: a firewall module adding cost per request, antivirus scanning static media, and the firewall being re-enabled by a release. The IIS logs showed it. The fourth was the application's own heavy project pages and a single-session rule, which is next on the list.

How do you deploy to a server with no room for its own backup? Take the backup, copy it off the server, delete it locally, then deploy. Keep the off-server copy until the client accepts the release. And tell the client the disk is theirs to grow, because the next backup will hit the same wall.

Why a fixed price for containment? Because the client needed to know the ceiling before approving a security project on an application nobody had touched in a decade. The extras that surfaced during testing were folded in rather than billed, and the full cleanup remains a separate, hourly conversation.

Service:Infrastructure

Want Results Like These?

Tell us about your website, hosting setup, or IT challenges — and we’ll outline exactly how we can help.

Start a Conversation