Isn't a constant validation key a security risk? - asp.net-mvc

Today I learned that on server farms, I must not use validationKey="AutoGenerate", but specify a 'fixed' string value for validationKey, for consistency across request handling by the farm servers. The example given was:
<machineKey
validationKey="21F090935F6E49C2C797F69BBAAD8402ABD2EE0B667A8B44EA7DD4374267A75D7
AD972A119482D15A4127461DB1DC347C1A63AE5F1CCFAACFF1B72A7F0A281B"
decryptionKey="ABAA84D7EC4BB56D75D217CECFFB9628809BDB8BF91CFCD64568A145BE59719F"
validation="SHA1"
decryption="AES"
/>
I was under the impression that the generation of a new key for every request and every user enhanced security quite a bit, but if the key is constant, it be retrieved from the hidden field on a form and used in a forged request.
Or must I cease using hidden fields for this key when on a server farm and use a header or cookie instead?
I say the anti-forgery key is stored in the form because this Razor markup:
#Html.AntiForgeryToken()
renders this HTML:
<input name="__RequestVerificationToken" type="hidden" value="bH6_-oZcRMuC9tA13RrOzmr0N3sWrzgkjKOhg2igHs5K2-G0HbJbF3KaK-QMrUDcQTXFbHJ-HFMNn9AjvF-TkAuBFo5f8Afi8q0OHXBzOTI1">
Unless we are talking about different keys here, but how would that work?

No, it's not a security risk.
The autogenerated key is not sent in a hidden field in the form, it's stored in the Local Security Authority (LSA) on the server (ref). If the key was sent in the form then there would be no need to have the same key on multiple servers. There would also not be any need to retrieve a key to forge a request, you could just send whatever key you wanted in a request.
With autogeneration the server doesn't generate a new key for each request, it generates a key for the server (or for the application if the IsolateApps option is used). It's only the message authentication code (MAC) that is generated for each request.

Related

SHA1 encode data and access hash result in a grails view

