Using IIS url rewrite to protect against SQL injection - iis

We use a variety of CMS (Wordpress, Joomla) on our server and therefore a lot of the code is third-party so we have no control over quality. Whilst we always keep everything up-to-date we recently had a website that was hacked.
To try and prevent this from happening in the future i've added the URL rewrite rules into web.config files. But I have a couple of questions:
Should I use RequestFiltering instead of Query_String to detect injections? Or is there no difference?
Is web.config cached by IIS or is it like Apache .htaccess and queried on every single request? If so, will this be a performance drain?
Have I missed any possible attacks?
CODE...
<rule name="Injection Blocking">
<match url="^(.*)$" ignoreCase="false" />
<conditions logicalGrouping="MatchAny">
<add input="{HTTP_USER_AGENT}" pattern="(havij|libwww-perl|wget|python|nikto|curl|scan|java|winhttp|clshttp|loader)" />
<add input="{HTTP_USER_AGENT}" pattern="(%0A|%0D|%27|%3C|%3E|%00)" />
<add input="{HTTP_USER_AGENT}" pattern="(;|<|>|'|"|\)|\(|%0A|%0D|%22|%27|%28|%3C|%3E|%00).*(libwww-perl|wget|python|nikto|curl|scan|java|winhttp|HTTrack|clshttp|archiver|loader|email|harvest|extract|grab|miner)" />
<add input="{THE_REQUEST}" pattern="(\?|\*|%2a)+(%20+|\\s+|%20+\\s+|\\s+%20+|\\s+%20+\\s+)HTTP(:/|/)" />
<add input="{THE_REQUEST}" pattern="etc/passwd" />
<add input="{THE_REQUEST}" pattern="cgi-bin" />
<add input="{THE_REQUEST}" pattern="(%0A|%0D|\\r|\\n)" />
<add input="{URL}" pattern="owssvr\.dll" />
<add input="{HTTP_REFERER}" pattern="(%0A|%0D|%27|%3C|%3E|%00)" />
<add input="{HTTP_REFERER}" pattern="\.opendirviewer\." />
<add input="{HTTP_REFERER}" pattern="users\.skynet\.be.*" />
<add input="{QUERY_STRING}" pattern="[a-zA-Z0-9_]=http://" />
<add input="{QUERY_STRING}" pattern="[a-zA-Z0-9_]=(\.\.//?)+" />
<add input="{QUERY_STRING}" pattern="[a-zA-Z0-9_]=/([a-z0-9_.]//?)+" />
<add input="{QUERY_STRING}" pattern="\=PHP[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" />
<add input="{QUERY_STRING}" pattern="(\.\./|%2e%2e%2f|%2e%2e/|\.\.%2f|%2e\.%2f|%2e\./|\.%2e%2f|\.%2e/)" />
<add input="{QUERY_STRING}" pattern="ftp\:" />
<add input="{QUERY_STRING}" pattern="http\:" />
<add input="{QUERY_STRING}" pattern="https\:" />
<add input="{QUERY_STRING}" pattern="\=\|w\|" />
<add input="{QUERY_STRING}" pattern="^(.*)/self/(.*)$" />
<add input="{QUERY_STRING}" pattern="^(.*)cPath=http://(.*)$" />
<add input="{QUERY_STRING}" pattern="(\<|%3C).*script.*(\>|%3E)" />
<add input="{QUERY_STRING}" pattern="(<|%3C)([^s]*s)+cript.*(>|%3E)" />
<add input="{QUERY_STRING}" pattern="(\<|%3C).*embed.*(\>|%3E)" />
<add input="{QUERY_STRING}" pattern="(<|%3C)([^e]*e)+mbed.*(>|%3E)" />
<add input="{QUERY_STRING}" pattern="(\<|%3C).*object.*(\>|%3E)" />
<add input="{QUERY_STRING}" pattern="(<|%3C)([^o]*o)+bject.*(>|%3E)" />
<add input="{QUERY_STRING}" pattern="(\<|%3C).*iframe.*(\>|%3E)" />
<add input="{QUERY_STRING}" pattern="(<|%3C)([^i]*i)+frame.*(>|%3E)" />
<add input="{QUERY_STRING}" pattern="base64_encode.*\(.*\)" />
<add input="{QUERY_STRING}" pattern="base64_(en|de)code[^(]*\([^)]*\)" />
<add input="{QUERY_STRING}" pattern="GLOBALS(=|\[|\%[0-9A-Z]{0,2})" ignoreCase="false" />
<add input="{QUERY_STRING}" pattern="_REQUEST(=|\[|\%[0-9A-Z]{0,2})" ignoreCase="false" />
<add input="{QUERY_STRING}" pattern="^.*(\(|\)|<|>|%3c|%3e).*" />
<add input="{QUERY_STRING}" pattern="^.*(\x00|\x04|\x08|\x0d|\x1b|\x20|\x3c|\x3e|\x7f).*" />
<add input="{QUERY_STRING}" pattern="(NULL|OUTFILE|LOAD_FILE)" ignoreCase="false" />
<add input="{QUERY_STRING}" pattern="(\.{1,}/)+(motd|etc|bin)" />
<add input="{QUERY_STRING}" pattern="(localhost|loopback|127\.0\.0\.1)" />
<add input="{QUERY_STRING}" pattern="(<|>|'|%0A|%0D|%27|%3C|%3E|%00)" />
<add input="{QUERY_STRING}" pattern="concat[^\(]*\(" />
<add input="{QUERY_STRING}" pattern="union([^s]*s)+elect" />
<add input="{QUERY_STRING}" pattern="union([^a]*a)+ll([^s]*s)+elect" />
<add input="{QUERY_STRING}" pattern="\-[sdcr].*(allow_url_include|allow_url_fopen|safe_mode|disable_functions|auto_prepend_file)" />
<add input="{QUERY_STRING}" pattern="(;|<|>|'|"|\)|%0A|%0D|%22|%27|%3C|%3E|%00).*(/\*|union|select|insert|drop|delete|update|cast|create|char|convert|alter|declare|order|script|set|md5|benchmark|encode)" />
<add input="{QUERY_STRING}" pattern="(sp_executesql)" />
</conditions>
<action type="CustomResponse" statusCode="403" statusReason="Forbidden" statusDescription="Forbidden" />
</rule>

