Block IP Addresses In HttpModule - asp.net

I have taken over a domain that had was running an old version of Community Server. Needless to say the bots are spamming me trying to find holes.
I'd like to block entire IP blocks before System.Web.HttpRequest.ValidateInputIfRequiredByConfig() is fired. I have a IHttpModule that I have tried but I assume it's getting called after because HealthMonitoring is catching the Exceptions. Here is the module:
public class IpBlockerModule : IHttpModule
{
private static readonly string[] Hacks = new[]
{
"60.169.73.",
"60.169.75.",
"61.160.232.",
"61.160.207.",
"92.85.161."
};
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.BeginRequest += (Application_BeginRequest);
}
private void Application_BeginRequest(object source, EventArgs e)
{
var context = ((HttpApplication) source).Context;
var ipAddress = context.Request.UserHostAddress;
if (!IsHackIpAddress(ipAddress))
{
context.Response.StatusCode = 403; // (Forbidden)
}
}
private static bool IsHackIpAddress(string ip)
{
if (ip == null) return true;
return Hacks.Any(x => x.StartsWith(ip));
}
}
And the relevent web.config sections:
<system.web>
<httpModules>
<add name="IpBlockerModule" type="MyNameSpace.IpBlockerModule" />
</httpModules>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="true" >
<add name="IpBlockerModule" type="MyNameSpace.IpBlockerModule" preCondition="" />
</modules>
</system.webServer>
The reasoning behind this is my inbox is getting spammed from all the
A potentially dangerous Request.Path value was detected from the
client
and
A potentially dangerous Request.Form value was detected from the client
notifications. Is something wrong with my Module, or am I correct in assuming modules don't get fired until after the fact?

As an alternative solution have you considered letting IIS do the work for you? This way the request never makes it to your application. You can do this via the web.config and there's an article detailing the process located here. The following example is copied directly from that article and would be placed inside the <system.webServer> section of your web.config:
<security>
<ipSecurity allowUnlisted="true"> <!-- this line allows everybody, except those listed below -->
<clear/> <!-- removes all upstream restrictions -->
<add ipAddress="83.116.19.53"/> <!-- blocks the specific IP of 83.116.19.53 -->
<add ipAddress="83.116.119.0" subnetMask="255.255.255.0"/> <!--blocks network 83.116.119.0 to 83.116.119.255-->
<add ipAddress="83.116.0.0" subnetMask="255.255.0.0"/> <!--blocks network 83.116.0.0 to 83.116.255.255-->
<add ipAddress="83.0.0.0" subnetMask="255.0.0.0"/> <!--blocks entire /8 network of 83.0.0.0 to 83.255.255.255-->
</ipSecurity>
</security>

You can also add the ability to get and log IP addresses so as to identify and block only the spammy ones.
Here's C# code to get IP addresses
string ipadd;
ipadd = Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if (ipadd == "" || ipaddress == null)
ipadd = Request.ServerVariables["REMOTE_ADDR"];
I noticed that the link in the answer above is dead, so use this well-detailed article here

Related

Add custom response header to web.config

I have a website that is susceptible to a clickjacking vulnerability. Doing some research, it looks like one of the simple approaches is to simply add the X-Frame-Options: SAMEORIGIN to the response header. This is a very old web application (ca. last update was 2004), and is running IIS 6 with ASP.NET 2.0.
In newer versions, I could simply add the following section to the web.config
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="X-Frame-Options" value="SAMEORIGIN" />
</customHeaders>
</httpProtocol>
</system.webServer>
And that would be the end of it. However, I can't seem to be able to verify that this is possible using IIS 6.
Is this possible with IIS 6 and ASP.NET 2.0 to be done in only the web.config file? If so, how? If not, what code changes would I have to make in order to achieve the same result? Would simply adding
Context.Response.AddHeader("X-Frame-Options", "SAMEORIGIN");
to the Global.asax#Application_EndRequest be sufficient?
I don't believe that you'll be able to accomplish this solely by updating the web.config since you are targeting II6 (as support for the <customHeaders> section was added in IIS7+).
What you would likely need to do would be to create a custom HttpModule similar to the approach mentioned in this blog post that would handle actually adding the Header which might look something like this :
public class SameOriginHeaderModule : IHttpModule
{
private HttpApplication _application;
public void Init(HttpApplication context)
{
_application = context;
context.PreSendRequestHeaders += OnPreSendRequestHeaders;
}
void context_EndRequest(object sender, EventArgs e)
{
// If your request exists, add the header
if (_application.Context != null)
{
var response = _application.Response;
response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
}
}
public void Dispose()
{
}
}
You would then need to register this module within your web.config file as seen below :
<configuration>
<system.web>
<httpModules>
<add name="SameOriginHeaderModule" type="SameOriginHeaderModule" />
</httpModules>
</system.web>
</configuration>

