ExtJS 4.1 or general Javascript HTTP_Authorization Function - javascript

When making a request using curl --basic --user someuser:somepass http://someurl/ it append a header like this: Authorization: Basic Y2FsaWRvZzpmMDBmM2IwMg==. This is, of course,
Basic access authentication is a method for a web browser or other client program to provide a user name and password when making a request. Before transmission, the user name is appended with a colon and concatenated with the password. The resulting string is encoded with the Base64 algorithm and transmitted in the HTTP header and decoded by the receiver, resulting in the colon-separated user name and password string. Basic access authentication was originally defined in 1996 by RFC 1945 [http://en.wikipedia.org/wiki/Basic_access_authentication][1] [1]: http://en.wikipedia.org/wiki/Basic_access_authentication
I'm looking for a way to have all my ExtJS 4.1 Ext.data.proxy.Rest proxies add this to all requests. It seems like a simple task, but I can find no documentation on it. By the way, I do know how to add headers to proxies generally through headers: {'X_MyHeader':'somevalue'} property. I just do not know how to tell Ext to do it globally for the current username/password.

I was looking for a similar solution for applying OAuth tokens to my Ext.data.proxy.Rest proxies and couldn't find any info about it. Quite shocked there is nothing out there about how to do this. Makes me think I'm going about it the wrong way. Anyway heres what I've come up with to achieve this.
Override Ext.data.proxy.Ajax and keep a static variable so it applies to all proxies. Then merge the set authHeader with the current list of headers so you can still customise per proxy.
Ext.override(Ext.data.proxy.Ajax, {
statics: {
authHeader: undefined,
},
/**
* #cfg {Object} headers
* Any headers to add to the Ajax request. Defaults to undefined.
*/
doRequest: function(operation, callback, scope) {
var writer = this.getWriter(),
request = this.buildRequest(operation, callback, scope);
if (operation.allowWrite()) {
request = writer.write(request);
}
console.log(this.headers);
Ext.apply(request, {
headers : Ext.apply(this.headers, Ext.data.proxy.Ajax.authHeader) || Ext.data.proxy.Ajax.authHeader,
timeout : this.timeout,
scope : this,
callback : this.createRequestCallback(request, operation, callback, scope),
method : this.getMethod(request),
disableCaching: false // explicitly set it to false, ServerProxy handles caching
});
Ext.Ajax.request(request);
return request;
}
});
Then I look for my token in local storage, if it's there set it globally, otherwise prompt for a login.
MyApp.model.AuthToken.load(1, {
callback: function(record) {
// If we don't have anything in local storage for the user, show the login box.
if (record.get('access_token') == '') {
var window = Ext.create('MyApp.view.Login');
window.show();
} else {
Ext.data.proxy.Ajax.authHeader = { 'Authorization': 'Bearer ' + record.get('access_token') };
}
}
});
There should be some extra logic in there for handling token expiry and refresh requests, but you get the idea!

Related

How can I make a Cloudflare worker which overwrites a response status code but preserves the rest of the response?

Specifically I am interested in changing all responses with code 403 to code 404, and changing all responses with code 301 to 302. I do not want any other part of the response to change, except the status text (which I want to be empty). Below is my own attempt at this:
addEventListener("fetch", event => {
event.respondWith(fetchAndModify(event.request));
});
async function fetchAndModify(request) {
// Send the request on to the origin server.
const response = await fetch(request);
const body = await response.body
newStatus = response.status
if (response.status == 403) {
newStatus = 404
} else if (response.status == 301) {
newStatus = 302
}
// Return modified response.
return new Response(body, {
status: newStatus,
statusText: "",
headers: response.headers
});
}
I have confirmed that this code works. I would like to know if there is any possibility at all that this overwrites part of the response other than the status code or text, and if so, how can I avoid that? If this goes against certain best practices of Cloudflare workers or javascript, please describe which ones and why.
You've stumbled on a real problem with the Fetch API spec as it is written today.
As of now, status, statusText, and headers are the only standard properties of Response's init structure. However, there's no guarantee that they will remain the only properties forever, and no guarantee that an implementation doesn't provide additional non-standard or not-yet-standard properties.
In fact, Cloudflare Workers today implements a non-standard property: webSocket, which is used to implement WebSocket proxying. This property is present if the request passed to fetch() was a WebSocket initiation request and the origin server completed a WebSocket handshake. In this case, if you drop the webSocket field from the Response, WebSocket proxying will break -- which may or may not matter to you.
Unfortunately, the standard does not specify any good way to rewrite a single property of a Response without potentially dropping unanticipated properties. This differs from Request objects, which do offer a (somewhat awkward) way to do such rewrites: Request's constructor can take another Request object as the first parameter, in which case the second parameter specifies only the properties to modify. Alternately, to modify only the URL, you can pass the URL as the first parameter and a Request object as the second parameter. This works because a Request object happens to be the same "shape" as the constructor's initializer structure (it's unclear if the spec authors intended this or if it was a happy accident). Exmaples:
// change URL
request = new Request(newUrl, request);
// change method (or any other property)
request = new Request(request, {method: "GET"});
But for Response, you cannot pass an existing Response object as the first parameter to Response's constructor. There are straightforward ways to modify the body and headers:
// change response body
response = new Response(newBody, response);
// change response headers
// Making a copy of a Response object makes headers mutable.
response = new Response(response.body, response);
response.headers.set("Foo", "bar");
But if you want to modify status... well, there's a trick you can do, but it's not pretty:
// Create an initializer by copying the Response's enumerable fields
// into a new object.
let init = {...response};
// Modify it.
init.status = 404;
init.statusText = "Not Found";
// Work around a bug where `webSocket` is `null` but needs to be `undefined`.
// (Sorry, I only just noticed this when testing this answer! We'll fix this
// in the future.)
init.webSocket = init.webSocket || undefined;
// Create a new Response.
response = new Response(response.body, init);
But, ugh, that sure was ugly.
I have proposed improvements to the Fetch API to solve this, but I haven't yet had time to follow through on them. :(

Many AJAX requests at once with CSRF protection

Hi everybody.
My web application is based on asynchronous requests. Timer widget is working and updating it's status every second by AJAX (yes, it is necessary).
I am sending with each AJAX my CSRF tokens:
project_data.append(csrf_name_key,csrf_name_value);
project_data.append(csrf_value_key,csrf_value_value);
And in response I am updating that global variables:
function setCSRF(response) {
csrf_name_key = response.nameKey;
csrf_name_value = response.name;
csrf_value_key = response.valueKey;
csrf_value_value = response.value;
}
Everything is generally fine. But if I will do another AJAX for example when I change task in todo list to "done" it sometimes ending with error because I am sending AJAX before I am getting new tokens from previous request.
I really don't know how to do solve that problem. First idea was that I will make "like stack array" with 5 different tokens but one https request = one pair of tokens and I can't generate it.
Maybe some type of queue of ajax requests, but what with doing them in a right time - I don't know.
My actual pseudo-solution is "if failed try again max 10 times":
if(e.target.response=="Failed CSRF check!") {
if(failedAjax<10) checkForSurvey();
failedAjax++;
return;
}
It is generally working, but errors are appears in a console and it is very dirty solution.
I am using Slim 3 microframework with CSRF extension. Really please for help with that interesting problem.
I will be very thankful,
Arthur
There are some options for you:
Use a stack of csrf-tokens inside you javascript code
Use a csrf token which is can be used more than once (not so secure)
Use a queue for the request
A stack for the tokens
The Slim-Csrf-middleware provides functionallity for you, to generate these tokens, you just need to get them to the clientside.
You could do an api for getting 5 csrf tokens, this api would also consume on csrf-token.
Add an api and generate the tokens there.
$app->get('/foo', function ($request, $response, $args) {
// check valid csrf token
$tokens = [];
for ($i = 0; $i < 5; $i++) {
$tokens[] = $this->csrf->generateToken();
}
return $response->withJson($tokens);
});
Now the csrf-token are valid through the whole user session.
Guard::generateToken() returns something like this:
array (size=2)
'csrf_name' => string 'csrf58e669ff70da0' (length=17)
'csrf_value' => string '52ac7689d3c6ea5d01889d711018f058' (length=32)
A multi-use csrf-token
For that, Slim-Csrf already provides functionallity with the token persistance mode. That can be enabled through the constructor or the Guard::setPersistentTokenMode(bool) method. In my example, I'm doing this with the method:
$container['csrf'] = function ($c) {
$guard = new \Slim\Csrf\Guard;
$guard->setPersistentTokenMode(true);
return $guard;
};
Here the PhpDoc from the persistanceTokenMode-attribute
/**
* Determines whether or not we should persist the token throughout the duration of the user's session.
*
* For security, Slim-Csrf will *always* reset the token if there is a validation error.
* #var bool True to use the same token throughout the session (unless there is a validation error),
* false to get a new token with each request.
*/
A queue for the ajax requests.
Add a queue for the request, that could be delay the execution of your request but there will always be a valid csrf token.
This should be seen as pseudocode as I havn't tested this yet.
var requestQueue = [];
var isInRequest = false;
var csrfKey = ''; // should be set on page load, to have a valid token at the start
var csrfValue = '';
function newRequest(onSuccessCallback, data) { // add all parameters you need
// add request to the queue
requestQueue.push(function() {
isInRequest = true;
// add to csrf stuff to data
$.ajax({
data: xxx
url: "serverscript.xxx",
success: function(data) {
// update csrfKey & csrfValue
isInRequest = false;
tryExecuteNextRequest(); // try execute next request
onSuccessCallback(data); // proceed received data
}
}});
);
tryExecuteNextRequest();
}
function tryExecuteNextRequest() {
if(!isInRequest && requestQueue.length != 0) { // currently no request running &
var nextRequest = requestQueue.shift();
nextRequest(); // execute next request
}
}
Generally, you can simply eliminate CSRF by not accepting cookies for authentication.
You can save the authentication token in the localStorage and send it as an header with each request.
This way you'll never need to worry about CSRF and its tokens.

Flickr API throws up "Invalid API Key (Key has invalid format)" when POSTing data with oauth signing

I can't seem to find this answered anywhere on SO or even Google - I have an oauth-signed call to the Flickr upload API, and following the docs I've signed the POST operation the usual oauth way (but without the photo data). For testing purposes I've only passed along a title and the photo data, which means I end up a var flickrURI that contains the following url for POSTing to:
https://api.flickr.com/services/upload/
? format=json
& oauth_consumer_key=...
& oauth_nonce=2e57b73fec6630a30588e22383cc3b25
& oauth_signature_method=HMAC-SHA1
& oauth_timestamp=1411933792346
& oauth_token=...
& title=test
& oauth_signature=O7JPn1m06vl5Rl95Z2in32YWp7Q%3D
(split over multiple lines for legibility in this question, the actual URL has no whitespacing around the ? and & for obvious reasons).
The oauth signing itself is quite correct, and code used for accessing the not-upload-API all over the place with correct behaviour, so it seems pretty much impossible for that to get the signing wrong, other than perhaps signing with "not enough data" or perhaps signing with "too much data".
The auth signing first forms the auth query string, in this case:
oauth_consumer_key=...
&oauth_nonce=60028905f65cf9d7649b3bce98f718f8
&oauth_signature_method=HMAC-SHA1
&oauth_timestamp=1411939726691
&oauth_token=...
&title=test
which is then used to form the verb + address + encoded base string:
POST&https%3A%2F%2Fapi.flickr.com%2Fservices%2Fupload%2F&oauth_consumer_key%3D...%26oauth_nonce%3D60028905f65cf9d7649b3bce98f718f8%26oauth_signature_method%3DHMAC
-SHA1%26oauth_timestamp%3D1411939726691%26oauth_token%3D...%26title%3Dtest
This is then HMAC-SHA1 digested using the Flickr and oauth secrets:
function sign = (data, key, secret) {
var hmacKey = key + "&" + (secret ? secret : ''),
hmac = crypto.createHmac("SHA1", hmacKey);
hmac.update(data);
var digest = hmac.digest("base64");
return encodeURIComponent(digest);
}
And for GET requests, this works perfectly fine. For POST requests things seem to be difference, despite the docs not explain which part is supposedly different, so I the tried to use the Nodejs request package to perform the POST action in what seemed a normal way, using:
uploadOptions = {
oauth_consumer_key = auth.api_key,
oauth_nonce = auth.oauth_nonce,
oauth_timestamp = auth.oauth_timestamp,
oauth_token = auth.access_token,
oauth_signature_method = "HMAC-SHA1",
title: title,
photo: <binary data buffer>
};
flickrURL = formSignedURL(auth);
request.post({
url: flickrURI,
headers: {
"Authorization": 'oauth_consumer_key="...",oauth_token="...",oauth_signature_method="HMAC-SHA1",oauth_signature="...",oauth_timestamp="...",oauth_nonce="...",oauth_version="1.0"'
},
multipart: [{
'content-type': 'application/json',
body: JSON.stringify(signOptions)
}]
},function(error, response, body) {
console.log("error");
console.log(error);
console.log("body");
console.log(body);
}
);
which yields a body that contains:
<?xml version="1.0" encoding="utf-8" ?>
<rsp stat="fail">
<err code="100" msg="Invalid API Key (Key has invalid format)" />
</rsp>
As the oauth signing doesn't really give me a choice in which API key to provide (there is only one) and the signing works just fine for the not-upload API, I can't figure out what this error message is trying to tell me. The key is definitely the right format because that's the format Flickr gives you, and it's the correct value, because it works just fine outside of uploading. I also made sure to get the oauth token and secret for that key with "delete" permission (widest possible permissions) so the included access token and access token secret should pass the "does this token for this key have permissions to write" test.
What obvious thing am I missing here that's preventing the upload to go through?
It looks like you're using the https://up.flickr.com/services/upload/ endpoint, which uses the old authentication scheme.
For OAuth, it should be https://api.flickr.com/services/upload/. Make sure the endpoint is included in signing process.
I don't think it's documented anywhere, but I remember having same issue a while back.
It turns out adding the data as request.post multipart information isn't good enough, and will make the Flickr API throw an "Invalid API Key (Key has invalid format)" error instead of saying what's actually wrong. The following request call, with exactly the same data, works:
var uploadOptions = ...
var flickrURL = ...;
var req = request.post(flickrURL, function(error, response, body) {
callback(error, body);
});
var form = req.form();
uploadOptions.photo = fs.createReadStream(...);
Object.keys(photoOptions).forEach(function(prop) {
form.append(prop, photoOptions[prop]);
});
Despite not making all that much sense call wise (why would the POST not already be done by the time we call form = req.form()?) this is request's "proper" way to send the POST payload over the wire, and makes the Flickr API process the photo upload just fine.

Angular against Asp.Net WebApi, implement CSRF on the server

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.

MVC4 Razor. How to pass a javascript variable to server?

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.

Categories