Symfony 2 - Secure Ajax Controller - ajax

I have ajax controler to store actions that are called through the AJAX from JS.
Here in every action I validate if request is made by AJAX and no other:
if (!$request->isXmlHttpRequest()) {
return new JsonResponse(array('message' => 'You can access this only using Ajax!'), 400);
}
Now the problem is that not every ajax controller action should be called by everyone, but rather depending on a role of the logged in user.
The request to action is made by AJAX from JS, but since actions are in controller I am still able to get logged in user object by $this->getUser() and to check if user has acceptable ROLE to execute the controller action by isGranted().
Example:
if (!$authorizationChecker->isGranted('ROLE_ADMIN')) {
return new JsonResponse(array('message' => 'This can be performed only by admin!'), 400);
}
Should I check ROLES from inside controller in every action or try to configure access_control for ajax routes in security.yml?
I have no idea what is the big difference between these two approaches, but would like to know which one would be more practical and could keep my ajax actions more secure.

Depends on the number of AJAX Controllers you have. If you have so many controllers those are to be allowed only for a certain role, access_control in security.yml is a good choice. Else you can have the condition in each controller.
For dynamic permission layer, where permission to be decided based on subject attribute(the viewing content), You might want to use voter.

Related

How do we protect our post data processing method in CodeIgniter controller from the external form?

I'm working on a small project with CodeIgniter which handling some post data submitted from the admin form page.
I do transfer the post data to a method in my controller and send it to the database.
Its working all the time.
Im thinking, what if someone make an external form with the exact same inputs name and action attribute with mine in the admin page (I dont know how to figure the inputs name out but this is just my wonder), and try to post some data to the controller?
I try to use session but I wonder if there are any way to protect that kind of inject method?
you can try before your form_validator with
if( $_SERVER['HTTP_REFERER'] == base_url()){
//form validator
}
else{
$this->session->set_flashdata('warning', 'You try to enter from an external web without permission');
redirect(base_url(), 'refresh');
}
There are a couple of things you can do.
First, since this is an admin only page I assume you have some kind of login and user verification in place.
You can use session data to store the successful admin login.
//admin log in OK
$this->session->set_userdata('admin_logged_in', TRUE);
In the method that processes the form post, confirm the user is logged in
if($this->session->userdata('admin_logged_in') !== TRUE)
{
redirect('somewhere_else');
return; //here in case the redirect call doesn't work
}
Second, since you are 'posting' to this page confirm that is the method the server has received - it MUST be post. If you are using CI version => 3.0 the do this
if($this->input->method() !== 'post')
{
//somebody is trying to fool you
redirect('somewhere_else');
return; //here in case the redirect call doesn't work
}
If you are using an earlier version of CI (before 3.0.x) do this
if(strtolower($this->server('REQUEST_METHOD') !== 'post')
{
//somebody is trying to fool you
redirect('somewhere_else');
return; //here in case the redirect call doesn't work
}
You also might want to consider the case where the session info checks out but it was not a POST request. That is very suspicious to me and it might be wise to destroy the session before redirecting.

Convention for a controller that generates a view from several models

In Sails.js, a route is set up against a controller method which can render a view. For the most part, this is straightforward, i.e. you could set up a GET /users route that points to UserController.find (which is usually set up automatically anyway).
However, say the home page of a blog renders the 10 most recent posts in the main section and a column with a list of authors and categories. The controller method has to fetch posts, authors, and categories before rendering the view and sending it back to the client. Clearly, a method like this doesn't really belong in PostController, AuthorController, or CategoryController.
What's the best thing to do in this situation? Create a controller for rendering views that rely on data from multiple models? Is there a good name for such a controller?
Thanks!
What I would do (this is purely opinion-based) is creating a PageController and create an action for each page you'd want.
For your home page example you can create a home action, get whatever you need and then render it with res.ok() (if everything is fine).
Another option would be to use Sails as a pure API and use HTTP requests (Ajax) or sockets to get your data in JSON. If you want to do so, I'd advise you to use a front end framework such as Angular, Ember, React...
By the way you could also create actions rendering HTML in your existing controllers and create a route to hit them through Ajax requests and just print them in your page. I'd prefer the 2nd solution because it takes full advantage of the Blueprint API (you don't need new controller or action whatsoever).
As Yann pointed out, this answer has to be a little opinionated. It seems that you are using the views system and not building a single page application. For the home page, I would go for an IndexController.js file with a home(req, res) action.
// api/controllers/IndexController.js
module.exports = {
home: function (req, res) {
// Retrieve all the information you need
// Take care about managing the asynchronous calls before rendering the view
return res.view('homepage');
}
};
Declare the route
// config/routes.js
module.exports.routes = {
'get /': 'IndexController.home'
}
Create the view in views/homepage.ejs.

Registering routes with Laravel but make them unaccessible

