Script to lock down IIS paths
In IIS 7 we have request filtering available to help with locking down files and directories that you don't want to serve out. This is useful for resources (like configuration) that you want your IIS worker process to have access to, but not serve it out to clients. Things like web.config files fall in to this bucket, and default IIS 7 request filtering configuration denies serving out this extension. However on IIS 6, you don't have request filtering functionality built into the IIS platform. You would need to install stand-alone tools like UrlScan.
But there is a way on IIS 6 to prevent serving out files that exists on the platform; it's the AccessFlags metabase property. This property can be applied to any file or directory, and setting it to 0 will block anything from the directory or file from being served out. To make the task of setting this property for any file or directory under your sites easy, I wrote a quick script using ADSI to assist with the task. I have done very minimal testing on this, so please let me know if there are any issues.
// File: IISLockPath.js
// Copyright Microsoft Corp. 2009
// Author: Nazim Lala
//
// This script will set the AccessFlags property to 0 for a file/folder in
// IIS so that the file is not served out.
// Access to the file/folder will result in 403s.
//
// Usage:
// IISLockPath.js <app_mb_path> <dir/filepath>
// where -
// <vdir_path>: Metabase path to vdir under which application lives.
// Can be ROOT as well.
// <dir/filepath>: relative path to file or folder under vdir.
// eg -
// For Application = W3SVC/1/MyApp, which has physical path c:/MyApp
// To lock c:/MyApp/Config/Hidden folder the command would be:
// IISLockPath.js W3SVC/1/MyApp Config/Hidden
//
//////////////////////////////////////////////////////////////////////////////////////////////
var VdirMBPath = null;
var RelResourcePath = null;
var VdirObj = null;
var WebObj = null;
var IsFolder = false;
var IsFile = false;
var SplitPath;
if ( WScript.Arguments.Count() != 2 )
{
WScript.Echo("Usage: IISLockPath.js <app_mb_path> <dir/filepath>.\r\n" +
"<vdir_path>: Metabase path to vdir under which application lives." +
"Can be ROOT as well.\r\n" +
"<dir/filepath>: relative path to file or folder under vdir." +
"Eg: App = W3SVC/1/MyApp, which has physical path c:/MyApp." +
"To lock c:/MyApp/Config/Hidden folder the command would be: "+
"IISLockPath.js W3SVC/1/MyApp Config/Hidden");
WScript.Quit();
}
else
{
// Verify format for MB vdir path
// Must have atleast w3svc/# - 7 characters
if ( WScript.Arguments.Item(0).length < 7 ||
WScript.Arguments.Item(0).toUpperCase().slice(0,6) != "W3SVC/" )
{
WScript.Echo("Error: " + WScript.Arguments.Item(0) +
" is not a valid IIS metabase path");
WScript.Quit();
}
// Replace all '\' with '/' and remove beginning and trailing slashes
var rgx = new RegExp("\\\\", "g");
var startindex, endindex;
var s = WScript.Arguments.Item(0).replace(rgx, "/");
if ( s.substring(0, 1) == "/" ) startindex = 1;
else startindex = 0
if ( s.substring(s.length-1, s.length) == "/" ) endindex = s.length-1;
else endindex = s.length
VdirMBPath = s.substring(startindex, endindex);
s = WScript.Arguments.Item(1).replace(rgx, "/");
if ( s.substring(0, 1) == "/" ) startindex = 1;
else startindex = 0
if ( s.substring(s.length-1, s.length) == "/" ) endindex = s.length-1;
else endindex = s.length
RelResourcePath = s.substring(startindex, endindex);
// Verify existence of vdir path
// Technically we can get this working even if this is a web directory, but that has more steps to it
// So we will stick to VDirs for now.
try
{
VdirObj = GetObject("IIS://localhost/" + VdirMBPath);
}
catch (e)
{
VdirObj = null;
}
if ( VdirObj == null )
{
WScript.Echo("Error: Could not locate virtual directory " +
VdirMBPath);
WScript.Quit();
}
// Check if the Metabase has a web directory/file for this.
try
{
var Path = "IIS://localhost/" + VdirMBPath + "/" + RelResourcePath;
WebObj = GetObject(Path);
// We should be able to directly set the AccessFlag then.
WebObj.Put( "AccessFlags", 0 );
WebObj.SetInfo();
WScript.Echo("Done");
WScript.Quit();
}
catch (e)
{
WebObj = null;
}
// We will need to create the web directory/file under the vdir.
// Validate that the relative path exists under the vdir and if it
// is a file or folder
try
{
var FSObject = new ActiveXObject("Scripting.FileSystemObject");
var PhysicalPath = VdirObj.Path + "\\" + RelResourcePath;
if ( FSObject.FileExists(PhysicalPath) )
{
IsFile = true;
}
else if ( FSObject.FolderExists(PhysicalPath) )
{
IsFolder = true;
}
if ( !( IsFile || IsFolder ) )
{
WScript.Echo("Error: Could not locate " + RelResourcePath +
" under physical path of " + VdirMBPath);
WScript.Quit();
}
}
catch (e)
{
WScript.Echo("Error: Could not create file system object.");
WScript.Quit();
}
// Recursively create web folders for the relative path, making sure
// the last one is either folder or file.
SplitPath = RelResourcePath.split("/");
var CurrentPath = "IIS://localhost/" + VdirMBPath;
for (i=0; i<SplitPath.length-1; i++)
{
WebObj = GetObject(CurrentPath);
WebObj.Create("IISWebDirectory", SplitPath[i]);
WebObj.SetInfo();
CurrentPath += "/" + SplitPath[i];
}
if ( IsFolder )
{
WebObj = GetObject(CurrentPath);
WebObj.Create("IISWebDirectory", SplitPath[SplitPath.length-1]);
WebObj.SetInfo();
}
else
{
WebObj = GetObject(CurrentPath);
WebObj.Create("IISWebFile", SplitPath[SplitPath.length-1]);
WebObj.SetInfo();
}
CurrentPath += "/" + SplitPath[SplitPath.length-1];
WebObj = GetObject(CurrentPath);
WebObj.Put( "AccessFlags", 0 );
WebObj.SetInfo();
WScript.Echo("Done");
WScript.Quit();
}