Request returns 404 Not Found for OPTIONS, but works fine when using Postman (ASP.NET)?

Browser console output:
XMLHttpRequest cannot load https://api.[...].com/[...]. Response to
preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost' is therefore not allowed access.
The response had HTTP status code 404.
I have an AngularJS application that is making calls to an API built in ASP.NET. Recently the backend team added versioning to the application, and for some reason, one of the API endpoints will no longer return 200 OK for the OPTIONS request (even though all the other API endpoints on that same server still return 200 OK). Always return 404 Not Found.
ASP.NET server seems to be using a WebApi.Cors package; no specific [HttpOptions] methods are being declared (all OPTIONS request are handled through the package); and the web.config feeds the CorsConfig with * for all of origin, headers, methods.
I've tried many of the solutions from Google results but nothing has worked.
Anyone face a similar issue and can give general guidance on what could be causing the issue, or how to potentially test for the problem, or attempt a solution?
[Edit:] Found solution.
Issue caused because "Version 1" of the API endpoint was dropped on that specific route. The first valid version on that endpoint was now "Version 2". So I added a blank Controller method to catch requests for "Version 1" (which only returns a blank string, nothing more), and this was sufficient to allow the OPTION request to resolve.
I think part of the issue here is the routing has changed:
Recently the backend team added versioning to the application
Things to check:
WebApi Configuration
Can you make sure that your configuration takes place as the first item in your Global.asax file:
void Application_Start(object sender, EventArgs e)
{
GlobalConfiguration.Configure(WebApiConfig.Register);
//...
}
Web API Routing
Has the versioniong been correctly configured inside the WebApiConfig?
public static void Register(HttpConfiguration config)
{
config.EnableCors(new EnableCorsAttribute("*", "*", "*");
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/v2/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
}
In IIS 7.5 the only way I got CORS working was via the web.config and not through the Nuget package:
My web.config was as follows:
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<remove name="ExtensionLessUrlHandler-ISAPI-4.0_32bit" />
<remove name="ExtensionLessUrlHandler-ISAPI-4.0_64bit" />
<remove name="ExtensionLessUrlHandler-Integrated-4.0" />
<add name="ExtensionLessUrlHandler-ISAPI-4.0_32bit" path="*." verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="ExtensionLessUrlHandler-ISAPI-4.0_64bit" path="*." verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
Found the solution. Posting it here for anyone else that has the problem in the future.
The API endpoint started with "version 2". There had been an earlier "version 1" that never made it to production, but it was dropped/discontinued because the signature would be completely changing. This was done because development teams had been using "version 1" up to that point, but since it wasn't going into production, logic dictated no longer having that version/endpoint.
For some reason, not having a "version 1" caused the OPTIONS to fail and return a 404 Not Found. My guess is because the required version header was not actually included with the OPTIONS pre-flight request, so it never resolved to a GET destination in the Controller.
Thus, you must have a version 1 reference, even if it's a placeholder that just returns a blank string.
Before:
[VersionedRoute("products", 2, Name = "GetProducts")]
[HttpGet]
public IHttpActionResult GetProducts([FromUri] GetProductsRequest request)
{
After:
[VersionedRoute("products", 1, Name = "GetProducts")]
[HttpGet]
public IHttpActionResult GetProducts()
{
    return NotFound();
}
[VersionedRoute("products", 2, Name = "GetProducts_V2")]
[HttpGet]
public IHttpActionResult GetProducts_V2([FromUri] GetProductsRequest request)
{

Fastest way to rewrite only one subpath

I have a website developed in Classic ASP.NET and running on IIS 7.5. It works okay. But now I have the task to add affiliate program to my website. I want my reflink to looks like www.mywebsite.com/r/userid. Well, I googled around and found I can:
Use UrlRewrite third-party HttpModules. As I understand, they are based on runAllManagedModulesForAllRequests="true" web.config setting. Theoretically, I can:
Set runAllManagedModulesForAllRequests="true" and do RewritePath in Application_BeginRequest manually. But my Application_BeginRequest already contains a bit of code. It is too heavy to send all pages, images etc. to Application_BeginRequest because of one rarely called URL.
So, the question is how can I rewrite only subpath that starts with www.mywebsite.com/r/, and do not call Application_BeginRequest for every image, css etc.? Best if no third-party things.
Well, finished up with HttpModule.
web.config:
<system.webServer>
<modules>
<add name="ReflinkModule" preCondition="" type="www.ReflinkModule" />
</modules>
</system.webServer>
ReflinkModule.cs:
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(BeginRequest);
}
public void BeginRequest(Object source, EventArgs e)
{
if (HttpContext.Current == null)
return;
if (HttpContext.Current.Request == null)
return;
if (HttpContext.Current.Request.RawUrl == null)
return;
string start = "/r/";
if (!HttpContext.Current.Request.RawUrl.StartsWith(start))
return;
string key = HttpContext.Current.Request.RawUrl.Substring(start.Length);
HttpContext.Current.RewritePath("~/Xxxxx.aspx?r=" + key);
}

Custom Role Provider not called. What am I doing wrong?

So I'm trying to create a Hello World custom Role Provider-solution in ASP.NET MVC 4.
Basically I've set authentication mode="Windows" in web.config along with defining a role provider like this:
<roleManager enabled="true" defaultProvider="MyRoleProvider">
<providers>
<clear />
<add name="MyRoleProvider" type="MyProject.Code.MyRoleProvider" />
</providers>
</roleManager>
Then I've decorated the About controller method like this:
[Authorize(Roles = "SomeRole")]
public ActionResult About()
{ /* ... */ }
The custom RoleProvider-class looks like this:
namespace MyProject.Code {
public class MyRoleProvider : RoleProvider
{
public override bool IsUserInRole(string username, string roleName)
{
if (roleName == "SomeRole" && username = "Administrator") return true;
return false;
}
public override string[] GetRolesForUser(string username)
{
return new string[] { "SomeRole" };
}
/* a bunch of other overridden methods simply throwing not implementedException */
}
}
Now the thing is I've put a breakpoint on every single executable line of code in MyRoleProvder but none are hit. I have tested that breakpoints elsewhere are hit so the debugger is not the problem. Why isn't my code in the role provided executed? I was expecting IsUserInRole and/or GetRolesForUser to be executed when I navigate to the About-page. Am I wrong? Have I configured something wrong?
Full web.config for reference
edit: The user is redirected to the login page when the about page is clicked. I now realize this is due to the user actually is not authenticated yet. Authorization naturally happens after authentication. Is IISExpress not providing Windows identity?
As this is the first result in a google search i want to give a solution, maybe for others:
To use the windows identity in IIEExpress you have to activate it in your applicationhost.config , which is located in
[SolutionDir].vs\config\applicationhost.config
there you can find the xml tag
<configuration>
<system.webServer>
<security>
<authentication>
<windowsAuthentication enabled="false">
change enabled="false" to enabled="true"
and you can use windows authenticatuion in IISExpress
I think your type declaration is incomplete, you should include both the full name and the assembly name.
type="MyProject.Code.MyRoleProvider, MyProject"
You might also need to set Version, Culture and PublicKeyToken if your assemblies are place in the GAC
Hope this helps

Using Routing without MVC: authentication form

Now I'm trying to work with System.Web.Routing. All is just fine, but I can't understand how to make form authentication work with url routing (return url, redirection, etc). Google says nothing. Help! :)
UPD: I forgot - I don't use MVC. That's the problem. How to use rounig and form authentication without MVC
UPD2: more about my problem
What I want to get: urls such “mysite.com/content/123”, “mysite.com/login/”, etc using Routes. It’s important to make login page works like “regular” ASP.NET login form (redirects to login from secure area when not login on, and redirect back to secure area when loggined).
That’s what I’m doing.
In global.asax on Application_Start, register routes like this:
routes.Add("LoginPageRoute", new Route("login/", new CustomRouteHandler("~/login.aspx")));
routes.Add("ContentRoute", new Route("content/{id}", new ContentRoute("~/content.aspx"))
{
Constraints = new RouteValueDictionary {{ "id", #"\d+" }}
});
Where CustomRouteHandler and ContentRoute – simple IRouteHandler classes, just like:
...
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
var page = BuildManager.CreateInstanceFromVirtualPath(VirtualPath, typeof(Page)) as IHttpHandler;
return page;
}
...
All seems to be perfect: I’m getting content.aspx when go to “/content/10” and login.aspx when go to “/login/”. But…
When I make content secured (in web.config, with deny=”?”), login form doesn’t work like expected.
Now I can’t reach the “/content/10” page:
0. I’m typing “/content/10” in my browser.
1. Site redirects to “/login/?ReturnUrl=%2fcontent%2f10”. (Hm… seems like all problems starts here, right? :)
2. I’m trying to log in. No matter what credentials I’m entered…
3. …site redirects me to “login?ReturnUrl=%2fContent%2f10” (yellow screen of error - Access is denied. Description: An error occurred while accessing the resources required to serve this request. The server may not be configured for access to the requested URL.)
So, the problem is how to get ASP.NET understand real ReturnUrl and provide redirection after login.
These steps should allow you to implement the required behaviour.
To summarize:
You are using routing but not MVC. My example will map a url like http://host/Mysite/userid/12345 onto a real page at http://host/Mysite/Pages/users.aspx?userid=12345.
You want to control access to these addresses, requiring the user to logon. My example has a page http://host/Mysite/login.aspx with a standard login control, and the site is configured to use forms authentication.
Step 1
I've "hidden" the contents of the Pages folder using this web.config in the Pages folder:
<?xml version="1.0"?>
<configuration>
<system.web>
<httpHandlers>
<add path="*" verb="*"
type="System.Web.HttpNotFoundHandler"/>
</httpHandlers>
<pages validateRequest="false">
</pages>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<handlers>
<remove name="BlockViewHandler"/>
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler"/>
</handlers>
</system.webServer>
</configuration>
This ensures that if anyone uses a url like http://host/Mysite/Pages/users.aspx?userid=12345, then they receive a standard 404 response.
Step 2
My top level web.config file contains (as well as all the standard stuff) this location element:
<location path="userid">
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
</location>
This prevents anonymous access to urls of the form http://host/Mysite/userid/12345 which means users will be automatically redirected to login.aspx, then if they provide valid credentials, they will be redirected to the correct location.
Step 3
For reference here is my global.asax:
<script RunAt="server">
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
RegisterRoutes(RouteTable.Routes);
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.RouteExistingFiles = true;
routes.Add("UseridRoute", new Route
(
"userid/{userid}",
new CustomRouteHandler("~/Pages/users.aspx")
));
}
</script>
And here is my route handler:
using System.Web.Compilation;
using System.Web.UI;
using System.Web;
using System.Web.Routing;
using System.Security;
using System.Web.Security;
public interface IRoutablePage
{
RequestContext RequestContext { set; }
}
public class CustomRouteHandler : IRouteHandler
{
public CustomRouteHandler(string virtualPath)
{
this.VirtualPath = virtualPath;
}
public string VirtualPath { get; private set; }
public IHttpHandler GetHttpHandler(RequestContext
requestContext)
{
var page = BuildManager.CreateInstanceFromVirtualPath
(VirtualPath, typeof(Page)) as IHttpHandler;
if (page != null)
{
var routablePage = page as IRoutablePage;
if (routablePage != null) routablePage.RequestContext = requestContext;
}
return page;
}
}
The first result I got from a Google search is Frederiks excellent post on forms authentication in ASP.NET MVC. Note that the post was relevant for an early version of ASP.NET MVC, you will have to write and test the code.
HTH, Indy

Resources