Essentially you're trying to write your own black list based primitive mini-WAF (web application firewall).
From my experience, it is pointless to try to guess all possible attacks based solely on black list primitive signatures, it's a game you cannot win.
For example your attempt to block XSS by blocking script and Iframe tags can be easily be bypassed in so many ways, some of it are just as simple as <img src='a' onerror=alert('xss')/>, and I haven't even stared mentioning the lower-case upper-case regex evasion that would bypass pretty much all of your anti-SQL Injection attempts.
Web application firewalls are products that worth millions of dollars, they keep a small army of investigators that try to update their signatures and also put great effort in behavior analysis and white-list based learning mode which increases the security level dramatically, and you can't really compare your nice try to their products.
You'll probably stop stop script kiddies, make a pro hacker to sweat a bit more but you won't stop attackers based on this defense.
My recommendation, instead of try to do it yourself, buy one of the major WAFs (F5 ,Imperva) or if you can't afford it just search for the right free IIS based WAF for you and install it. It would probably do a better job than you - no offend. Speaking of free WAFs, I'm familiar with the good old ModSecurity which apparently has an IIS version too.
Good luck!

Related

IIS Rewrite - Condition for ignoring external URLs

I use the following rule to change the path of every image file to .webp. Except for the subdirectories typo3, fileadmin and upload. That works fine.
But the website also loads external images. These URLs shouldnt be changed to .webp
So I added
another condition
<add input="{HTTP_HOST}" pattern="mydomain.com" ignoreCase="true" />
In the rule test interface in IIS it works fine. I use an imagepath from my own domain. The rule test says the pattern fits. If I use an external image path, the pattern did not fit.
BUT if I load a page on my website with different images from different URLs all paths are changed to .webp and this means, that the external ones are not found (404).
How can I make this work?
<rule name="Webp" enabled="true">
<match url="(.+)\.(jpe?g|png)$" ignoreCase="true" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_ACCEPT}" pattern="image/webp" ignoreCase="false" />
<add input="{HTTP_HOST}" pattern="mydomain.com" />
<add input="{DOCUMENT_ROOT}/{R:0}.webp" matchType="IsFile" />
<add input="{REQUEST_URI}" pattern="typo3/" negate="true" />
<add input="{REQUEST_URI}" pattern="fileadmin/" negate="true" />
<add input="{REQUEST_URI}" pattern="uploads/" negate="true" />
</conditions>
<serverVariables>
<set name="ACCEPTS_WEBP" value="true" />
</serverVariables>
<action type="Rewrite" url="{R:0}.webp" appendQueryString="false" logRewrittenUrl="true" />
</rule>
</rules>
<outboundRules rewriteBeforeCache="true">
<rule name="jpg to webp" preCondition="ResponseIsHtml" enabled="true">
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_HOST}" pattern="mydomain.com" ignoreCase="true" />
<add input="{REQUEST_URI}" pattern="typo3/" negate="true" />
<add input="{REQUEST_URI}" pattern="fileadmin/" negate="true" />
<add input="{REQUEST_URI}" pattern="uploads/" negate="true" />
</conditions>
I have something new. The external images (and more information) are a response to an AJAX call like this from my own domain.
https://www.example.org:443/index.php?tx_jsdshopware_searchview%5B__referrer%2D%5B%40extension%5D=JsdShopware&tx_jsdshopware_searchview%5B__referrer%5D%5B%40controller%5D=SearchView&tx_jsdshopware_searchview%5B__referrer%5D%5B%40action%5D=list&tx_jsdshopware_searchview%5B__referrer%5D%5Barguments%5D=YTowOnt9af44e2e88d7b23601117be0ff9656fd98a839a38&tx_jsdshopware_searchview%5B__referrer%5D%5B%40request%5D=%7B%22%40extension%22%3A%22JsdShopware%22%2C%22%40controller%22%3A%22SearchView%22%2C%22%40action%22%3A%22list%22%7D2eb573bf2f44341434ca68abb64479da78fd5613&tx_jsdshopware_searchview%5B__trustedProperties%5D=%7B%22searchword%22%3A1%2C%22action%22%3A1%7D0a3b9fd37c893b374a4bb4c2daaa7dd2afa90ccc&tx_jsdshopware_searchview%5Bsearchword%5D=footest&type=4321&tx_jsdshopware_searchview%5Baction%5D=ajaxCall&_=13212131
Can I exclude the rewriting of the filenames my filtering for "ajax" ? I have no experience with these technology.

