Securing ASP Data Access Credentials Using the IIS Metabase - More Issues About the Metabase (Page 3 of 4 )
Storing Values in the Metabase
Now let's have some fun. Open MetaEdit. If you left it open while you ran MetaSchema.vbs, then close it and re-open it to make sure it reflects the changes the script made to the schema. Once you have the Metabase Editor console open, expand the Schema key, and then expand Classes. You should see a key folder named DataAccessMethods. (Mine appeared at the top of the list.)
There's nothing of interest in it for us at the moment, but it's important to note (and verify) that it is there. Now, expand the /Schema/Properties/Names key. If you sort the list in the right hand pane by Id, you should see three items in the 13000 range that correspond to the three values we defined using the MetaSchema.vbs script. Each will start with "ODBC". The actual ID values may vary depending on your server configuration.
Once you've verified that the new property types exist, minimize the Schema key and expand the LM key. LM stands for "local machine", by the way. Under this key you will see all kinds of keys for different kinds of services running under IIS on the server. You will want to create a new key to use for credential storage. Create a new key by right clicking LM and selecting New | Key. I chose to name my key ASP101, but you can call it whatever you like, as long as you also change any code that references this path.
Create a new string value in the ASP101 key folder. In the Id dropdown, choose KeyType, and type "DataAccessStorage" in the data field. This will help ADSI figure out what properties and objects are supported by these containers, including the AdminACL object, which will be essential, as we'll see soon enough.
Next, we need to separate the credentials for this particular web application from any others that we might potentially need for other programs. To do this, create another key under ASP101. I will call mine TestCred. If you want, you can use a friendlier name like an IIS site name or DNS name. Remember that the code in the upcoming samples is based on our naming convention; you'll have to modify it to reflect whatever names you decide to use. You'll need to create a new key for each new set of credentials you require; expect at least one per web site, but possibly more if your site has multiple levels of access.
Now open the TestCred key. Add a new value by right clicking it and selecting New | String. A dialog box will open. In the Id field, use the dropdown to find the ODBCDataSource item and select it. Change the User Type option to ASP App; this will relax security a little so that your web application can read the data. Check the inherit checkbox, if it is not already selected. Finally, type your data source name into the Data field and click OK.
Perform the same operations for ODBCUserName and ODBCPassword. As an extra step, when you create your ODBCPassword property, check the box marked Secure. This will prevent people from casually browsing with MetaEdit to determine the database password. You will get a warning about this step, telling you that it cannot be undone; once a data property has been secured, it can not be unsecured using MetaEdit.
We're almost done now. Create another string value in the TestCred key and choose KeyType for the Id. Put "DataAccessMethods" in the data field and click OK. You don't need to create the AdminACL keys for either ASP101 or TestCred; we'll explain why when we talk about metabase security.
So, now there are values stored for the TestCred applications DSN, user name, and password. This is everything we need to access the database from ASP, and maybe even more than we needed to secure in this way. That's fine for one application, but how can we automate the setting of these values, so that we can administer multiple sites easily? The answer is to use the same ADSI functions to read and write to this part of the metabase as we'd use to access the schema.
If you want to write ASP scripts that set or change the values stored in the metabase, you will need to force the user to authenticate for those scripts. The user will need to log in to an account belonging to the server's Administrators group. Otherwise your scripts will fail in a profound way. In fact, this is going to be a problem for our scripts when we read the settings, as we'll explain in the next section.
Security and the Metabase
Before you can use ASP code to write to the database, or even read from it, you need to consider metabase security and how it will affect attempts to access this data. By default, only server administrators can access the metabase information. Even MetaEdit doesn't provide a means of changing the access control lists within the metabase. Microsoft provides a sample showing how to change security ACLs using VBScript though, and you can download it here:
However, the error checking within the script is not very good, and it has some bugs, too. Because of this, I have included a revised version of the script, MetaACL.vbs that fixes a few shortcomings and provides better error checking.
If you want you can grab parts of this code and append them to MetaSchema.vbs to create one script that will set your schema and also set security for you. In my opinion, it is a better idea, to create a separate automated script for this purpose, because you will only need to configure the schema once, but you may reuse this security code to help you configure many, many instances of our DataAccessMethods class.
To change the permissions to suit our purposes, Everyone must be granted the ability to Read entries, and Enumerate objects. In order to do this, you'll have to remove the existing ACL for Everyone first. From the command line, go to the directory in which you have placed the revised MetaACL.vbs script, then type the following, hitting enter after each command:
METAACL "IIS://localhost/ASP101" Everyone -d METAACL "IIS://localhost/ASP101" Everyone RE METAACL "IIS://localhost/ASP101/CredTest" Everyone -d METAACL "IIS://localhost/ASP101/CredTest" Everyone RE
This will remove Everyone from each part of the tree, and recreate it with the correct permissions. The second set of commands is important because, in this example, we have not set AdminACL to inherit settings from its parent. This is an improvement that can certainly be made later.
True, these settings won't make your keys hack-proof. But storing credentials here will help obfuscate your data, so that it won't fall victim to some script-kiddy who just happens to have learned to exploit CodeView.asp. In time, you can do more to secure the metabase even further, enhancing its ability to protect your data.
For example, you could easily make this part of an ASP based web administration script that could both create the necessary keys and properties, and set their security. I think it's a fine idea, but I only have so much space in this article. Also, it's more important for you to understand what is going on in the metabase regarding creation of data and setting security. Trust me, this will help you later when you do begin writing code, and if you do that, then I've done my job here.
Reading The Credentials from the MetaBase
Now that all this preparatory work is done, we arrive at our anticlimactic ending. In fact you're probably going to wonder how something so difficult to set up and configure could possibly be this easy to use. Let's take a look at the code for MetaRead.asp:
There is a lot of code there, but what it does is very simple. First, it defines the names for the machine, root key, and specific data access key that will be uses for this web application. It uses the same names we defined when we created keys earlier. If you created different keys, remember to check your code here.
If this fails, we generate an error report and set the Boolean MetaSuccess to FALSE. Otherwise, we set MetaSuccess to TRUE. Once we're past our trap, we turn errors back on.
If Err.number <> 0 Then Response.Write "<P>ERROR Reading data access " _ & "credentials from metabase<BR/>" Response.Write Err.Number & ": " & Err.Description MetaSuccess = FALSE Else MetaSuccess = TRUE End If On Error Goto 0
At this point we take a brief moment to indulge in a little optional code. The next section just displays the values we've read in HTML. Obviously, this serves no purpose but to help inflate our egos a bit after a hard day of wrestling the metabase beast, and it should be removed from any real applications for this code.
The last section of the code checks for success by testing MetaSuccess. If TRUE, it will create an ADODB Connection object, then call the Open method to connect itself to an ODBC data source.
If MetaSuccess Then ADOConn = Server.CreateObject("ADODB.Connection") ADOConn.Open DataSource, UserName, Password End If
If you are still using my sample data in the metabase, these commands will probably error out, unless of course you've created a DSN called myDSN and set your system-admin password to "ItsASecret". But why go to all that trouble? Put your own DSN data into the metabase and run the code again.
If you have trouble getting the code to access the metabase, check the spelling of your container names. If you don't see any mistakes there and you are still having trouble, or if you are getting messages that say "Permission Denied", then recheck the steps we discussed in the security section. You can use MetaACL.vbs to display the access rights for specific users or all the ACLs of a key. View the source code for details on how to use MetaACL.vbs.