Reading appSettings from Web.config into Classic ASP

I’ve done far too much work in my career on Classic ASP. Many times, sites that were originally written in Classic ASP were to be ported to ASP.Net, and often the transition between the two happened over time, with both technologies living together in the same web site. This poses many challenges, but one is easy to resolve: global application settings.

In ASP.Net, it is common to use the appSettings section of the Web.config file to store global application values. In Classic ASP, it is common to use the Application() object to store these values, and to set these values in the global.asa file.

If we want to share these values so they do not exist in both places, we can create Application object values based on appSettings in the Web.config file.

First, let’s imagine this is our global.asa file:

Sub Application_OnStart
	Application("value1") = "hello"
	Application("value2") = "goodbye"
End Sub

And let’s imagine this is the appSettings section of our Web.config file:

<appSettings>
	<add key="value1" value="hello" />
	<add key="value2" value="goodbye" />
</appSettings>

Same values, but they exist in two places. Fortunately, the Web.config file is an XML file, and Classic ASP can read XML files. Replace your global.asa code with this:

Sub Application_OnStart
	Dim xmlDoc, xmlappSettings, xmladd
	Set xmlDoc = Server.CreateObject("Microsoft.XMLDOM")
	Set xmlappSettings = Server.CreateObject("Microsoft.XMLDOM")
	Set xmladd = Server.CreateObject("Microsoft.XMLDOM")
	xmlDoc.async = false
	xmlDoc.load(Server.MapPath("Web.config"))
	Set xmlappSettings = xmldoc.GetElementsByTagName("appSettings").Item(0) 
	Set xmladd = xmlappSettings.GetElementsByTagName("add")
	For Each addItem In xmladd
		Application(addItem.getAttribute("key")) = addItem.getAttribute("value")
	Next
	Set xmladd = Nothing
	Set xmlappSettings = Nothing
	Set xmlDoc = Nothing
End Sub

This code will read your Web.config and create corresponding Application() object items with the same key/value. Going forward, you can save your configuration entries into a Web.config file (and use config file transformations).

Keep in mind that the global.asa’s Application_OnStart only runs when the application is first started, so changes to the Web.config may not get picked up until you restart your web application.

Classic ASP may have its limitations, but creative solutions like this can solve some of them.

Making the old new: Classic ASP and CSFBL

It’s embarrassing to say that my longest project, CSFBL (a web-based multiplayer baseball game), still has most of its interface written in Classic ASP. It’s also embarrassing to say that the web interface for CSFBL is a bit 1990s. I’ve had a lot of false starts moving forward to new technologies, having experimented on moving to WebForms, Castle MonoRail, and ASP.Net MVC (twice). The “next big thing” for CSFBL has, sadly, become a bit of phantomware similar to Duke Nukem 3D.

The hard part of moving to a new technology is getting rid of technical debt. I started coding CSFBL in 1999, using classic ASP and SQL Server. It’s amazing to think that some code is still the same as it was 17 years ago, but that’s not surprising for a product that’s been around for, well, 17 years. But it is what it is, and moving to something new means breaking something old and reliable — even if old and reliable is a beat-up old car that still runs great but is not so pretty to look at.

Taking a hard step backwards, I had to take stock and decide what is truly holding me back, and realized that it’s not the technology that holds me back, it’s that technical debt. So I experimented for a few hours, and then realized that my platform — Classic ASP and SQL Server — is already solid. I just needed to clean it up and make it work.

Classic ASP is more powerful than people give it credit for. I’ve done a lot with it over the years, even to the point of building a mini-ORM to make coding easier and modular. The newer code I’ve written is clean and functional. So why not take it all the way?

First, here’s three screenshots of a typical CSFBL page. The one on the left is the current (“legacy”) version, the other two are the new (“modern”) versions, built with Twitter Bootstrap, showing the desktop and mobile versions of the same page (fully responsive design so one page works for all devices).

CSFBL (legacy) CSFBL (modern, desktop) CSFBL (modern, mobile)

 

What a difference design makes… but how do we make that work in Classic ASP in a clean way?

Here’s the basic ASP template:

<%@ Language=VBScript %>
<% Option Explicit %>
<!-- #INCLUDE VIRTUAL="scripts/cTemplate.asp" -->
<%
	Response.Write template.GetHtmlHeader("Template")
	Response.Write template.GetPageHeader()
%>
	<div class="container" id="container-main">
		<div class="row">
			<h3>Template</h3>
		</div>
	</div>
<%
	Response.Write template.GetHtmlFooter()
%>

That’s a nice clean template. And the bulk of the work is done in the cTemplate class. What is that, you ask? It’s a class that allows us to do things like GetHtmlHeader() and GetPageHeader(). Here’s a snippet. The “htmlheader.html” files is a plain HTML file, with some curly braced tags for inserting the page title and timestamp of the CSS file (so we can better handle browser caching).

class cTemplate
	private m_htmlheaderfile
	private m_cssfile

	private sub Class_Initialize()
		set m_htmlheaderfile = new cFile
		m_htmlheaderfile.Load( Server.MapPath("/scripts/htmlheader.html") )

		set m_cssfile = new cFile
		m_cssfile.Load( Server.MapPath("/css/csfbl.css") )
	end sub

	private sub Class_Terminate()
		set m_htmlheaderfile = nothing
		set m_cssfile = nothing
	end sub

	public function GetHtmlHeader(title)
		dim output
		output = m_htmlheaderfile.GetText
		output = Replace(output, "{title}", title)
		output = Replace(output, "{csstimestamp}", CDbl(m_cssFile.DateLastModified))
		GetHtmlHeader = output
	end function
end class

dim template
set template = new cTemplate

What about things like querystring handling, since the URL of this page is “/team/awards.asp?teamid=###”? Simple — just parse the querystring, and load the team, and if it fails, send the person to a friendly “not found” page.

Dim team
Set team = new cTeam
team.Load(utility.ParseInteger(Request.QueryString("teamid")))

If team.ID = 0 Then
	Server.Transfer "/notfound.asp"
End If

That “utility” is an instance of another class, with utilities to do similar things in repetitive ways, like parsing integers.

The point is this: classic ASP isn’t bad. Like any technology, it can be mis-used. But when used properly, there’s a lot that can be gained with a small amount of work — especially when moving from a legacy codebase to a clean codebase.

For a functional comparison of the two pages above — both working on the same Classic ASP/MSSQL platform, and both using the same SQL back-end — try the following two links. The only difference between the two is the HTML/CSS/JS rendered.

Legacy: http://www.csfbl.com/teamawards.asp?teamid=5
Modern: http://dev.csfbl.com/team/awards.asp?teamid=50

Identifying ASP pages vulnerable to SQL injection attacks using Microsoft’s Source Code Analyzer

Back in July 2008, Microsoft released the Source Code Analyzer for SQL Injection, a “static code analysis tool for finding SQL Injection vulnerabilities in ASP code.” With the large number of SQL injection attacks occurring recently, running this tool against your ASP-based web sites is important. (It’s not the only thing you should do, but it’s at least one thing you should do!)

The tool itself is composed of two command-line tools:

  • msscasi_asp.exe, which reviews an ASP file and outputs an XML file with vulnerability warnings.
  • msscasi_view.cmd, a script which opens the generated XML file for viewing in a web application window.

One limitation is that you can’t run these utilities on more than one file — but you can run each of the utilities on every ASP file on your computer by running a batch file.

Continue reading