301 Redirect with separate redirect file (on azure)

I'm trying to redirect an old site urls to a new site using Url Rewrite and placing the url's in a seperate file. I would ideally like to catch all http and https requests. This is what i have in my web.config.
<rewrite>
<rewriteMaps configSource="rewritemaps.config" />
<rules>
<rule name="Redirect oldsite.com to newsite.com" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{HTTP_HOST}" pattern="^www.oldsite.com$" />
</conditions>
<action type="Redirect" url="https://www.newsite.com" redirectType="Permanent" appendQueryString="false" />
</rule>
</rules>
</rewrite>
And in my rewritemaps.config file
<rewriteMaps>
<rewriteMap name="Redirects">
<add key="/~woodburningstove/index.php/contacts/" value="/ContactUs" />
<add key="/about-us" value="/about-us-2" />
<add key="/blog" value="/blog" />
<add key="/brands" value="/manufacturer/all" />
<add key="/brands/herald" value="/herald-stoves" />
<add key="/brands/stovax" value="/stovax-stoves" />
<add key="/brands/town-country" value="/town-country-fires"/>
<add key="/brands/warmglow" value="/warmglow-stoves"/>
<add key="/brands/warmglow/warmglow-4-cast-iron-stove-flat-black-paint-finish" value="/warmglow-4-black-2"/>
<add key="/brands/warmglow/warmglow-7-cast-iron-stove-flat-black-paint-finish" value="/warmglow-7-cast-iron-black"/>
<add key="/brands/warmglow/warmglow-7-cast-iron-stove-flat-ivory-enamel" value="/warmglow-7-cast-iron-ivory-enamel"/>
<add key="/brands/warmglow/warmglow-7-cast-iron-stove-flat-metallic-black-enamel" value="/warmglow-7-cast-iron-metallic-black-enamel"/>
<add key="/brands/warmglow/warmglow-7-cast-iron-stove-flat-metallic-black-enamel" value="/warmglow-7-cast-iron-metallic-black-enamel"/>
</rewriteMap>
</rewriteMaps>
However i keep getting "The page cannot be displayed because an internal server error has occurred" but i can't spot what i am doing wrong!?
It was down to having a duplicate entry's in my rewritemaps.config file. The following were listed twice :
<add key="/brands/warmglow/warmglow-7-cast-iron-stove-flat-metallic-black-enamel" value="/warmglow-7-cast-iron-metallic-black-enamel"/>
<add key="/brands/warmglow/warmglow-7-cast-iron-stove-flat-metallic-black-enamel" value="/warmglow-7-cast-iron-metallic-black-enamel"/>
Is it due to your regex?
Old:
<add input="{HTTP_HOST}" pattern="^www.oldsite.com$" />
New:
<add input="{HTTP_HOST}" pattern="^www\.oldsite\.com$" />
Otherwise I recommend turning custom errors off or doing some remote debugging.
https://azure.microsoft.com/en-us/documentation/articles/web-sites-dotnet-troubleshoot-visual-studio/

