Read and authentication using network credentials in a generic handler (ashx) - c#

I have created a generic handler inside an entity framework application that displays data from a table. I want to secure the handler in case anyone tries to access it directly with the url or otherwise. Where and how do I write the username and password that authenticates before processing and bringing up the data when this is called from another application (the calling application will have the username and pwd)
public class MyDatahandler: IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.Clear();
context.Response.ContentType = "text/plain";
Mydatalogic a = new dataLogic;
a.DisplayView();
}
}
The calling request is using
request.Credentials = new NetworkCredential(userName, password);
where have mutual agreement of what username and password to use. Where will I map these in my handler?

What you are looking for is the AuthorizeAttribute. This allows you to use role based permissions to access your classes and methods for
Here are some additional attributes that can help lock down your pages:
[HttpPost] //URL Post method only
[HttpGet] //URL Post method only
[ChildActionOnly] //Not accessible via URL
EDIT
You want to secure the handler in case anyone tries to access it directly with the url, right? To do this, check the server variable HTTP_REFERER from within MyDatahandler_ProcessRequest. If the user isn't coming from where they should be, throw an HTTP 403 error.
In your web.config, try adjusting it to:
<authorization>
<allow verbs="POST" users="*"/>
<deny verbs="GET" users="*"/>
</authorization>
This will prevent any direct URL access using URL parameters.

Related

Session Start Global.asax C# ASP.NET

I have the following code:
protected void Session_Start(object sender, EventArgs e)
{
WindowsPrincipal p = Thread.CurrentPrincipal as WindowsPrincipal;
string sUserAccount = HttpContext.Current.User.Identity.Name.ToString();
HttpContext.Current.Session["WinAccount"] = sUserAccount;
}
The code is to get the windows user name. From the session_start, I want to create a session which called WinAccount. But, when I tried to call the session from one of my page (default.aspx) which is has master page on it.
Let say, on page_load:
string sWinAccount = Session["WinAccount"].ToString();
Label1.Text = sWinAccount.ToString();
The web.config looks like:
<authentication mode="Windows"/>
<identity impersonate="false"/>
<authorization>
<deny users="?"/>
</authorization>
Also, the properties of the project has been enabling the windows authentication mode.
When I run, it blanks.
Please advise.
Thank you.
Verify if the application is using Windows Authentication (check web.config). If you are providing custom or forms authentication, you will need to set user details on success handler, not the session start; and use CustomPrincipal rather than WindowsPrincipal .
If windows authentication is enabled, the user credential will be available on the very first request (session start) and can be retrieved are you mentioned in your code. Place a debugger in session start and verify if you are retrieving it properly or not.
try
string sUserAccount =System.Security.Principal.WindowsIdentity.GetCurrent().Name.Tostring();
Session_Start event fired when a new client start their very first request to the app, not when the user is logged in. So in your case, the HttpContext.Current.User.Identity.Name is empty at the time Session_Start is called. It worked as expected.

Form based Authentication using C# failing for the application

I am trying to implement form authentication in asp.net for one of my applications under default website in IIS to prevent anonymous users from accessing the website and I am facing some issues when I try to do this
I have the below settings done in web.config file for this application to implement form-auth. I have included the machine key tag, authentication tag for form-based auth and authorization tag to deny anonymous user
<authentication mode="Forms">
<forms name="cookiename" cookieless="UseCookies" path="/" protection="None" timeout="30" />
</authentication>
<authorization>
<deny users="?"/>
</authorization>
In the IIS UI,
.NET Authorization rules have Anonymous Users with Deny Rule and All Users (of Inherited type) with Allow rule
Also, under Authentication UI, I have Anonymous Authentication disabled and From based Authentication enabled (Do we need to disable the Anonymous authentication here?)
When user logs into our portal, a cookie gets assigned to this user so that IIS can keep track of user's identity and gets navigated to the application (Aspx page) for which I am trying to implement form authentication. I am using webclient to post some request to perform some validation before navigating to this application and I am using CookieAwareWebClient to handle the cookies.
public class CookieAwareWebClient : WebClient
{
public CookieAwareWebClient()
{
CookieContainer = new CookieContainer();
}
public CookieContainer CookieContainer { get; private set; }
protected override WebRequest GetWebRequest(Uri address)
{
var request = (HttpWebRequest)base.GetWebRequest(address);
request.CookieContainer = CookieContainer;
return request;
}
}
Using the CookieAwareWebClient, I am trying to post the request. Here, authenticationCookie is the cookie which gets created right after user login and I am setting this value to myCookie object so that IIS can keep track of user's identity
using (CookieAwareWebClient client = new CookieAwareWebClient())
{
Cookie myCookie = new Cookie();
myCookie.Name = authenticationCookie.Name;
myCookie.Value = authenticationCookie.Value;
myCookie.Domain = URL.Host;
client.CookieContainer.Add(myCookie);
client.UseDefaultCredentials = true;
byte[] responsebytes = client.UploadValues(URL, "POST", reqparm);
}
When I try to post the request to the URL, the identity of the user is lost somewhere even though the web client has cookie container which holds the cookie value, and I am getting 401 error
Remote server returned an error (401) Unauthorized
Because of this, IIS thinks the user as anonymous and hence not able to load the application. Is this the right way to handle form-auth to prevent anonymous users from accessing website and where am I going wrong? I am using IIS 8.5 version

