HOWTO: Use IIsWebFile to Securely Run CGI in /cgi-bin from the root directory
One of the least leveraged features of IIS is the IIsWebFile, which can work absolute wonders within the proper configuration, as I will shortly show...
Question:
Hi
I have a CGI script (in perl) in a cgi-bin directory. I want to be able to run that script from the root of the site without the user seeing the ugly URL.
I don't want to make the root directory executable.
For example:
Instead of: https://www.mysite.com/cgi-bin/script.cgi
I want: https://www.mysite.com/
I tried putting a Server.Transfer in index.asp but that didn't seem to like calling non-asp.
I am using a 3rd party host so don't have a lot of control although they would likely change settings if I asked.
Any pointers would be greatly appreciated.
Thanks in advance,
Answer:
IIS supports this behavior intrinsically without needing external plugins nor ASP pages. All you need to do is set up a IIsWebFile with the proper configuration. I will first give the necessary steps in sequence, then explain what is happening with each step, and finally how it all works together at runtime.
The Non-Obvious Steps...
cscript %SYSTEMDRIVE%\Inetpub\AdminScripts\adsutil.vbs SET W3SVC/1/ROOT/DefaultDoc Document.foobar
Create a zero byte file at /Document.foobar
cscript %SYSTEMDRIVE%\Inetpub\AdminScripts\adsutil.vbs CREATE W3SVC/1/ROOT/Document.foobar IIsWebFile
cscript %SYSTEMDRIVE%\Inetpub\AdminScripts\adsutil.vbs SET W3SVC/1/ROOT/Document.foobar/AccessFlags 512
cscript %SYSTEMDRIVE%\Inetpub\AdminScripts\adsutil.vbs SET W3SVC/1/ROOT/Document.foobar/ScriptMaps "*,C:\perl\bin\perl.exe %SYSTEMDRIVE%\Inetpub\Scripts\Script.cgi,1"
Now, make a request to https://www.mysite.com/ and notice that script.cgi gets executed and its response sent back. Notice that:
- The file "script.cgi" does not even need to be in /cgi-bin
- No configuration of the root directory needs to change other than DefaultDoc (expected, since you want special behavior for the file named "/" [i.e. default document]). In particular, root directory does not need to allow script nor executable, yet requests to the root's default document can execute a script...
- The extension of the bogus Default Document does not need to be scriptmapped.
In other words, it does exactly what you are asking for and nothing else, no security issues, and runs circles around .htaccess
The Details
Here is the step-by-step explanation of what each above step accomplishes:
In order to catch requests to the Default Document, you must set the DefaultDoc property to some named resource. In this case, I set it to be a resource named "Document.foobar".
Since the Default Document routing in IIS will check for the existence of the named Default Document, I have to create a zero-byte file of that name to get this going.
This is the magical step where I create an IIsWebFile that corresponds to the URL of /Document.foobar . This is used for all subsequent behaviors.
I set the AccessFlags applicable to the URL /Document.foobar to 512, which only enables Script execution. Neither Read nor Execute permissions are needed to execute a script - this is why I set 512 instead of 517 = 1(Read) + 4(Execute) + 512(Script)
I set the ScriptMap applicable to the URL /Document.foobar to be a wildcard, so it is applicable to everything sent to the URL, and for each such request have IIS execute a commandline of
C:\perl\bin\perl.exe %SYSTEMDRIVE%\Inetpub\Scripts\Script.cgi
Now, the trailing "1" in the configuration is important because it makes the mapping into a Script Engine based scriptmapping (so code execution will not need the Execute permission). If you set it to "0", then the prior AccessFlags setting needs to be 4 instead of 512.
Basically, what I am doing is setting an arbitrary URL resource as the Default Document and then using IIsWebFile to modify the execution behavior associated with exactly that URL resource and nothing else.
Finally, at Runtime, this is what happens:
- When you make a request to https://www.mysite.com/ , IIS eventually routes the request handling to the default document handler.
- Since you set DefaultDoc to be "Document.foobar", the handler looks for the file on disk, notices it is there, and proceeds to execute the URL "/Document.foobar".
- To execute that URL, IIS gathers the metadata applicable to the URL. This is basically a merge between the inherited values from w3svc/1/root and the additional override metadata provided by our IIsWebFile for the URL "/Document.foobar", which modify the AccessFlags and ScriptMaps
- The AccessFlags modification is why the root does NOT need script/executable permissions - because we already set those permissions on the specific URL of "/Document.foobar" that is being executed as the default document. You can view setting script/executable permissions at the virtual directory level as setting it at the parent URL and "inheriting" it to any child URL resource - we are simply doing the reverse here to get more control.
- The ScriptMaps modification controls the execution of that single resource, so setting it to directly run PERL with a script name will cause that perl script's output to appear as the response to the original request to the default document.
Voila! Enjoy.
//David
Comments
- Anonymous
September 21, 2005
how do you reverse it? - Anonymous
September 21, 2005
The comment has been removed - Anonymous
April 23, 2006
can we run this script for the whole site. - Anonymous
April 23, 2006
This document works for only only file. Could we run this for the whole site. - Anonymous
April 23, 2006
Naveen - I have no idea what you mean by "run this for the whole site".
Can you describe in detail what EXACTLY you want to accomplish (NOT HOW you want to accomplish it).
The original question wanted:
1. User requests http://www.mysite.com/
2. http://www.mysite.com/cgi-bin/script.cgi transparently executes
I do not understand what you mean by "whole site".
//David - Anonymous
April 24, 2006
The comment has been removed - Anonymous
April 24, 2006
Naveen - Can you explain how the following is supposed to work in your configuration:
Since you have more than one CGI file, assume you have the following CGI files:
%SYSTEMDRIVE%InetpubScriptsFile1.cgi
%SYSTEMDRIVE%InetpubScriptsFile2.cgi
When the user makes a request to http://www.mysite.com/ , how do you determine whether File1.cgi or File2.cgi executes to handle the request? Or do you want both File1.cgi and File2.cgi to run? Or ???
//David - Anonymous
April 25, 2006
naveen - I'm finding out the solution for UrlRewrite. I read out the Making "clean" URLs with Apache and PHP article at http://www.evolt.org/article/Making_clean_URLs_with_Apache_and_PHP/18/22880/
So, I'm looking UrlRewrite with IIS. Could you have any solution for it.