Can you use request or response header in an IIS8 url rewrite map?

I have some rules set up to rewrite certain urls to make things a bit tidier, but am having issues with HTTPS as our server is only accessed via our load balancer using HTTP. The load balancer adds the request header Front-End-Https which I would like to use in place of the IIS server variable {HTTPS}. Is this possible?
Something like:
<rule name="RemoveAspxExtension" enabled="true" stopProcessing="true">
<match url="(.*).aspx$" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{URL}" pattern="\.axd$" negate="true" />
<add input="{URL}" pattern="\.aspx/" negate="true" />
<add input="{URL}" pattern="\.asmx" negate="true" />
<add input="{URL}" pattern="\.ashx" negate="true" />
<add input="{URL}" pattern="\.css$" negate="true" />
<add input="{URL}" pattern="\.js$" negate="true" />
<add input="{URL}" pattern="\.png$" negate="true" />
<add input="{URL}" pattern="\.jpg$" negate="true" />
<add input="{URL}" pattern="\.gif$" negate="true" />
<add input="{URL}" pattern="\.xml$" negate="true" />
<add input="{URL}" pattern="\.txt$" negate="true" />
<add input="{URL}" pattern="\.html$" negate="true" />
<add input="{URL}" pattern="/scripts/" negate="true" />
<add input="{URL}" pattern="/styles/" negate="true" />
<add input="{URL}" pattern="/secured/" negate="true" />
</conditions>
<action type="Redirect" url="{MapProtocol:{ToLower:{Front-End-Https}}}://{HTTP_HOST}/{R:1}" appendQueryString="true" />
</rule>
</rules>
<rewriteMaps>
<rewriteMap name="MapProtocol" defaultValue="http">
<add key="on" value="https" />
<add key="off" value="http" />
</rewriteMap>
</rewriteMaps>
Yes, you can use any HTTP Header by adding an {HTTP_ in front, som you can use {HTTP_Front-End-Https} and it should work.

prerender.io IIS configuration

I am trying to implement Prerender.io in my ASP.NET application. I configured all the required necessary steps including
1)<meta name="fragment" content="!"> in head of Index.html
2) Configured the Module
<httpModules>
<add name="Prerender" type="Prerender.io.PrerenderModule, IslamicMatchMakers.Web, Version=1.0.0.0, Culture=neutral" />
</httpModules>
3) Added Custom Header
<httpProtocol>
<customHeaders>
<add name="X-Prerender-Token" value="XXXX" />
</customHeaders>
</httpProtocol>
4) Defined Rewrite rules
<rewrite>
<rules>
<!--# Only proxy the request to Prerender if it's a request for HTML-->
<rule name="Prerender" stopProcessing="true">
<match url="^(?!.*?(\.js|\.css|\.xml|\.less|\.png|\.jpg|\.jpeg|\.gif|\.pdf|\.doc|\.txt|\.ico|\.rss|\.zip|\.mp3|\.rar|\.exe|\.wmv|\.doc|\.avi|\.ppt|\.mpg|\.mpeg|\.tif|\.wav|\.mov|\.psd|\.ai|\.xls|\.mp4|\.m4a|\.swf|\.dat|\.dmg|\.iso|\.flv|\.m4v|\.torrent))(.*)" ignoreCase="false" />
<conditions logicalGrouping="MatchAny">
<add input="{HTTP_USER_AGENT}" pattern="baiduspider|facebookexternalhit|twitterbot" />
<add input="{QUERY_STRING}" pattern="_escaped_fragment_" ignoreCase="false" />
</conditions>
<action type="Rewrite" url="http://service.prerender.io/http://{HTTP_HOST}/{R:1}" />
</rule>
<rule name="Html5Mode" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{URL}" pattern="/scripts/" negate="true" />
<add input="{URL}" pattern="/content/" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
5) I installed the Application Request Routing Module on IIS, in which I enable the Proxy. Here is the image how it looks like:
ARR
Now after all these configuration when I make a request to http://localhost:2525?_escaped_fragment_=
It just show me a blank page. I am not sure what I am missing, can anyone please suggest.
You won't be able to see anything if you're running on localhost. The Prerender.io servers can only access publicly available websites, so you could try pushing your code to a public staging server...or download our open source server and run it locally to test things first.
Feel free to email me if you have any more questions about getting things up and running! :)
todd#prerender.io

