I have a javascript variable that I want to pass back to the server side, which I thereafter intend to use it as an access token to grant user access to other pages which requires this token.
I wonder how do I pass this javascript variable back to server, so I can set it to a session variable? Do I need to send it back using ajax?
this is the part of jQuery I use to retrieve the token from server
$(document).ready(function () {
$('#loginForm').submit(function(e) {
var blargh = $(this).find('input').serialize();
$.ajax({
type: 'post',
url: '/WebAPI/api/authenticate/login',
data: blargh,
success: function (data) {
$.each(data, function(index, token) {
$('#container').prepend('<input type="hidden" name="MY_HIDDEN_FIELD_NAME" id="MY_HIDDEN_FIELD_NAME" value="'+token+'">');
});
},
error: function(jqXHR, status, errorThrown) {
alert("Error " + status + "\nError Thrown" + errorThrown )
},
});
e.preventDefault();
});
});
To pass back an additional item in the AJAX POST, you could add it like this...
var blargh = $(this).find('input').serialize();
blargh.someItem = "value";
Bear in mind that this only works when the form is submitted using AJAX, so not where JavaScript isn't available or is disabled.
All the normal security disclaimers apply!
I would recommend sending you the acess token in request headers when u are sending a ajax request
xhr.setRequestHeader('custom-header', 'value');
and on the server side you can fetch the request header
Couldn't you pass it back as either a hidden form element or pass it back in the query string of a ajax postback?
Example of a hook to get the post back value in global.asmx
protected void Session_Start(object src, EventArgs e)
{
if(!string.IsNullOrEmpty(Request.Form["MY_HIDDEN_FIELD_NAME"]))
{
Session["MY_SESSION_NAME"] = Request.Form["MY_HIDDEN_FIELD_NAME"]
}
}
First - why is your client generating the token (I hope I've understood you correctly there)? The server should generate the token and the client must then be responsible for maintaining it.
If it's an API token that'll only ever be used in the browser from javascript, then I recommend using an authentication cookie - all browsers know how to handle them and you can also easily expire them server-side if you no longer want to allow a particular token to have access (that's quite an important point). Also I strongly recommend against relying on server-side session to maintain the authentication session.
Authentication tokens should ideally be stateless (just like in Forms Authentication's cookie) - the burden of proof is on the client to send you a correct token, with that token containing the information you need to re-initialise the current requests state with the correct user.
If, however, it's a general purpose API for any type of client then you should allow the client to send the token to you in the query string of all requests at a very minimum. You should also support taking it in the request header as well - clients that can easily support setting request headers often prefer to because it then hides the auth token from the URL and makes formatting requests easier (there's also the potential to max out a web server's query string limit if the token is big enough).
I then recommend you look, at a minimum, at overriding MVCs AuthorizeAttribute (there are 2 - one for the 'standard' MVC 4 pipeline and one for the new Web API pipeline, & they would both need to be done if you are using both technologies. The link is for the MVC 4 one) to crack out your cookie/header/query string value. In there you can get the value, decrypt the token, identify the user and set the roles. The core code of that attribute then contains the logic for denying a request based on whether the user is authenticated/has a certain role/is a certain user.
Related
On my site, I have some jquery which is making a post request from my login form to my API. Here is the jquery:
function doLogin()
{
var userEmail = $("#email").val();
var userPassword = $("#password").val();
$.post(
"https://api.linkenfest.co.uk/access/login/<?= $referrer ?>",
{
email: userEmail,
password: userPassword
}
).done(function( data ){
var status = data.data.status;
var referrer = data.data.referrer;
var message = data.data.message;
var session = data.data.session;
alert( message );
if( status == 200 ){
window.location.replace( referrer + "?session=" + session );
}
}
);
On returning a success, the response from my api looks like this:
{
"status": 200,
"data":{
"status": 200,
"referrer": "page.php",
"message": "Successful login",
"session": "o3vo1uram0k2mojmbd45pmicr2"
}
}
As you can see from my response, it includes the value obtained from php session_id()
I would like to find out if giving this information to the user opens up for a security vulnerability on the site.
The session ID value is used to start the session after window.location.reload
Thanks.
As others have already pointed out in comments, the client getting the session id is the standard way to manage sessions - how else could it send it in subsequent requests? But it does matter how the client receives it.
Note that the code above is very likely vulnerable.
"https://api.linkenfest.co.uk/access/login/<?= $referrer ?>" - This is reflected XSS if $referrer is the Referer header from the request. As it is in a Javascript context, it is not enough to simply html encode, you need javascript encoding.
window.location.replace( referrer + "?session=" + session ) - This technically is an open redirect to the referrer parameter. It is not easy to exploit though. The best practice would still be to validate it (preferably on the server - which might already be in place).
Weak session management, session id is available to Javascript. While in some cases this is acceptable, accepting this risk should be a conscious decision. A session id should normally (and traditionally) be set in an httpOnly cookie. One exception when this can't be done is when a token needs to be sent to a different origin. But in that case it's not a session id semantically, and a proper SSO solution should be in place.
A session id is sensitive information, and as such, should not be sent in the url. The problem with the url is that it gets logged in multiple places (the user's browser will remember it and save it to disk, it will get logged on intermediate proxies, and in the target server's logs. These are all places from where an attacker might be able to get it. It's best to not send sensitive information in the url, but either in a header (a cookie is also just a header), or the request body is also ok if needed.
The destination url (api.linkenfest.co.uk) is vulnerable to login csrf if it allows you to log in this way.
Also looking at this from a broader perspective, what I guess you are doing is using the api as an identity provider to log your user in. For this purpose you should use a standard protocol like OpenID Connect, and not reinvent the wheel, because as you can see above, it is not at all straightforward.
I'm making an OAuth2 call from my ASP.NET MVC web application to my Web API through JavaScript to authenticate my user and get a token. The Web API accesses a SQL Server database where the user's login is stored using Identity and the typical AspNetUsers tables. My API call returns a 20 min access token and a 2 week refresh token. The API and consuming apps are products that we are developing that our customers will register with. In other words, all the code is on our side.
I know I must refresh the access token before it expires by passing the API the refresh token. My question is...where do I store the access and refresh tokens on the client for use in my JavaScript to make subsequent API calls or to refresh the token? People online are saying that storing anything client-side is bad, cookies are unsecure, etc and without offering any solutions. Local storage? But of course these are Ajax calls in JavaScript that we are making to the API, so the tokens needs to exist somewhere on the client side! It's driving me crazy trying to figure this out. I know I need to at least use HTTPS.
I'd suggest you to create a table in database to store the refresh token and access token.
Table structure will look like below
ID,Access_Token,Refresh_Token,LastUpdated_Time
Whenever you're calling a API with access token , please check the current time and LastUpdated_Time of token , if it is more than one hour your token will become invalid, so you need to get another valid token using your refresh token.
In my application , I had 55 minutes lifespan of toke, after that time token gets invalid.
Code
if (dateTimeDiff > 55) {
var request = (HttpWebRequest) WebRequest.Create("https://www.googleapis.com/oauth2/v3/token");
var postData = "refresh_token=your refresh token";
postData += "&client_id=your client id";
postData += "&client_secret=your client secret";
postData += "&grant_type=refresh_token";
var data = Encoding.ASCII.GetBytes(postData);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
request.UseDefaultCredentials = true;
using(var stream = request.GetRequestStream()) {
stream.Write(data, 0, data.Length);
}
var response = (HttpWebResponse) request.GetResponse();
string responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
}
The response will contain new access token, don't forget to update your table with LastUpdated_Time and new token.
The auth token is something that you can always obtain using the refresh token. So, have an interceptor that validates the incoming and outgoing requests and store the auth-token there. Coming to the refresh-token, in our application we were initially storing in cookies, later moved to local storage.
You can tried to store the token on the localStorage, what I am doing right now is(I am using typescript and react, but I think it will give a clue for your web app):
const msg = await login({ ...values, type });
let accessToken = msg.accessToken;
window.localStorage.setItem("x-access-token", accessToken);
I am facing very strange issue here. I have a servlet running on my machine which renders my web page based on some input parameters.
Now, my screen capture with PhantomJS is not working if I try to send my data as a JSON object as a POST request type. For e.g. if I try:
Client side
var data = {"op": "get"};
page.open(address, 'POST', data, function (status) {
if (status !== 'success') {
console.log('[ERROR] :: Unable to fetch the address - ' + address + ", with data - " + data);
phantom.exit();
} else {
page.render(output);
}
console.log('processing...');
});
Server side
Now, on the server side, I am using Apache Velocity View templating so I have a single method which handles both get and post like :
public Template handleRequest(HttpServletRequest request, HttpServletResponse response,
Context context){
System.out.println(request.getParameter("op"));
//Always null
}
However, if I try sending my data from phantomjs as:
var data = "op=get&..."
It works
Also, at many places elsewhere in my code..I am doing Ajax POST requests to the same servlet and it works perfectly for all those request.
Would anybody explain why my servlet is not reading the JSON parameters passed from Phantomjs?
Servlets deal with simple request, so they only know how to parse (natively) HTTP parameters, either GET parameters from the URL, or POST parameters sent as application/x-www-form-urlencoded. Newer versions of the Servlet specification can also read multipart/form-data.
But there's nothing about JSON mentioned either in the Servlet or the HTTP specifications. So you must use a library that knows how to parse JSON and make the resulting object available in the Velocity context.
I'm implementing a website in Angular.js, which is hitting an ASP.NET WebAPI backend.
Angular.js has some in-built features to help with anti-csrf protection. On each http request, it will look for a cookie called "XSRF-TOKEN" and submit it as a header called "X-XSRF-TOKEN" .
This relies on the webserver being able to set the XSRF-TOKEN cookie after authenticating the user, and then checking the X-XSRF-TOKEN header for incoming requests.
The Angular documentation states:
To take advantage of this, your server needs to set a token in a JavaScript readable session cookie called XSRF-TOKEN on first HTTP GET request. On subsequent non-GET requests the server can verify that the cookie matches X-XSRF-TOKEN HTTP header, and therefore be sure that only JavaScript running on your domain could have read the token. The token must be unique for each user and must be verifiable by the server (to prevent the JavaScript making up its own tokens). We recommend that the token is a digest of your site's authentication cookie with salt for added security.
I couldn't find any good examples of this for ASP.NET WebAPI, so I've rolled my own with help from various sources. My question is - can anyone see anything wrong with the code?
First I defined a simple helper class:
public class CsrfTokenHelper
{
const string ConstantSalt = "<ARandomString>";
public string GenerateCsrfTokenFromAuthToken(string authToken)
{
return GenerateCookieFriendlyHash(authToken);
}
public bool DoesCsrfTokenMatchAuthToken(string csrfToken, string authToken)
{
return csrfToken == GenerateCookieFriendlyHash(authToken);
}
private static string GenerateCookieFriendlyHash(string authToken)
{
using (var sha = SHA256.Create())
{
var computedHash = sha.ComputeHash(Encoding.Unicode.GetBytes(authToken + ConstantSalt));
var cookieFriendlyHash = HttpServerUtility.UrlTokenEncode(computedHash);
return cookieFriendlyHash;
}
}
}
Then I have the following method in my authorisation controller, and I call it after I call FormsAuthentication.SetAuthCookie():
// http://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-(csrf)-attacks
// http://docs.angularjs.org/api/ng.$http
private void SetCsrfCookie()
{
var authCookie = HttpContext.Current.Response.Cookies.Get(".ASPXAUTH");
Debug.Assert(authCookie != null, "authCookie != null");
var csrfToken = new CsrfTokenHelper().GenerateCsrfTokenFromAuthToken(authCookie.Value);
var csrfCookie = new HttpCookie("XSRF-TOKEN", csrfToken) {HttpOnly = false};
HttpContext.Current.Response.Cookies.Add(csrfCookie);
}
Then I have a custom attribute which I can add to controllers to make them check the csrf header:
public class CheckCsrfHeaderAttribute : AuthorizeAttribute
{
// http://stackoverflow.com/questions/11725988/problems-implementing-validatingantiforgerytoken-attribute-for-web-api-with-mvc
protected override bool IsAuthorized(HttpActionContext context)
{
// get auth token from cookie
var authCookie = HttpContext.Current.Request.Cookies[".ASPXAUTH"];
if (authCookie == null) return false;
var authToken = authCookie.Value;
// get csrf token from header
var csrfToken = context.Request.Headers.GetValues("X-XSRF-TOKEN").FirstOrDefault();
if (String.IsNullOrEmpty(csrfToken)) return false;
// Verify that csrf token was generated from auth token
// Since the csrf token should have gone out as a cookie, only our site should have been able to get it (via javascript) and return it in a header.
// This proves that our site made the request.
return new CsrfTokenHelper().DoesCsrfTokenMatchAuthToken(csrfToken, authToken);
}
}
Lastly, I clear the Csrf token when the user logs out:
HttpContext.Current.Response.Cookies.Remove("XSRF-TOKEN");
Can anyone spot any obvious (or not-so-obvious) problems with that approach?
Your code seems to be fine. The only thing is, you don't need most of the code you have as web.api runs "on top" of asp.net mvc, and latter has built in support for anti-forgery tokens.
In comments dbrunning and ccorrin express concerns that you only able to use build in AntiForgery tokens only when you are using MVC html helpers. It is not true. Helpers can just expose session based pair of tokens that you can validate against each other. See below for details.
UPDATE:
There is two methods you can use from AntiForgery:
AntiForgery.GetTokens uses two out parameters to return cookie token and form token
AntiForgery.Validate(cookieToken, formToken) validates if pair of tokens is valid
You totally can repurpose those two methods and use formToken as headerToken and cookieToken as actual cookieToken. Then just call validate on both within attribute.
Another solution is to use JWT (check eg MembershipReboot implementation)
This link shows how to use built in anti-forgery tokens with ajax:
<script>
#functions{
public string TokenHeaderValue()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return cookieToken + ":" + formToken;
}
}
$.ajax("api/values", {
type: "post",
contentType: "application/json",
data: { }, // JSON data goes here
dataType: "json",
headers: {
'RequestVerificationToken': '#TokenHeaderValue()'
}
});
</script>
void ValidateRequestHeader(HttpRequestMessage request)
{
string cookieToken = "";
string formToken = "";
IEnumerable<string> tokenHeaders;
if (request.Headers.TryGetValues("RequestVerificationToken", out tokenHeaders))
{
string[] tokens = tokenHeaders.First().Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
AntiForgery.Validate(cookieToken, formToken);
}
Also take a look at this question AngularJS can't find XSRF-TOKEN cookie
This solution isn't secure since CSRF attacks are still possible as long as the Auth cookie is valid. Both the auth and the xsrf cookie will be sent to the server when an attacker makes you perform a request via another site, and therefore you are still vulnerable until the user does a "hard" logout.
Each request or session should have its own unique token to truly prevent CRSF attacks. But probably the best solution is to not use cookie based authentication but token based authentication such as OAuth. This prevents other websites from using your cookies to perform unwanted requests, since the tokens are used in http headers instead of cookies. And http headers are not automatically send.
Token Based Authentication using ASP.NET Web API 2, Owin, and Identity
AngularJS Token Authentication using ASP.NET Web API 2, Owin, and Identity
These excellent blog posts contain information of how to implement OAuth for WebAPI. The blog posts also contains great information of how to integrate it with AngularJS.
Another solution might be to disable CORS and only accept incoming requests from whitelisted domains. However this won't work for non-website applications, such as mobile and/or desktop clients. Next to that once your website is vulnerable to a XSS attack the attacker will still be able to forge requests on your behalve.
I think your code is flawed. The whole idea around prevent CSRF is to prevent a unique token on each REQUEST, not each session. If the anti-forgery token is a session persisted value, the ability to perform CSRF still remains. You need to provide a unique token on each request...
Haven't had any problems pointed out with the code, so I consider the question answered.
I'm building a website that makes use of Facebook connect. I'm authenticating users client-side with the javascript SDK and calling an AJAX method on my server every time a user logs in to check if the user is known to my app, and if the user is new to store their FBID in my database to register them as a new user.
My question is: Can the access token returned by Facebook to the Javascript SDK be used server-side (with the PHP SDK for example)? Can I send the access token string to the server via an AJAX call, store it in my database (along with a timestamp so I know how long it's valid for) and then use it to make calls to the graph API server-side? Is this even a logical thing to do?
Yes, this should work. Look at this question: How to properly handle session and access token with Facebook PHP SDK 3.0?
This is a workaround for the old JS and new PHP SDK. In my app I send the access token generated by the JS SDK via a form to my PHP. I have no doubts that this also works by sending the access token via ajax!
Using Jquery:
//Set an error message
var oops = ("Put your something went wrong message here.");
//Function to post the data to the server
function save(uid, accessToken){
$.post("../foo/bar", { uid: uid, access_token: accessToken, etc, etc }, function(data){
alert("Successfully connected to Facebook.");
location.reload();
}, "text");
}
function handler(x){
if (x.authResponse){
var token = x.authResponse.accessToken;
var uid = x.authResponse.id;
FB.api("/me/accounts", {access_token: token},
function(response){
if(response.data.length == 0) {
//Regular facebook user with one account (profile)
save(uid, token);
}else{
//Handle multiple accounts (if you want access to pages, groups, etc)
}
});
}else{
alert(oops);
}
}
FB.login(handler, {scope: 'The list of permissions you are requesting goes here'});
Any improvement suggestions are always appreciated.