We have a little discussion in my team how to handle http restriction in our app.
In our app a user can create products. So we have routes like /products and /product/1/show to list and show products of a user. A user can not see products of another user. The app uses a REST endpoint to fetch the data. The API call looks like this /api/product/1/ to fetch a single product.
We have more routes/API endpoint for other kinds of entities.
The question is how to protect a route/API request against other users?
We have two solutions:
use the firewall and voters. The voter gets the current url /product/1/show and checks if given product is owned by the current logged in user.
use a voter without the firewall: http://symfony.com/doc/current/cookbook/security/voters_data_permission.html
use the role system: http://jmsyst.com/bundles/JMSSecurityExtraBundle/master/annotations
I prefer solution 1. because all information we need (who is the owner of the product) still exist. We only need to fetch the entity and do a check.
In solution 2. we have to spread the voter logic over several controllers.
Are there recommendations or experiences on this problem?
If i have to choose between those three it would be 1. But i suggest a different route. I assume that the digit 1 in /product/1/show stands for the user number? If that is the case i suggest that you make new routes without the numbers e.g. /my-products/show . The controller must then use the id of the currently logged in user.
Related
I've routes that offer some features to the users. I want new users to access the routes say for 3 times after that they should be routed back to login page.
We can do it in many ways but which one would be the most robust approach?
For e.g we can save the user session in cookies but if user clear the cookies they can access the routes and that is not what I want.
Any help would be appreciated
Storing cookies on the client devices is not a safe solution. Users can delete their cookies. But it can be useful if you combine this with other methods.
You probably don't have a 100% solution as users can change every information you collect from them for unification. But you can make it as hard as possible.
Let's talk about device unification solutions.
IP: You can get the user IP, store it on your backend. And link it with user type to detect if the user has access to content or not. There are some downsides of this solution; you need to implement a system to update user access. This means another complexity at the backend; most of your users have dynamic IP addresses. They can change it, and this will cost them 10 minutes.
Cookie: As you said, they can delete them easily.
Fingerprinting: There are some unique ways to detect devices. This library is one of them. It abuses some browser features to create unique ids. It is a clientside library, and you can create an id and send it to your backend for persisting. The downside of this solution: It blocks the browser for a second or something like that. But even incognito, it creates the same id.
My way: I would combine all of them and use storage with TTL at the backend. My document would be like this:
Random generated device id at cookie: string
Fingerprint: string
IP: string
User features: bit flag value
Visit number for the feature: int
Whenever a user deletes or changes cookies, I would identify the user from IP or fingerprint. Whenever a user changed its IP, I will identify it from other unique ids I have. And also, on each change, I would update this document. When the user bought new features, I would update the bit flag value. Also, if I can't link this visit with any of the previous visits already existing in the database, I would create a new document.
It is a multi-tenant serverless system.
The system has groups with permissions.
Users derive permissions based on the groups they are in.
If it makes a difference, we are using Cognito for authentication and it is a stateless application.
For example:
GET endpoint for sites (so sites that the logged-in user has access to based on the groups they are in)
GET endpoint for devices (so sites that the logged-in user has access to based on the groups they are in)
In REST APIs. "The idea is that the data returned by an endpoint should depend solely on the parameters passed meaning two different users should receive the same result for the identical request.
"
What should the REST URI look like to ensure the above-stated idea? Since the deciding factor for the list here is "groups" and thus effective permissions, I was thinking we could pass the groups a user in, in the URI in sorted order to leverage caching on GET endpoints as well, Is there a better way to do it?
In REST APIs. "The idea is that the data returned by an endpoint should depend solely on the parameters passed meaning two different users should receive the same result for the identical request. "
No this is not strictly true. It can be a desirable property, but absolutely not needed. In fact, if you build a proper hypermedia REST api, you would likely want to hide links/actions that the current user is not allowed to use.
Furthermore, a cache will never store responses and send to different users if an AUthorization header is present on the request.
Anyway, there could be other reasons to want this.. maybe it's a simpler design for your case, and there is a pretty reasonable solution.
What I'm inferring from your question is that you might have two endpoints:
/sites
/devices
They return different things depending on who's accessing. Instead of using those kind of routes, you could just do:
/user/1234/sites
/user/1234/devices
Now every user has their own separate 'sites' and 'devices' collection. The additional benefit is that if you ever want to let a user find the list of sites or devices from another user, the API is ready to support that.
The idea is that the data returned by an endpoint should depend solely
on the parameters passed
This is called the statelessness constraint, but if you check the parameters always include auth parameters because of this. The idea is keeping the session data on the client side, because managing sessions becomes a problem when you have several million users and multiple servers all around the world. Since the parameters include auth data, the response can depend on this data, so you can use here the exact same endpoints for users with different permissions.
As of the responses you might want to send back hyperlinks, which represent the available operations. The concept is the same here, if the user does not have permission for the actual operation, then they won't get a hyperlink for that operation and in theory they should never get a 403 status either, because you must follow the hyperlinks you got from the service instead of hardcoding URI templates into your client. So you have to handle less errors and junk requests, and another reason here that you can change your URI templates without breaking the clients. This is called hypermedia as the engine of application state, it is part of the uniform interface constraint.
Let's say that I'm developing an application similar to Twitter. I can see the profile of a user tom if I visit https://twitter.com/tom, but there are several application reserved endpoints like /login, /logout, /settings, /about, etc.
Twitter actually tells you the reason why you cannot take a certain username:
And I suppose the following message applies to reserved endpoints:
My question is: how does Twitter maintain a list of reserved endpoints for filtering out new registrations? It would appear that there is one check that matches the username against a list of registered users, and a second check that looks for reserved keywords.
Keeping a table with these keywords seems like an obvious answer, but I find one could easily forget to update after each creation of a top level (reserved) page.
You're not going to get an answer to how Twitter does it because that would almost certainly require somebody to break an NDA. I would expect it to be a database table, but I have no inside information.
The preferred practice is to not overload a collection endpoint like that. Better would be
/users/tom
/login
/logout
...
Then your URI structure solves the problem for you.
I was trying to find it in docs or anywhere on the web but I did not find.
What I am asking about?
I am building website for multiple users. Frontend is not important, back backend API is being build in Loopback.
Every user will be assigned to some, let's name it GROUP.
Group content will be then exposed on subdomain but it is not important now.
Users will be kind of admins of their group.
I will have plenty of different models, but I will always have to protect user from accessing elements which not belongs to his group.
How should I do it? I think it will be some middleware but I do not know how to do it properly.
Of course, every user and every element have field "group_id".
I am also trying to find a good solution... I did find this npm package that looks worth a try: https://www.npmjs.com/package/loopback-component-access-groups
Here is a short description of what the package is used for:
"This loopback component enables you to add multi-tenant style access controls to a loopback application. It enables you to restrict access to model data based on a user's roles within a specific context."
I'm struggeling with the same problem, and I did not yet find a satisfatory response.
My workaround is explained in this question. I've got my user ID and with this, I retrieve the data I need to restrict the access. Then I alter the query in accordance with fetched data.
I have a Play framework application that has a model like this:
A Company has one and only one User associated with it.
I have URLs like http://www.example.com/companies/1234, http://www.example.com/companies/1234/departments, http://www.example.com/companies/1234/departments/employees and so on. The numbers are the company id's, not the user id's.
I want that normal users (not admins) should only be able to access their own profile pages, not other people's profile pages. So a user associated with the company with id 1234 should not be able to access the URL http://www.example.com/companies/6789
I tried to accomplish this by overriding Secure.check() and comparing the request parameter "id" to the ID of the company associated with the logged in user. However, this obviously fails if the parameter is called anything else than "id".
Does anyone know how this could be accomplished?
You could have a simple #Before function, or if it is only on the view page that you want to apply the security, then you could have a simple bit of code at the beginning that checks the user's id (I assume from the session), and checks that they are allowed to access the page, by getting the User form the id in the session, and the Company from the id passed in, and checking against each other.
If security fails, then either return a badrequest instead of render, or call an action that shows a notAuthorised custom page.
You could make a SecureProfileController class that extends Controller, has a method that does the checkCompanyId-that-is-to-be-viewed against users companyId, and let the controllers that need that logic extend the SecureController.
If the method is an #Before function, like Codemwnci says, then it can intercept all the action methods in the inherited classes.
Alternatively you could have a look at Deadbolt, where you can setup roles for users and restrict access based on those roles: http://www.playframework.org/modules/deadbolt-1.0/home
Hope that helps :)