I am trying to make a single page CRUD application with Laravel. I will use ajax to create, edit and delete my entity, and also to render partial views. The corresponding controller methods will process the information and return the views.
I want to register the routes so I can call the different methods when necessary. I don't see any other way:
However, registering them so I can do something like this {{ Form::open(['route' => ['cities.store', $city->id]]) }} will allow access via the URL, and I only want to make those routes accessible through the tools I am going to create in that one page CRUD.
I can only think of applying a before filter, but what would be the filter? Also, any other ideas on how I should approach this situtation?
I've had to do something similar with a web service I created. Basically, I wanted only my app to be able to access the routes I created.
What I ended up doing was adding a hashed key to each request being sent, then checking for this key value in the controller. So, only if the key is present and matches the one sent would you then process the request.
Or, if you're using forms, you could do something like the following:
//check if request was sent from our form
if ( Session::token() !== Input::get( '_token' ) ) {
return Response::json( array(
'msg' => 'Unauthorized access attempt'
) );
}
Hope this helps.
another way that doesnt need tokens but is less secure, you got to know what you need,
is using laravels request information
if (Request::ajax())
{
//your action
}else{
//error
}
note this only works when your application always uses ajax you could even type this in your before filter and add it to all needed routes

Laravel Session data not written/update/availabe in View when using AJAX

When I add things to the Session with Session::put() in my controller action, then that data is not available in my view, with Session::get() when doing AJAX request.
The same problem goes for the Former package, which I use for nice form building. It relies on passing some info via the Session, which is used to mark fields as valid/invalid. This functionality is also not working when using AJAX.
I set a view like always, in my View:
$this->layout->content = View::make('account.login')
For AJAX requests, I do NOT render the normal way with layout, but instead get the specific "content" section of the template and return it:
$this->layout->content->renderSections()['content']
When I do a "normal" request, then Session data works fine.
When I do an AJAX request, then Session data set in the controller DURING the AJAX call is ignored. Any Session data set BEFORE the AJAX call is available.
I'm wondering if Laravel has some issue with Session under AJAX calls, or with the the "renderSection()" method above?
I have checked all the obvious problems:
AJAX request uses the same session ID as non-AJAX request.
GET/POST verbs are used correctly etc.
Replicate:
In CONTROLLER action: Session:put('foo','bar');
In VIEW file (in the content part): Session:put('foo2','bar2');
In VIEW file (in the content part): var_dump(Session::get('foo','bar')); // Returns 'bar' in non-AJAX calls, but returns nothing for AJAX calls (!!!)
In VIEW file (in the content part): var_dump(Session::get('foo2')); // Returns 'bar2' in both AJAX and non-AJAX calls as expected.
It seems like the Session values set in the controller action ARE LOST when it renders the view. Therefore my question if this is 1) an AJAX vs. SESSION issue in Laravel, or 2) an Session vs. renderElement() problem that I am not aware of?
I had the same problem and just found a potential solution:
I found a similar problem relating to laravel 3. For the session to persist in an ajax call you need to return the response correctly.
return json_encode($response);
This is causing the problem. It's not it appears a valid response to enable the session to persist. Change it to:
return Response::json($response);
This enables the session to persist!
For some reason a normal form submit or call to the method allows the first one but ajax does not.
I've seen references elsewhere about echo statements in the method affecting the session - the return I suppose must behaving similar to an echo
This is the post that triggered the solution:
http://forumsarchive.laravel.io/viewtopic.php?id=1304

Use CodeIgniter form validation in a view

I have footer view that's included on all my pages which contains a form. I would like to be able to make use of CI's form validation library to validate the form. Is that possible?
Currently the form posts back to the current page using the PHP_SELF environment variable. I don't want to get it to post to a controller because when validation fails it loads the controller name in the address bar, which is not the desired behaviour.
Any suggestions gratefully received.
Thanks,
Gaz
One way, whilst far from ideal, would be to create a "contact" function in every controller. This could be in the form of a library/helper.
CI doesn't natively let you call one controller from another, although I believe there are extensions that enable this.
Another option would be an AJAX call instead, which would allow you to post to a generic controller, validate etc whilst remaining on the current page.
In this use case, I would definitely go for an AJAX call to a generic controller. This allows you to show errors even before submitting in the origin page.
Another way (slightly more complex), involves posting your form data to a generic controller method, passing it a hidden input containing the current URL.
The generic controller method handling your form can then redirect to the page on which the user submitted the form, passing it the validation errors or a success message using flash session variables: $this->session->set_flashdata('errors',validation_errors()) might do the trick (untested)
The good thing about this is that you can use the generic form-handling method for both the ajax case (suppressing the redirect) and the non-ajax case
AJAX would be best, just like everyone else says.
I would redirect the form to one function in one controller, you could make a controller just for the form itself. Then have a hidden value with the return URL. As far as errors go you could send them back with flashdata.
Just remember to never copy paste code, it a bad practice and guarantees bugs.
//make sure you load the proper model
if ($this->form_validation->run() == FALSE){
// invalid
$redirect = $this->input->post('url');
$this->session->set_flashdata('errors',validation_errors());
redirect($redirect);
} else {
/*
success, do what you want here
*/
redirect('send them where ever');
}

Resources