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
Related
I am not able to read a cookie on the client that I set on the server.
I am using C# / MVC and am handling the cookie logic outside of the controller.
I pass in the Response, and can see it is set after I call this method from the controller, but once the response gets sent to the client, this cookie is not present.
In the controller, I am calling the SetCookie method like this:
CookieHelper.SetCookie(Response);
I have also tried to pass in System.Web.HttpContext.Current.Response to no avail.
The method I am calling is:
public class CookieHelper {
public static void SetCookie(HttpResponseBase Response)
{
HttpCookie cookie = new HttpCookie("jayscookie");
cookie.Value = "a much longer string than this";
cookie.Expires = DateTime.Now.AddDays(7);
Response.Cookies.Add(cookie);
}
}
I have tried HttpResponseWrapper as well.
I have also tried System.Net.Cookie instead of HttpCookie
My IIS Setting for Session State is represented by this line in the Web.Config:
<sessionState mode="StateServer" stateConnectionString="tcpip=127.0.0.1:42424" cookieless="false"/>
I have tried without cookieless="false"
The Cookie Settings section of this application in IIS are:
Mode: UseCookies
Name: ASP.NET_SessionId
Time-out: 20 (minutes)
I have visited the following and tried solutions:
Why is my cookie not set?
Cookies not getting set in c#
Why are the cookies not being set when doing a Redirect?
Cookie not setting in asp.net mvc
How to set cookie value?
HttpWebRequest/Reponse return cookies sent
As well as several microsoft documentation pages, such as:
https://msdn.microsoft.com/en-us/library/ms178194.aspx
Is there anything I am missing, or any basic concept I am obviously not taking into account? Setting cookies on the client works fine, just not setting them on the server.
The cookie.Value string was a different value in my application. The value in the application was far too big, and exceeded the limit for cookie size. I will have to figure out a different approach, but that was the issue.
The size limit for cookies is around 4KB, for any who may find this information useful.
Server side can t access client side directly and vice versa. Sorry for poor explain but i'm in train
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.
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.
I have 4 applications running on my IIS server. One of these applications is my 'main site' and there are 3 sub applications as shown in this diagram -
All of these sites are in the same domain: mainSite.mydomain.com, app1.mydomain.com etc..
I would like users to be able to login to either APP 1, APP 2 or APP 3 and also be logged into the MAIN SITE, however if the user is logged into APP 1 they can't access APP 2 or APP3. So basically a user will only have access to one of the apps and the main site using the same login.
I currently use the following code however this allows the user to access all the applications with the same login :
<forms name=".ASPXAUTH" loginUrl="Login/" protection="Validation" timeout="120" path="/" domain=".mydomain.com"/>
and this is the code I use to create the authentication ticket:
var ticket = new FormsAuthenticationTicket(
1, user.EmailAddress,
now,
now.Add(mExpirationTimeSpan),
createPersistentCookie,
user.EmailAddress,
FormsAuthentication.FormsCookiePath);
var encryptedTicket = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
cookie.HttpOnly = true;
if (ticket.IsPersistent)
{
cookie.Expires = ticket.Expiration;
}
cookie.Secure = FormsAuthentication.RequireSSL;
cookie.Path = FormsAuthentication.FormsCookiePath;
if (FormsAuthentication.CookieDomain != null)
{
cookie.Domain = FormsAuthentication.CookieDomain;
}
mHttpContext.Response.Cookies.Add(cookie);
How would I determine what application the user should have access to and limit the users access to the other applications? The only thing I can currently think of is somehow adding information regarding which application to use into the cookie, however would I be able to decrypt this information?
I have set the .ASPXAUTH cookie to be https only but I am not sure how to effectively do the same with the ASP.NET_SessionId.
The entire site uses HTTPS so there is no need for the cookie to work with both http and https.
To add the ; secure suffix to the Set-Cookie http header I simply used the <httpCookies> element in the web.config:
<system.web>
<httpCookies httpOnlyCookies="true" requireSSL="true" />
</system.web>
IMHO much more handy than writing code as in the article of Anubhav Goyal.
See: http://msdn.microsoft.com/en-us/library/ms228262(v=vs.100).aspx
Here is a code snippet taken from a blog article written by Anubhav Goyal:
// this code will mark the forms authentication cookie and the
// session cookie as Secure.
if (Response.Cookies.Count > 0)
{
foreach (string s in Response.Cookies.AllKeys)
{
if (s == FormsAuthentication.FormsCookieName || "asp.net_sessionid".Equals(s, StringComparison.InvariantCultureIgnoreCase))
{
Response.Cookies[s].Secure = true;
}
}
}
Adding this to the EndRequest event handler in the global.asax should make this happen for all page calls.
Note: An edit was proposed to add a break; statement inside a successful "secure" assignment. I've rejected this edit based on the idea that it would only allow 1 of the cookies to be forced to secure and the second would be ignored. It is not inconceivable to add a counter or some other metric to determine that both have been secured and to break at that point.
Going with Marcel's solution above to secure Forms Authentication cookie you should also update "authentication" config element to use SSL
<authentication mode="Forms">
<forms ... requireSSL="true" />
</authentication>
Other wise authentication cookie will not be https
See: http://msdn.microsoft.com/en-us/library/vstudio/1d3t3c61(v=vs.100).aspx
Found that setting the secure property in Session_Start is sufficient, as recommended in MSDN blog "Securing Session ID: ASP/ASP.NET" with some augmentation.
protected void Session_Start(Object sender, EventArgs e)
{
SessionStateSection sessionState =
(SessionStateSection)ConfigurationManager.GetSection("system.web/sessionState");
string sidCookieName = sessionState.CookieName;
if (Request.Cookies[sidCookieName] != null)
{
HttpCookie sidCookie = Response.Cookies[sidCookieName];
sidCookie.Value = Session.SessionID;
sidCookie.HttpOnly = true;
sidCookie.Secure = true;
sidCookie.Path = "/";
}
}
It is also worth considering:
Using cookie prefixes
__Secure-, which signals to the browser that the Secure attribute is required.
__Host-, which signals to the browser that both the Path=/ and Secure attributes are required, and at the same time, that the Domain attribute must not be present.
A good article on why this helps
https://check-your-website.server-daten.de/prefix-cookies.html
Renaming your cookies
Instead of using names that clearly identify programming language.
e.g
ASP.NET_SessionId = __Secure-SID
Using samesite settings
sameSite="Lax"
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
Make cookie https secure
requireSSL="true"
SECURE EXAMPLE
<sessionState cookieless="false" cookieName="__Secure-SID" cookieSameSite="Lax" />
<httpCookies httpOnlyCookies="true" sameSite="Lax" requireSSL="true" />
Adding onto #JoelEtherton's solution to fix a newly found security vulnerability. This vulnerability happens if users request HTTP and are redirected to HTTPS, but the sessionid cookie is set as secure on the first request to HTTP. That is now a security vulnerability, according to McAfee Secure.
This code will only secure cookies if request is using HTTPS. It will expire the sessionid cookie, if not HTTPS.
// this code will mark the forms authentication cookie and the
// session cookie as Secure.
if (Request.IsSecureConnection)
{
if (Response.Cookies.Count > 0)
{
foreach (string s in Response.Cookies.AllKeys)
{
if (s == FormsAuthentication.FormsCookieName || s.ToLower() == "asp.net_sessionid")
{
Response.Cookies[s].Secure = true;
}
}
}
}
else
{
//if not secure, then don't set session cookie
Response.Cookies["asp.net_sessionid"].Value = string.Empty;
Response.Cookies["asp.net_sessionid"].Expires = new DateTime(2018, 01, 01);
}
If the entire site uses HTTPS, your sessionId cookie is as secure as the HTTPS encryption at the very least. This is because cookies are sent as HTTP headers, and when using SSL, the HTTP headers are encrypted using the SSL when being transmitted.