Grails - Is there a recommended way of dealing with CSRF attacks in AJAX forms? - security

I am using the Synchronizer Token Pattern for standard forms (useToken = true) but I cannot find any recommended method of dealing with this over AJAX.
EDIT
Since posting this, I have rolled my own solution incorporating Grails existing pattern from above.
In the jQuery ajax I post the entire form (which will include Grails' injected SYNCHRONIZER_TOKEN and SYNCHRONIZER_URI hidden fields) such that the withForm closure can perform as expected in the controller.
The problem is, on successful response, there is no new token set (as the page is not reloaded and the g:form taglib is not evoked) and so I do this manually in the controller calling into the same library as the g:form taglib, and return it in the ajax response, and then reset the hidden field value.
See below:
var formData = jQuery("form[name=userform]").serializeArray();
$.ajax({
type: 'POST',
url: 'delete',
data: formData,
success: function (data) {
// do stuff
},
complete: function (data) {
// Reset the token on complete
$("#SYNCHRONIZER_TOKEN").val(data.newToken);
}
})
in the Controller:
def delete(String selectedCommonName) {
def messages = [:]
withForm {
User user = User.findByName(name)
if (user) {
userService.delete(user)
messages.info = message(code: 'user.deleted.text')
} else {
messages.error = message(code: 'user.notdeleted.text')
}
}.invalidToken {
messages.error = message(code: 'no.duplicate.submissions')
}
// Set a new token for CSRF protection
messages.newToken = SynchronizerTokensHolder.store(session).generateToken(params.SYNCHRONIZER_URI)
render messages as JSON
}
Can anyone identify if I have unknowingly introduced a security flaw in the above solution. It looks adequate to me but I don't like hand rolling anything to do with security.

Nice!
IMO, you'd better reset the token at the same time.
SynchronizerTokensHolder.store(session).resetToken(params.SYNCHRONIZER_URI)
and if you have multiple forms in the same page, define a variable to hold tokens returned from each ajax request.
btw, why not implement the token pattern on your own?
Generate a token, e.g., UUID.randomUUID().toString(), and store it into session with the url as the key.
Check and reset the token at the satrt of post actions.

Related

Safest way to persist logged in state when forced to use basic auth

Whats the best/safest way to store credentials for subsequent calls to an API using Basic Auth which I dont have control over?
We're apparently only using basic auth for our beta testing phase thankfully, but I have lived long enough to know that sometimes security details get lost on product people and beta users reuse passwords, so want to make it as secure as I can just in case despite it being a temporary thing.
Currently I am using session storage to store credentials in base64 form. Is that really the best that can be done?
Note that in order to get rid of the ugly browser login prompt, the WWW-Authenticate header has been removed from the server response. This means that the browser no longer 'magically' caches auth info for subsequent calls, so I need to do it manually somehow.
This what I am currently using. While it technically works, are there ways I can decrease the security risks?
const baseUrl = getServerBaseUrl()
const authenticationService = {
authenticate: (username, password) => {
const token = btoa(`${username}:${password}`)
sessionStorage.setItem('credentials', token)
return axios.get(baseUrl, {
headers: {
Authorization: `Basic ${token}`,
},
})
},
checkAuthentication: () => {
return axios.get(baseUrl, {
headers: {
Authorization: `Basic ${sessionStorage.getItem('credentials')}`,
},
})
},
}

Implement Log In With Spotify Popup

Hi I want to implement a Log In with Spotify feature in my website but I don't want to redirect users to a different page, I would like to just open a popup window. An example of the behavior I want is found at https://developer.spotify.com. There when you click on log in, a pop up window is opened so you can log in with spotify without any redirect.
That's how Spotify Developer website does it:
Open a popup window to /api/authorize. Once the user has allowed the application, it will redirect him to the callback page.
On the callback page, use the returned authorization code (GET parameter code) to generate access/refresh tokens by doing a POST request to /api/token (check out the documentation). This should be done on server side because it requires sending client ID and client secret keys.
Store the access/refresh tokens in the localStorage and close the popup.
Detect close event, get the tokens from the localStorage and use them for the API.
Example
Login page:
// Open the auth popup
var spotifyLoginWindow = window.open('https://accounts.spotify.com/authorize?client_id=REPLACE_ME&redirect_uri=REPLACE_ME&response_type=code');
// Close event
spotifyLoginWindow.onbeforeunload = function() {
var accessToken = localStorage.getItem('sp-accessToken');
var refreshToken = localStorage.getItem('sp-refreshToken');
// use the code to get an access token (as described in the documentation)
};
Callback page:
// Assuming here that the server has called /api/token
// and has rendered the access/refresh tokens in the document
var accessToken = "xxx";
var refreshToken = "xxx";
/////////////////////////
// Store the tokens
localStorage.setItem("sp-accessToken", accessToken);
localStorage.setItem("sp-refreshToken", refreshToken);
// Close the popup
window.close();
Following up on Teh's response above. If you don't want to use localStorage, I registered a global window function and simply passed the token as a payload back to parent window. Works well for a pass-through experience like saving playlists.
Popup:
popup = window.open(
AUTHORIZATION_URL,
'Login with Spotify',
'width=800,height=600'
)
Callback Function:
window.spotifyCallback = (payload) => {
popup.close()
fetch('https://api.spotify.com/v1/me', {
headers: {
'Authorization': `Bearer ${payload}`
}
}).then(response => {
return response.json()
}).then(data => {
// do something with data
})
}
Callback Page:
token = window.location.hash.substr(1).split('&')[0].split("=")[1]
if (token) {
window.opener.spotifyCallback(token)
}
I wrote about this technique in more detail on Medium.

How to include access-token in the HTTP header when requesting a new page from browser

The similar question was asked by someone else (here) but got no proper answer. Since this is basic and important for me (and maybe for someone else as well), I'm trying to ask here. I'm using Node.js+Express+EJS on the server side. I struggled to make the token authentication succeeded by using jsonwebtoken at the server and jQuery's ajax-jsonp at the web browser. Now after the token is granted and stored in the sessionStorage at the browser side, I can initiate another ajax request with the token included in the request header, to get the user's profile and display it somewhere in the 'current' page. But what I want is to display a new web page to show the user's profile instead of showing it in the 'current' page (the main/index page of the website). The question is:
How to initiate such an HTTP GET request, including the token in the HTTP header; and display the response as a new web page?
How the Node.js handle this? if I use res.render then where to put the js logic to verify the token and access the DB and generate the page contents?
Or, should we say the token mechanism is more suitable for API authentication than for normal web page authentication (where the web browser provides limited API)?
I think the answer to this question is important if we want to use the token mechanism as a general authentication since in the website scenario the contents are mostly organized as web pages at the server and the APIs at the client are provided by the browser.
By pure guess, there might be an alternative way, which the ajax success callback to create a new page from the current page with the response from the server, but I have no idea of how to realize that as well.
By calling bellow code successfully returned the HTML contents in customer_profile.ejs, but the client side ajax (obviously) rejected it.
exports.customer_profile = function (req, res) {
var token = req.headers.token;
var public_key = fs.readFileSync(path.resolve() + '/cert/public_key.pem');
var decoded = jwt.verify(token, public_key);
var sql = 'SELECT * FROM customer WHERE username = "' + decoded.sub + '"';
util.conn.query(sql, function (err, rows) {
if (!err) {
for (var i = 0; i < rows.length; i++) {
res.render('customer_profile', {customer_profile: rows[i]});
break;
}
}
});
};
I am trying to find a solution to this as well. Please note, I am using Firebase for some functionality, but I will try to document the logic as best as I can.
So far what I was able to figure out is the following:
Attach a custom header to the HTTP request client-side
// landing.js - main page script snippet
function loadPage(path) {
// Get current user's ID Token
firebase.auth().currentUser.getIdToken()
.then(token => {
// Make a fetch request to 'path'
return fetch(`${window.location.origin}/${document.documentElement.lang}/${path}`, {
method: 'GET',
headers: {'X-Firebase-ID-Token': token} // Adds unverified token to a custom header
});
})
.then(response => {
// As noted below, this part I haven't solved yet.
// TODO: Open response as new webpage instead of displaying as data in existing one
return response.text();
})
.then(text => {
console.log(text);
})
.catch(error => {
console.log(error);
});
}
Verify the token according to your logic by retrieving the corresponding header value server-side
// app.js - main Express application server-side file
// First of all, I set up middleware on my application (and all other setup).
// getLocale - language negotiation.
// getContext - auth token verification if it is available and appends it to Request object for convenience
app.use('/:lang([a-z]{2})?', middleware.getLocale, middleware.getContext, routes);
// Receives all requests on optional 2 character route, runs middleware then passes to router "routes"
// middleware/index.js - list of all custom middleware functions (only getContext shown for clarity)
getContext: function(req, res, next) {
const idToken = req.header('X-Firebase-ID-Token'); // Retrieves token from header
if(!idToken) {
return next(); // Passes to next middleware if no token, terminates further execution
}
admin.auth().verifyIdToken(idToken, true) // If token provided, verify authenticity (Firebase is kind enough to do it for you)
.then(token => {
req.decoded_token = token; // Append token to Request object for convenience in further middleware
return next(); // Pass on further
})
.catch(error => {
console.log('Request not authorized', 401, error)
return next(); // Log error to server console, pass to next middleware (not interested in failing the request here as app can still work without token)
});
}
Render and send back the data
// routes/index.js - main router for my application mounted on top of /:lang([a-z]{2})? - therefore routes are now relative to it
// here is the logic for displaying or not displaying the page to the user
router.get('/console', middleware.getTranslation('console'), (req, res) => {
if(req.decoded_token) { // if token was verified successfully and is appended to req
res.render('console', responseObject); // render the console.ejs with responseObject as the data source (assume for now that it contains desired DB data)
} else {
res.status(401).send('Not authorized'); // else send 401 to user
}
});
As you can see I was able to modularize the code and make it neat and clear bu use of custom middleware. It is right now a working API returning data from the server with the use of authentication and restricted access
What I have not solved yet:
As mentioned above, the solution uses fetch API and result of the request is data from server (html) and not a new page (i.e when following an anchor link). Meaning the only way with this code now is to use DOM manipulation and setting response as innerHTML to the page. MDN suggests that you can set 'Location' header which would display a new URL in the browser (the one you desire to indicate). This means that you practically achieved what both, you and I wanted, but I still can't wrap my head around how to show it the same way browser does when you follow a link if you know what I mean.
Anyways, please let me know what you think of this and whether or not you were able to solve it from the part that I haven't yet

nodejs request.post (user, pass) and get data from another url after auth

I want to get some data that are available after authentication. I pass through a post login and password on the page http://site.domain.com/auth.html and I want to get html from another page http://site.domain.com/anotherpage.html
request.post({followAllRedirects: true, url:'http://site.domain.com/auth.html', form:{user:'login#domain.com', pass:'password'}},
function (error, response, body) {
if (!error) {
request('http://site.domain.com/anotherpage.html', function(error, response, html){
fs.appendFileSync('log.txt', html, encoding='utf8');
});
}
});
Authentication takes place normally (there is a message in the html with greeting), after request I get the data as if the authentication is not passed.
fixed result:
var j = request.jar(); var request = request.defaults({jar:j});
and then my code
Most often than not, in the web, authentication information is store as cookies in the user's browser. Because this is a server request, I don't think two "unrelated" requests is going to cut it, as no header information pertaining to the first request is being sent along with the second request. Perhaps you could try this strategy or some other similar procedure to mimic that header interaction.
I found a solution, I put in top of my code, these lines
var j = request.jar(); var request = request.defaults({jar:j});
jar - If true, remember cookies for future use (or define your custom cookie jar;

How does AntiForgeryToken work?

I am applying Security to my .net 3.5 mvc2 web application.
My website doesn't contain any user authentication and consists of many ajax calls in .js files
In my .aspx file I wrote
<%= Html.AntiForgeryToken() %>
In my .js file function I wrote
$(document).ready(function() {
var token = $('input[name=__RequestVerificationToken]').val();
$.ajax({
url: "/Home/getCurrentLanguage/" + Math.random(),
cache: false,
type: "POST",
async: false,
data: {"__RequestVerificationToken":token},
success: function(data) {
if (data == "mr") {
alert("its Marathi");
} else {
alert("its English huh !!!");
}
return false;
},
error: function(data) {
alert("some Error" + data);
}
});
});
In my Controller I wrote
[AcceptVerbs(HttpVerbs.Post), ValidateAntiForgeryToken]
public JsonResult getCurrentLanguage(string id)
{
return new JsonResult
{
Data = "mr"
};
}
This works fine for me,
but I have 2 Questions
Q1. Is it the correct approach ?
If I see the page source, I found this code
<input name="__RequestVerificationToken" type="hidden" value="WFd+q5Mz0K4RHP7zrz+gsloXpr8ju8taxPJmrLO7kbPVYST9zzJZenNHBZqgamPE1KESEj5R0PbNA2c64o83Ao8w8z5JzwCo3zJKOKEQQHg8qSzClLdbkSIkAbfCF5R6BnT8gA==" />
but when I created the external html file and copy this value of __RequestVerificationToken and pass in ajax call, I am getting this error
A required anti-forgery token was not supplied or was invalid.
then
Q2. How does runtime know that this page is supplying the copied __RequestVerificationToken?
This "AntiForgeryToken" is in place to prevent Cross-Site Request Forgery attacks. This system can be undermined by an attacker if your application suffers from a Cross-Site Scripting vulnerability.
This token prevents CSRF attacks because due to the same-origin policy the attacker can send requests but he cannot read the token off of the page to make the request succeed (unless he has an xss vulnerability).
As for Q2, this value must be unique per user and therefore updated each time the page loads. If its just a static value, then its useless at stopping CSRF because the attacker will know this same static value.

Resources