ASP.NET FormsAuthenticationModule DefaultUrl overriding explicit

I have a mature ASP.NET web application using FormsAuthentication (FA) to manage logins. Under certain situations, I would like to redirect the "just logged in" user to a different URL to the one that FA uses. As per standard functionality, FA will redirect to our normal homepage (specified in web.config) unless a redirectUrl was used when it hits a page that requires an authenticated user.
In my system, after the user's username/password is validated I typically use
FormsAuthentication.RedirectFromLoginPage(userName, createPersistentCookie: true); // Also calls SetAuthCookie()
which handles most situations. However, depending on certain conditions (primarily based on the newly logged in user's role) I want to redirect to a different destination. My thoughts for doing this are to call SetAuthCookie() myself and then use Response.Redirect(myUrl, false); and ApplicationInstance.CompleteRequest().
Despite doing this, the very next request comes in using for the URL defined in my tag of web.config.
<authentication mode="Forms">
<forms loginUrl="~/Login" timeout="120" cookieless="UseCookies" defaultUrl="~/?raspberry=true" />
</authentication>
Here is the actual code I am using (if a different url is required, it is specified by the overrideUrl parameter:
internal static void CreateTicket(string userName, string overrideUrl)
{
// Ref: http://support.microsoft.com/kb/301240
if (overrideUrl == null)
{
FormsAuthentication.RedirectFromLoginPage(userName, createPersistentCookie: true); // Includes call to SetAuthCookie()
}
else
{
FormsAuthentication.SetAuthCookie(userName, createPersistentCookie: true, strCookiePath:FormsAuthentication.FormsCookiePath);
HttpContext.Current.Response.Redirect(overrideUrl, false);
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
}
If I pass in a value of /special/path for overrideUrl I would like the next request to come in to be '/special/path'. Instead I am seeing /?raspberry=true
Is something else forcing defaultUrl?
Is there a way to "snoop" into the Response object while debugging to see if a Redirect is already in place? or set a breakpoint whenever it gets set so I can look at the call stack?
EDIT: At the end of my method, the Response object is showing the following properties:
RedirectLocation: "/special/path"
Status: "302 Found"
StatusCode: 302
StatusDescription: "Found"
IsRequestBeingRedirected: true
HeadersWritten: false
which all looks absolutely correct.
Thanks for any advise.
Okay, I can see what is causing it. It is the fact that I am using the Login Web Control and running my code as part of the Authenticate event. Looking at the reference source for the Login Web Control, the Authenticate event is raised by its AttemptLogin method (search for it in ref source). After raising the event and seeing that Authentication was successful, it then goes on to:
Call SetAuthCookie itself (I've already done this myself but presumably the only thing I should be doing in my code is determining if authentication was successful or not, and not messing with AuthCookie or redirects)
Performing a Redirect (overwriting my carefully crafted Redirect)
I'm going to have to figure out a solution as there these methods are private (can't override by inheriting the usercontrol) and there appears to be no option for overring or suppressing the user of it's GetRedirectUrl().

How do I use WebClient to make Basic Authentication calls to IIS WebAPI?

Using a PowerShell Cmdlet encapsulating a WebClient call to a WebAPI, I am attempting to implement Basic Authentication. I have tried a couple methods and settled on adding a Filter to the WebAPI as suggested here WebAPI Authentication Filter.
FYI: Since this is to be a file-upload tool I would prefer the data only be sent once, as the files will be up to 10MB text logs, hence I am creating the header for the first contact.
From the PowerShell Cmdlet:
private async Task<string> Run(string postBody, string Username, string Password)
{
using (var client = new WebClient())
{
client.Headers[HttpRequestHeader.ContentType] = Common.DefaultMediaType;
var credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes(Username + ":" + Password));
client.Headers[HttpRequestHeader.Authorization] = $"Basic {credentials}";
string response;
try
{
response = await client.UploadStringTaskAsync($"{_baseAddress}/{_uriTestConPost}", postBody);
}
catch (WebException ex)
{
var webResponse = ex.Response as HttpWebResponse;
if (webResponse == null) throw ex;
WriteWarning("WebException: " + webResponse.StatusCode);
return null;
}
return response;
}
}
Running this code as is produced a 401, and no echoes in the debugger.
Running this code with the credentials and header commented out creates two messages from Fiddler, one without authorization and another with authorization. Both get denied with 401 but the debugger on the WebAPI Controller shows that I am only attempting to authorize the first time, the second attempt is formed correctly with the encoded authorization header but is rejected with code 401 without ever hitting the debugger.
The ApiController is as simple as I could make it to verify:
public class TestReportController : ApiController
{
[HttpPost]
[McpBasicAuthenticationFilter]
public async Task<string> TestConnectionPost()
{
dynamic message = await Request.Content.ReadAsStringAsync();
return "You sent " + message.ToString();
}
}
At this point, I have to think the authorization problem is my web.Config or my IIS setup. For IIS I have Anonymous and Basic Authorization enabled (other parts of the site need anonymous access).
Important area of the WebConfig from what I can tell:
<system.web>
<authentication mode="None" />
<compilation debug="true" targetFramework="4.5.1" />
<httpRuntime targetFramework="4.5.1" executionTimeout="240000" maxRequestLength="1073741824" />
<identity impersonate="false" />
</system.web>
What is blocking the requests that have the credentials?
Take a look at this: https://msdn.microsoft.com/en-us/library/aa292114(v=vs.71).aspx
This is how I understand IIS authentication. I am not an authority by any means but I'll tell how I use it and how it works for me. Basically the authentication on IIS tells the website how to handle the handshake between the client and the server, but they all use windows permissions. (even the anonymous requires the user to be using the IIS default user account or one with permissions to access the site).
There is the ASP.NET authentication which you can use Windows or Forms. When I want to validate a user from a database and not from active directory, I use anonymous authentication in IIS and forms authentication in my web.config. The credentials are passed in either through a logon form or the query string.
I have been using ASP.Net identity lately. It does almost all the plumbing and is very easy to set up. https://blogs.msdn.microsoft.com/webdev/2013/10/20/building-a-simple-todo-application-with-asp-net-identity-and-associating-users-with-todoes/
There of course is a lot of stuff out there on this.

Forms Authentication: How to handle unauthorized authenticated user

I am trying to setup a very basic Forms authentication example.
It is correctly redirecting unauthenticated users to the login page
and on submit verifying the credentials and if correct calling:
FormsAuthentication.RedirectFromLoginPage(username.Text, false);
If the user is one named in the authorization section they get their page.
If not it bounces them back to the login page with no error.
How can I redirect correctly authenticated but unauthorized users to a specific error page or detect the authorization error to display an error message on the login page bounce back?
Here is my web.config
<authentication mode="Forms">
<forms name=".ASPXAUTH" loginUrl="/forms/Login" />
</authentication>
<authorization>
<deny users="?" />
<allow users="username1, username2" />
<deny users="*" />
</authorization>
Update:
Based on the answers / comments / research I've got two working solutions.
Put the following in the Page_Load method of your Login form:
if (Request.IsAuthenticated && !string.IsNullOrEmpty(Request.QueryString["ReturnUrl"]))
{
// This is an unauthorized, authenticated request...
Response.Redirect("FailedAuthorization.aspx");
}
OR
Put the following in your Global.aspx file:
protected void Application_EndRequest(object sender, EventArgs e)
{
if (Response.StatusCode == 401)
{
//Use the built in 403 Forbidden response
Response.StatusCode = 403;
//OR redirect to custom page
//Response.Redirect("FailedAuthorization.aspx");
}
}
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (Request.IsAuthenticated)
{
// Requires ASP.NET >= 4.5
Response.SuppressFormsAuthenticationRedirect = true;
}
}
Thank you for all the help with this!
Unfortunately, this is one of those things that ASP.NET continually gets wrong. Even though MS and the .NET framework team full well understand the difference between authentication and authorization, they still insist on treating unauthorized as unauthenticated. I don't know why that is.
This is just a quirk of the FormsAuthentication module handler, in that it returns a 401 Unauthorized instead of a 403 Forbidden. (it doesn't help that the HTTP standard confuses Authentication with authorization as well in this manner).
This is not something you can easily override, so your only recourse would be to do something like checking in your Login page to see if they are already logged in, and if they were redirected... it's not foolproof, but it's one way to handle it.
You don't say what version of .NET you're using, but if you are using .net 4.5 then you have another option, which is to use the SuppressFormsAuthenticationRedirect option as in this article:
Forms authentication: disable redirect to the login page
2 checks: if they're authenticated && if there is a return url (which will be there if sent to the log-in page).
if (Request.IsAuthenticated && !string.IsNullOrEmpty(Request.QueryString["ReturnUrl"]))
{
// This is an unauthorized, authenticated request...
Response.Redirect("~/somewhere.aspx");
}
The Unauthorized redirect Status Code is 302 but this overrides with status 200 when it's redirected to the login page.
In order to redirect the user to Unauthorize Page rather than to the login page, the Hack is to implement Application_EndRequest in Global and check for Response Status Code 302, which is a temporary redirect from the current called to action.
protected void Application_EndRequest(object sender, EventArgs e)
{
if(HttpContext.Current.Response.StatusCode == 302 && User.Identity.IsAuthenticated)
{
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.Redirect("/UnauthorizedPageUrl");
}
}

Categories