IIS does not encode utf-8 urls?

I'm running Joomla 2.5 on an IIS7 server.
The problem is Joomla's search engine friendly urls don't work. Whatever url I enter, it goes to index.php.
After a painful day of struggling with rewrite rules and IIS settings, I came to two realizations:
Search engine friendly urls are only broken when the urls are unicode.
In my WAMP server, on which the SEF urls work perfectly:
$_SERVER['REQUEST_URI'] is "mydomain/%D9%85%D8%AD%D8%B5%D9%88%D9%84%D8%A7%D8%AA/%D9%82%D9%84%D8%A8%DB%8C-%D8%B9%D8%B1%D9%88%D9%82%DB%8C"
But in IIS
$_SERVER['REQUEST_URI'] is "mydomain/???????/????-?????"
It looks like urls are not URLEncoded.
I have enabled unicode aliases, search engine friendly urls, and rewrite in Joomla's global configuration.
I have copy/pasted the web.config.txt into my web.config.
Here is my web.config file:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appSettings />
<connectionStrings>
****
</connectionStrings>
<system.web>
<compilation debug="true" targetFramework="4.0">
<assemblies>
<add assembly="System.Web.Extensions.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=****" />
<add assembly="System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=****" />
<add assembly="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=****" />
</assemblies>
</compilation>
<customErrors mode="Off" />
<pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID" />
</system.web>
<system.webServer>
<directoryBrowse enabled="false" />
<defaultDocument>
<files>
<clear />
<add value="Default.htm" />
<add value="default.html" />
<add value="Default.asp" />
<add value="default.aspx" />
<add value="default.php" />
<add value="default.pl" />
<add value="default.cgi" />
<add value="index.htm" />
<add value="index.html" />
<add value="index.asp" />
<add value="index.aspx" />
<add value="index.php" />
<add value="index.pl" />
<add value="index.cgi" />
<add value="iisstart.htm" />
<add value="_holding.html" />
</files>
</defaultDocument>
<security>
<requestFiltering>
<hiddenSegments>
<add segment="backup" />
<add segment="oldsite" />
<add segment="logs" />
<add segment="tmp" />
<add segment="upload" />
</hiddenSegments>
</requestFiltering>
</security>
<httpErrors>
<remove statusCode="404" subStatusCode="-1" />
<error statusCode="404" prefixLanguageFilePath="" path="/" responseMode="ExecuteURL" />
</httpErrors>
<rewrite>
<rules>
<clear />
<rule name="Common Exploit Blocking" stopProcessing="true">
<match url="^(.*)$" />
<conditions logicalGrouping="MatchAny">
<add input="{QUERY_STRING}" pattern="mosConfig_[a-zA-Z_]{1,21}(=|\%3D)" />
<add input="{QUERY_STRING}" pattern="base64_encode.*\(.*\)" />
<add input="{QUERY_STRING}" pattern="(\<|%3C).*script.*(\>|%3E)" />
<add input="{QUERY_STRING}" pattern="GLOBALS(=|\[|\%[0-9A-Z]{0,2})" />
<add input="{QUERY_STRING}" pattern="_REQUEST(=|\[|\%[0-9A-Z]{0,2})" />
</conditions>
<action type="Redirect" url="index.php" appendQueryString="false" redirectType="SeeOther" />
</rule>
<rule name="Joomla Search Rule" stopProcessing="true">
<match url="(.*)" ignoreCase="true" />
<conditions logicalGrouping="MatchAll">
<add input="{URL}" pattern="^/search.php" ignoreCase="true" />
</conditions>
<action type="Rewrite" url="/index.php?option=com_content&view=article&id=4" />
</rule>
<rule name="Joomla Main Rewrite Rule" stopProcessing="true">
<match url="(.*)" ignoreCase="true" />
<conditions logicalGrouping="MatchAll">
<add input="{URL}" pattern="(/[^.]*|\.(php|html?|feed|pdf|raw))$" />
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="index.php/" />
</rule>
</rules>
</rewrite>
<caching>
<profiles>
<add extension=".php" policy="DisableCache" kernelCachePolicy="DisableCache" />
</profiles>
</caching>
</system.webServer>
</configuration>
This is a known bug of IIS7 and there is a hotfix for it, but I am in a hosted environment without privileges to install hotfixes. Here's a link to the Microsoft support website with detailed explanation and the resolution for the issue: FIX: A PHP application that depends on the REQUEST_URI server variable may fail when a request whose URL contains UTF-8 characters is sent to IIS 7.5
But if you can't install the hotfix, here's a workaround to get Joomla's SEF urls work with unicode:
The documentation says php's $_SERVER['REQUEST_URI'] doesn't work with unicode url rewriting, but I figured out that parameters work perfectly. We can send the url as a parameter to the php code and assign it to $_SERVER['REQUEST_URI']
Change your second rewrite rule in web.config file as follows:
<rule name="Joomla! Rule 2">
<match url="(.*)" ignoreCase="false" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{URL}" pattern="^/index.php" negate="true" />
<add input="{URL}" pattern="/component/|(/[^.]*|\.(php|html?|feed|pdf|vcf|raw))$" />
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="index.php?requesturi={URL}" />
</rule>
The only change is modifying the url attribute of the <action> tag
In your index.php (located at the root directory of your Joomla site), add this at the top (just after the <?php opening tag)
$_SERVER['REQUEST_URI'] = $_GET['requesturi'];
Done. You have unicode in your beautified urls.

Resources