The former HtmlHelper.AntiForgeryToken method which allows one to override the string path is deprecated.
[ObsoleteAttribute("This method is deprecated. Use the AntiForgeryToken() method instead. To specify a custom domain for the generated cookie, use the <httpCookies> configuration element. To specify custom data to be embedded within the token, use the static AntiForgeryConfig.AdditionalDataProvider property.",
true)]
public MvcHtmlString AntiForgeryToken(
string salt,
string domain,
string path
)
Tells you to use <httpCookies>. BUT httpCookies Element does not have a setting for PATH.
Is this an oversight in the deprecation of this method? What is the best way to overwrite this cookie path? (manually?) Running website in a virtual application is not implicitly adding the application path to the __RequestVeririfcation cookie.
Looking at the deprecation message:
"This method is deprecated. Use the AntiForgeryToken() method instead. To specify a custom domain for the generated cookie, use the configuration element. To specify custom data to be embedded within the token, use the static AntiForgeryConfig.AdditionalDataProvider property."
It tells us we can validate additional parameters whenever the forgery token is read back. So even if we can't set the path in the cookie, we can set the path as a property inside the token. To validate it later on, for example:
public class AdditionalDataProvider : IAntiForgeryAdditionalDataProvider
{
public string GetAdditionalData(HttpContextBase context)
{
return AdditionalData(context);
}
public bool ValidateAdditionalData(HttpContextBase context, string additionalData)
{
var currentData = AdditionalData(context);
return currentData == additionalData;
}
private static string AdditionalData(HttpContextBase context)
{
var path = context.Request.ApplicationPath;
return path;
}
}
When asp.net generates the token it will store the current path (or any other unique value you want to validate) for that app and
if you have another app running on a different path, when the token gets sent to that app (due to the lack of cookie path) it will validate the previous app properties against that app's properties. If it is a different set of properties it will fail and deny the request.
Additionally, looking at the code for the AntiforgeryConfig.cs, if the app is running in a virtual directory, it will add that virtual directory in the cookie's name by default:
private static string GetAntiForgeryCookieName()
{
return GetAntiForgeryCookieName(HttpRuntime.AppDomainAppVirtualPath);
}
// If the app path is provided, we're generating a cookie name rather than a field name, and the cookie names should
// be unique so that a development server cookie and an IIS cookie - both running on localhost - don't stomp on
// each other.
internal static string GetAntiForgeryCookieName(string appPath)
{
if (String.IsNullOrEmpty(appPath) || appPath == "/")
{
return AntiForgeryTokenFieldName;
}
else
{
return AntiForgeryTokenFieldName + "_" + HttpServerUtility.UrlTokenEncode(Encoding.UTF8.GetBytes(appPath));
}
}
So it will be like this:
_RequestVerificationToken vs
_RequestVerificationToken_L2RIdjAz0
Meaning App2 although can receive tokens from App1, it won't be able to read them since it will be looking always for App2 verification token only.
HTH
For ASP.NET Core - See: AntiforgeryOptions Class
Cookie - Determines the settings used to create the antiforgery
cookies.
Ex (adapted from Prevent Cross-Site Request Forgery (XSRF/CSRF) attacks in ASP.NET Core):
services.AddAntiforgery(options =>
{
options.Cookie.Path = "Path";
});
The best aproach to overwrite AntiForgeryToken's cookie configuration (Path, HttpOnly,...) is with encapsulation (Microsoft team post).
It is possible to configure the cookie path instead of setting it on the properties.
public static class AntiForgeryTokenExtensions
{
///<summary>
///Generates a hidden form field (anti-forgery token) that is
///validated when the form is submitted. Furthermore, this extension
///applies custom settings on the generated cookie.
///</summary>
///<returns>Generated form field (anti-forgery token).</returns>
public static MvcHtmlString AntiForgeryTokenExtension(this HtmlHelper html)
{
// Call base AntiForgeryToken and save its output to return later.
var output = html.AntiForgeryToken();
// Check that cookie exists
if(HttpContext.Current.Response.Cookies.AllKeys.Contains(AntiForgeryConfig.CookieName))
{
// Set cookie into the variable
var antiForgeryTokenCookie = HttpContext.Current.Response.Cookies.Get(AntiForgeryConfig.CookieName);
// Set cookie configuration
antiForgeryTokenCookie.Path = "/Path";
// antiForgeryTokenCookie.HttpOnly = true;
// ...
}
return output;
}
}
There is a last change that must be done and it is replace AntiForgeryToken() for AntiForgeryTokenExtension() if it is an existing project.
NOTES
With this code you can configure AntiForgeryToken cookie as a normal cookie.
It is also possible to add input parameters to this method, but I am not sure it would be a good practice.
There are different ways to get the cookies but I think that through Response.Cookies is the "most correct", since it is a response cookie.
IMPORTANT
It is needed to check if cookie exist first before trying to get it. If you try to get a Response cookie which doesn't exist, it will be generated. It doesn't happen with Request cookies.
COOKIE KNOWLEDGE
It is not the question itself but explains part of the code and it is quite important to know when we are working with cookies, so I consider it is good to have this information here too.
All Response.Cookies are in Request.Cookies, but not all Request.Cookies are in Response.Cookies.
If you create a Response.Cookie it will appear also in Request.Cookies.
If you create a Request.Cookie it will NOT appear in Response.Cookies.
If you try to get a non-existent cookie from Request.Cookies it will return a null.
If you try to get a non-existent cookie Response.Cookies it will return a new generated cookie.
SOURCES
There is the link where the developers tell to use encapsulation and many other things that could be useful.
Microsoft developers recommendations and information
Source to knowledge of cookies, Request.Cookies and Response.Cookies differences.
Difference between request cookies and response cookies
Difference between request cookies and response cookies 2
Check if cookie exist and difference between kind of cookies
Related
I'm using spring-session and I really like it. However I think I'm missing something. In my application the flow goes like this:
1) User requests HomepageController and that controller tries to put an attribute in the request:
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
final String sessionIds = sessionStrategy.getRequestedSessionId(request);
if (sessionIds != null) {
final ExpiringSession session = sessionRepository.getSession(sessionIds);
if (session != null) {
session.setAttribute("attr", "value");
sessionRepository.save(session);
model.addAttribute("session", session);
}
}
As you can see it will try to get the sessionID from the request-cookie, and if there's a session with that ID in the repository than use it (add attribute). This is perfect, but only after the second request. Why? Because if I restart the server than the cookie is left with the old value, and then the first request will not find the session in the repository. After the response is committed though the cookie will be updated, so the second request will be correct.
And here's the question: what is wrong with my logic and how should one develop the application in order to support the first request too?
BTW, here's a sample application that demonstrates the problem:
https://github.com/paranoiabla/spring-session-issue
If you are wanting to obtain the session, you should not use requested session id. The requested session id is just that...what the browser requests. Some problems with using requested session (some of which you already outlined):
If you clear all your cookies and make a request, then no session is requested by the browser.
As you pointed out if the data store is restarted and is not persistent, then the requested session id is invalid
If the session expires, then the requested session will be invalid
Instead, you should use the session id:
final String sessionIds = request.getSession().getId();
This will use the requested session id if it is valid, otherwise it will create a new session and ensure the session is written to the response (i.e. included in the response as a cookie).
I would say your approach is wrong, your controller does to much and you should be just using the HttpSession for which Spring Session provides support. You shouldn't also be putting the session in the model imho as you should be just accessing the HttpSession. Your application shouldn't know about Spring Session.
Your controller should look like this
#Controller
public class HomepageController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public String home(HttpSession session) {
session.setAttribute("attr", "value");
return "homepage";
}
}
if you don't want to force session creation inject the HttpServletRequest and do getSession(false) instead of injecting the HttpSession.
Everything else (storing the session after request handling etc.) will be handled transparently by Spring Session.
I had a Posting on a blog about Sessions AND Cookies. Here are details
Sessions
Sessions are More Secure
Sessions are on the server
Cookies
Cookies are On client side
Less Secure
Once it is disable on browser the difficult to use.
On the basis of above argument i used sessions in Login system to keep UserId,UserName & roleName
Now on the the basis of roleName i will decide either this is Admin to enter to administrator section or not.
I have used this Code in Model in MVC
public bool LoginMe()
{
Int64 Error;
//create db
Database db = DatabaseFactory.CreateDatabase("DBContext");
DbCommand dbCommand = db.GetStoredProcCommand("ValidateUser");
db.AddInParameter(dbCommand, "#Username", DbType.String, this.UserName);
db.AddInParameter(dbCommand, "#Password", DbType.String, EncryptPassword(this.Password));
db.AddOutParameter(dbCommand, "#Error", DbType.Int64, 10);
DataSet dsResult = db.ExecuteDataSet(dbCommand);
Error = Convert.ToInt64(db.GetParameterValue(dbCommand, "#Error"));
if (Error == 1100)
{
try
{
var query = (from o in dsResult.Tables[0].AsEnumerable()
select new AllUser
{
UserId = o.Field<int>("UserId"),
UserName = o.Field<string>("UserName"),
roleName = o.Field<string>("roleName"),
}).Single(); // this will raise an exception if there isn't just one record returned
Session["UserId"] = query.UserId;
Session["UserName"] = query.UserName;
Session["roleName"] = query.roleName;
return true;
}
catch {
// do nothing and let method return false as something has gone wrong.
// add logging here if you are using it to show there has been a problem
}
}
return false;
}
I used it in View like #Session["UserId"]
Now an expert comment on this like
If you aren't using https and securing the session cookie then this might make it easy to hack your site, although that's the same for any session based site (nearly all of them)
It might be nice to add some check so that if you remove a user's rights, the session variables are deleted the next time that user requests something from the server,
otherwise they could carry on using the site even though their account it banned.You'd have to decide if this is likely and then how you want to do this (using an authorization filter maybe.)
Above comments confused me.Can any body make it clear?What is the best way to keep these information?
Session state uses client tickets to identify the server-side session, it may be susceptible to session ID spoofing and injection attacks.
So, to hack session values one would require hacking the remote-server.
And yes, for highly secure application(such as online banking) use https.
http://msdn.microsoft.com/en-us/magazine/cc163730.aspx#S9
Secure sockets layer (SSL) should be used to prevent network-level sniffing of session IDs, authentication tickets, application cookies, and other request/response information.
Can session value be hacked?
Use HTTPS if you application handles sensitive information(credit-card number,account num,passwords).
Store the User object (model with userId,username,role) in the session than separate attributes
Set setHttpOnly attribute for SESSION_ID.
It might be costly to refresh the User object stored in session before invoking every operation to reflect the current rights stored in database.
I am working with a client that can read, but not send cookies. This is a problem since the client is to post to authenticated methods. I figured an easy work around would be to send the cookie information in a header, intercepting it in BeginRequest and attaching a fabricated cookie to the request. Since begin request occurs before Authentication I figured it would work. It did not.
Here is my current method.
protected void Application_BeginRequest()
{
// I have added the auth cookie to the header
var a = Request.Headers["Authorization"];
if (a == null) return;
// get cookie value
var s = a.Replace(".AspNet.ApplicationCookie=", "");
// I am injecting the cookie into the request
var c = new HttpCookie(".AspNet.ApplicationCookie", s);
Request.Cookies.Add(c);
}
I have set breakpoints to observed the "real" cookie and it matches my "fabricated" cookie. Am I doing something wrong or am I trying for the impossible ? Moreover if anyone has any Idea how I could authorize the user using the cookie information I would be great-full.
This is a MVC5 application using the now standard Owins library.
The Owins framework is called on before BeginRequest. Moreover, the Owins request context is read only. So yes, adding to the cookie is impossible using Owins.
... But it is possible to create your own owins middleware that reads the header value and 'unprotects' the AuthenticationTicket. The solution is based off the Cookie Middleware.
Ok, so we have the RequireHttpsAttribute that we can use to ensure that a controller/controller method can only be called over SSL. In the case that we try to hit the method over HTTP, the server issues a 302 to the HTTPS version of the same controller (method).
This implies to my users that it is acceptable to issue the first request insecurely in the first place. I don't feel that this is acceptable. Before I trot out an attribute that issues a 404/500 status code in the case that the HTTP version is hit, does such an attribute already exist?
Before I trot out an attribute that issues a 404/500 status code in
the case that the HTTP version is hit, does such an attribute already
exist?
No, such attribute doesn't exist out of the box.
If the simply act of requesting the page using HTTP is not compromising any user data, I'd say the redirect should be enough and a perfect approach for your scenario. Why bother user with things we can take care of?
This implies to my users that it is acceptable to issue the first
request insecurely in the first place. I don't feel that this is
acceptable. Before I trot out an attribute that issues a 404/500
status code in the case that the HTTP version is hit, does such an
attribute already exist?
If you don't want your application to work at all for these URLs using http:// instead of https://, don't serve anything at all (404 or no connection).
Note that it's ultimately the user's responsibility to check that SSL/TLS is used (and used correctly with a valid certificate). Make sure the links to those address use https:// indeed, and that the users expect https:// to be used, at least for the start page. You could consider using HSTS if their browser support it (or possibly permanent redirects to the entry point that would be cached).
From another comment:
I don't want any info about the url leaked in any way to any third parties
Once the request has been made using this http:// URL from the client, there's little point doing anything on the server. It's too late: an eavesdropper could have seen the request. (If your own page doesn't link to external websites, they wouldn't see that address in the referrer either.)
Even if your server doesn't even listen on the plain HTTP port, an active MITM attacker (or more simply, a proxy) could potentially listen to that request and get the URL, without it even reaching your server.
Again: make sure your users expect https:// to be used, and once they're on a secure page, make sure your links/form actions to other sections of your site all use https://.
So for reference, here's my new attribute:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,
Inherited = true,
AllowMultiple = false)]
public class HttpsOnlyAttribute : FilterAttribute, IAuthorizationFilter
{
private readonly bool disableInDebug;
public HttpsOnlyAttribute(bool disableInDebug = false)
{
this.disableInDebug = disableInDebug;
}
public virtual void OnAuthorization(AuthorizationContext filterContext)
{
#if DEBUG
if (disableInDebug) return;
#endif
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
var context = filterContext.HttpContext;
var request = context.Request;
var isSecure = request.IsSecureConnection;
if (!isSecure)
{
throw new HttpException(404, "Not found");
}
}
}
How do I even begin coding authentication using ASP.NET Web API so it is cross-platform to support desktop, mobile and web? I'd read of some methods of doing RESTful authentication, such as using tokens in the header.
Are there any example projects out there that utilizes this method?
Questions:
If not how do I fix the [Authorize] attribute to read the token?
How do I generate this token? I dont think i can use formsauthentication because that uses cookies.
How do I handle the actual authorization, do the client send raw password and username then I generate the token or is there some other way?
How do I handle when my website is using it? I heard this is handled differently than when an app is using it, such as getting the domain and authorizing it.
I think tokens would be a solid way to go. Forms authentication is based on cookies for the web. Not the most idea situation for all non browser clients though.
What I'd suggest is creating a custom AuthorizationFilterAttribute and overriding the OnAuthorization method. In that method, you could check for the existence of a token that you've issued to the client after they've supplied valid credentials. You can use this attribute on any method or controller you want validated. Here's a sample you might reference
public class AuthorizeTokenAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (actionContext != null)
{
if (!AuthorizeRequest(actionContext.ControllerContext.Request))
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized) { RequestMessage = actionContext.ControllerContext.Request };
}
return;
}
}
private bool AuthorizeRequest(System.Net.Http.HttpRequestMessage request)
{
bool authorized = false;
if (request.Headers.Contains(Constants.TOKEN_HEADER))
{
var tokenValue = request.Headers.GetValues("TOKEN_HEADER");
if (tokenValue.Count() == 1) {
var value = tokenValue.FirstOrDefault();
//Token validation logic here
//set authorized variable accordingly
}
}
return authorized;
} }
TOKEN_HEADER is just a string representing an HTTP header that the client should pass back for authenticated requests.
So let's walk through it
Client requests secure data
Client is not authorized, return a response with an Unauthorized status code
Client sends credentials to authenticate, which should be secured via HTTPS
Once validated, client receives a token via an HTTP header, or whatever works for you
Client tries requesting secure data again, this time attached the token to the request
The AuthorizeTokenAttribute will validate the token and allow the action to execute.
Also, check this post by John Petersen. Making your ASP.NET Web API’s secure
There are lots of ways to authenticate users for a REST service. Using tokens is possible but just using Basic Authentication is even simpler and about as standard and cross platform as you can go.
Don't confuse authorization with authentication. The [Authorize] attribute is all about authorization but only after a user has been authenticated using some other mechanism. Authorization is completely useless without doing proper authentication first.
The best resource to check is Dominick Baier who is an expert on the subject.
I use a very simple approach:
define an access profile with its unique accessId and accessKey (e.g. MD5 hashed GUID value)
store such access profile in database
every request (GET/POST/etc.) must supply accessId, queryHash (MD5 hash value represents the query) and signature (MD5 hash value of queryHash + accessKey). Of course the client needs keep the accessKey in a secure place!!!
server gets the request will check the accessId and the signature using the same calculation algorithm to reject or grant the access (authenticate)
further authorization can be done on request type basis utilizing the access profile
the service with this approach using the new ASP.NET MVC web API can serve whatever type of client: browser/javascript and native(desktop or mobile) etc.
U can use ActionFilterAttribute and override the OnActionExecuting method.
Later on register this filter in global.cs to apply this filter for all the actions like this in Application Start method
var config = GlobalConfiguration.Configuration;
config.Filters.Add(new CustomAuthAttribute ());
{
namespace Customss
{
Public class CustomAuthAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
// To inforce HTTPS if desired , else comment out the code
if (!String.Equals(actionContext.Request.RequestUri.Scheme, "https", StringComparison.OrdinalIgnoreCase))
{
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest)
{
Content = new StringContent("HTTPS Required")
};
return;
}
// get toekn from the header
var userToken = actionContext.Request.Headers.GetValues("UserToken");
// Customer Logic to check the validity of the token.
// U can have some DB logic to check , custom STS behind or some loca cache used to compare the values
}
}
}
}