This is in the view
<g:passwordField name="password" required="" value="${usuarioInstance?.password}"/>
This would be a part of the controller
pass = params.password.encodeAsSHA1()
This will be returned into the view again
${pass.password}
the ${pass.password} needs to change to text or decode the SHA1()
My advice:
don't use sha-1, it's insecure
don't unencrypt a password and display it in a form field. Make the user type it in if they want to change it, if they haven't entered anything then don't update that property
use spring security which by default manages all the headaches of web security and uses Bcrypt out of the box.
Security is something every dev needs to take seriously and if the client insists on unsecure practices then they need to be educated.
Edit: #zaph makes a good point below so I should add it - don't encrypt passwords. A password should never be able to be translated back into plain text. Always hash (and salt) them and compare the user input hash to the stored hash. If you choose not to use Spring Security, you can use Bcrypt standalone and use the static Bcrypt.checkpw(userInputPw, hashedPw) method to check it.
Final Edit: To avoid any confusion (since #zaph seems confused in the comments) my recommendation is to use Bcrypt - specifically, use Spring Security.

s_sess cookie not storing the value from query string parameter cid

Does anyone has any information about s_sess cookie. All I can find is that it's a performance cookie.
The issue here is : My client has 2 websites, one of them is storing the value of query string parameter "cid" in s_sess cookie while the other site is not. Both of them has same adobe analytics code and both sites are on third party cookies.
Many of Adobe's plugins make use of the s.c_r() and s.c_w() (legacy H code) or s.Util.cookieRead() and s.Util.cookieWrite() (AppMeasurement) functions, which are for reading/writing cookies, respectively. Out of the box, you specify a cookie name and it writes to that cookie namespace.
However, Adobe also has a "combined" cookie plugin. With this plugin, all cookie reading/writing with the above functions are instead written to one of two cookies:
s_sess - This cookie is for session scoped "cookies"
s_pers - This cookie is for "cookies" that persist longer than session
So for example, let's say on the following page:
http://www.yoursite.com/index.html?cid=some_code
And in your AA code, you have the following:
// look for cid= param to put into campaign variable
s.campaign = s.Util.getQueryParam('cid');
// use getValOnce plugin to make sure duplicate values do not pop it again
s.campaign = s.getValOnce(s.campaign, 'cid', 0);
Without the combined cookies function, you will see a cookie named "cid" in document.cookies with value of "some_code" set to expire on Session.
But with the combined cookies function, you will not see a cookie named "cid". Instead, you will see a cookie named "s_sess" with a value like this:
// encoded
%20cid=some_code%3B
// unencoded
cid=some_code;
Or, if you use a plugin that makes use of s.c_w or s.Util.cookieWrite for longer than Session, you will instead see the s_pers cookie populated the same way, but with a timestamp value thrown into the mix, e.g.
// encoded
%20cid=some_code%7C1519759520136%3B
// unencoded
cid=some_code|1519759520136;
Multiple "cookies" are separated by (unencoded) "; " (similar to document.cookie)
But why do I see it on one site but not the other?
Assuming your implementation is in fact identical, my guess based on what you posted vs. common implementations is you have code similar to my example above: You grab cid= param for campaign tracking and make use of getValOnce or some other plugin that pushes the value to a cookie, and you went to siteA page with a campaign code (cid= param) but not siteB.

Apigee doesn't seem to support the OAuth 2 specification, is there a reason why?

We're making requests for bearer tokens using client_credentials OAuth 2 grant flow with Apigee. According to the spec:
4.4.2. Access Token Request
The client makes a request to the token endpoint by adding the
following parameters using the "application/x-www-form-urlencoded"
format per Appendix B with a character encoding of UTF-8 in the HTTP
request entity-body:
grant_type
REQUIRED. Value MUST be set to "client_credentials".
If we make a call however we get an error like this:
{"ErrorCode" : "invalid_request", "Error" :"Required param : grant_type"}
It seems that using Apigee we have to send grant_type as a query parameter.
Why is this? We have clients of Apigee that are unable to use OAuth libraries in their language of choice because of the way that Apigee deals with OAuth 2, and it would be good to know if there is by-design or not.
In addition it doesn't seem like it supports grant_type in the post body and sending id and key using basic auth.
Turns out you do not need to send in grant_type as a query parameter. There is a <GrantType> element in your GenerateAccessToken policy that takes in a variable. For instance, I can use the following:
<OAuthV2 name="GenerateAccessToken">
<DisplayName>GenerateAccessToken</DisplayName>
<FaultRules/>
<Properties/>
<!-- This policy generates an OAuth 2.0 access token using the password grant type -->
<Operation>GenerateAccessToken</Operation>
<!-- This is in millseconds -->
<ExpiresIn>1800000</ExpiresIn>
<Attributes/>
<SupportedGrantTypes>
<GrantType>password</GrantType>
</SupportedGrantTypes>
<GenerateResponse enabled="false">
<Format>FORM_PARAM</Format>
</GenerateResponse>
<GrantType>user.grant_type</GrantType>
<UserName>request.header.username</UserName>
<PassWord>request.header.password</PassWord>
</OAuthV2>
In this example, the grant_type is passed in as user.grant_type. But user.grant_type can be anything-- header, query param, form param, or even a hard-coded value. This way, you (the developer) are provided maximum flexibility on how you want to send in the grant_type.
Can you paste the exact API call that you are making (obviously you should obfuscate the key and secret)?
I'd like to understand what you say when you say "Apigee" -- it could mean API BAAS (https://api.usergrid.com) or a proxy that you defined using API services and attached an OAuth 2 policy to, or something else?

Oauth 2 - params ordering and signature integrity

I have two questions:
Q1: Why does OAuth2 require params to be ordered and encoded (for 2-legged)?
All it has to worry about is the matching signature in both the end for the given data(query string).
We can just check the signature generated using the query string.(e.g ?a=1&b=2). Since the signature is generated based on the secret key which is known only to the client and provider, we can only consider the query string without any ordering/encoding.
So, what's the advantage in doing ordering/encoding and then creating the signature?
Q2: How can this signature save me from man-in-the middle attack?
If I have to make a request like this to my server from a client:
increaseUserPoints?userId=1&pointsToAdd=5&appId=x&token=XYZ
Now the token XYZ will be always same, so a hacker could keep posting the same request to increase points. Since the generated token from the given appId is the same, the server will allow this. How is this case handled?
Q1: Ordering the query parameters brings sanity to the HMAC.
Let's say you have two parameters: 'pointsToAdd' and 'appId'. Using the query string pointsToAdd=X&appID=y creates a different HMAC to appID=y&pointsToAdd=X. Because both you and the server need to generate the same HMAC to verify the requests having unordered query parmeters plain fails.
Q2: This saves you from an attack because only you and the server know how to sign your request.
You have a secret key, and only you and the server knows it. This key signs the request. If the HMAC doesn't match according to this secret key, the request fails.
Because all parameters have been used to create the HMAC the request is secure from MITM attacks — a hacker can't change, add or delete any query parameters, or the server will produce a different HMAC when it attempts to authorise and the request fails.

Why is AntiForgeryToken's hidden field not same as its cookies on my machine?

I just did a quick test with a simple ASP.NET MVC 3 sample by modifying default LogOn form. According to this article, both hidden field __RequestVerificationToken and cookies __RequestVerificationToken_Lw__ must contain same value that generated by Html.AntiForgeryToken(). But it isn't exactly same when I got them in Fiddle, by the way, looking at MVC 3 source code, method GetAntiForgeryTokenAndSetCookie seemed not use salt value for generating the cookies. Was there any change in MVC 3?
Forgot to say that I could still log on successfully with both normal or Ajax POST request.
Here is raw log from Fiddle:
POST http://localhost:51713/Account/LogOn HTTP/1.1
Referer: http://localhost:51713/Account/LogOn
Content-Length: 256
Origin: http://localhost:51713
X-Requested-With: XMLHttpRequest
Content-Type: application/x-www-form-urlencoded
Cookie: __RequestVerificationToken_Lw__=OIRtVqUvNt/LfDGeoVy3W1VhdKN7MwdbUZmRNScz4NqS4uV0I0vQH2MHg77SsVhcinK5SJi9mVcdBUWk2VMiPTk8EMUN2Zq0X4ucK8XQ3/zr6NoiIvVF73Bq8ahbFaY/IrNrWY7mmzvO9j/XVLNN2lNqgCd6I3UGZAw3/nlOmpA=
__RequestVerificationToken=zeDS%2F8MZE%2BLf%2FrRhevwN51J7bOE3GxlGNLQc8HogwFctF7glU1JboHePTTHa5YFe9%2FD2sY7w167q53gqvcwYZG1iZeecdnO4fdg6URdR4RUR%2BjIgk1apkXoxQ2xg48REfv4N5D4SHKU4MAf30Diy0MVyyF9N2Dl7uUGT6LbKHZU%3D&UserName=Tien&Password=tien&RememberMe=false
what makes you think they should be the same ? :) of course, they must me comparable in some way, but that doesnt mean they must look identical in their serialized form. There is different set of data serialized to cookie (i think only the "salt" and token) and to HTML markup (salt, token, creation time, username).
If you are interested in details, take ILSpy and look for System.Web.Mvc.AntiForgeryDataSerializer, System.Web.Mvc.AntiForgeryData and OnAuthorization method of System.Web.Mvc.ValidateAntiForgeryTokenAttribute
The article you are referencing in your question is simply wrong, because the hidden field anti forgery token will never be the same as anti forgery cookie value.
Added value of my answer is the link to interesting article which describes ASP.NET anti-forgery token internals. It, among others, provides clear steps to decode and decrypt cookie/form token:
BitConverter.ToString(System.Web.Helpers.AntiXsrf.MachineKey45CryptoSystem.Instance.Unprotect(tokenValue))
... and subsequent steps in order to match the cookie and form tokens.

Resources