I am not sure if I'm getting it right what Spring Security is capable of.
What my problem is, is that I want to prevent a logged in user to send arbitrary IDs to my server and therefore access data that does not belong to him. But every tutorial I can find is about a simple login procedure. But how can I use that to get rid of
if(item .getStore().getId() == store.getId()) { /* .. */ }
in this example:
// StoreService.java
#Transactional
public ItemDTO deleteItem(String sessionId, Long storeId, ItemDTO itemDto) {
// sessionId is the cookie I have placed in my database
// This way I want to ensure that I am only accessing a store
// that is associated with the logged in store owner (the user basically)
Store store = this.storeOwnerRepository.getStore(sessionId, storeId);
Item item = ConvertDTO.convertItem(store, itemDto);
// THIS CHECK IS WHAT I WANT TO GET RID OF:
// Check if the store ID that I got using the cookie is the
// same ID as the store ID from the item that should be deleted
if(item.getStore().getId() == store.getId()) {
item = this.storeOwnerRepository.deleteItem(item);
} else {
// If this didn't work we have a potentially hostile user:
throw new RuntimeException("Is somebody trying to delete items from a store he doesn't own?");
}
itemDto = ConvertEntity.convertItem(item);
return itemDto;
}
using Spring Annotations? Is that even possible with Spring Security?
Another thing that might work would be Hibernate Filters but I am not sure if I want my database to know about security aspects of my data.
So I am quite confused about how to do that correctly. Any ideas?
We've implemented this kind of security on domain objects with Spring's ACL API. This involves:
creating an implementation of Spring's org.springframework.security.acls.model.AclService interface that knows how to return the permissions a given principal has on a given domain object. E.g. if the principal has relationship foo to this domain object, then grant READ and WRITE permissions; if relationship bar, then grant READ, WRITE, and DELETE permissions.
adding to the service methods that operate on the domain objects annotations like org.springframework.security.access.prepost.PreAuthorize and org.springframework.security.access.prepost.PreAuthorize that define the access-control assertions to be enforced. E.g. this method requires the current authenticated user to have the "WRITE" permission on the argument of type X, or that method requires the current authenticated user to have the "READ" permission on the return object. If either assertion fails, an AccessDeniedException will be thrown.
adjusting your Spring Social config to turn on method-level security. I used the global-method-security element in Spring Security's XML namespace.
There are a lot of details to consider, but we use this approach in several web applications to good effect. It allows you to separate the who-gets-what-permissions-on-which-objects logic from the what-permissions-are-needed-to-perform-this-action logic, and it keeps both away from your database queries.
Of course, in some cases you'll want to enforce access control in the queries instead of querying first, and then filtering the results. I've seen the term "early binding" used to describe enforcement of access control in database queries, and "late binding" used to describe access control on the results of the queries. The Spring Security ACL API is a very good, robust solution for late binding.
You would end up with business service methods like:
#PostAuthorize("hasPermission(returnObject, 'READ')")
public MyItem getMyItem(Long id) {
return dao.getMyItem(id);
}
#PreAuthorize("hasPermission(#toDelete, 'DELETE')")
public void deleteMyItem(MyItem toDelete) {
dao.delete(toDelete);
}
And an AclService with a method like:
public Acl readAclById(ObjectIdentity objectIdentity, List<Sid> sids) throws NotFoundException {
/*
examines objectIdentity which identifies domain object in question, and sids which identifies the principal who wants permissions on the domain object, then returns an ACL instance with permission grants on that domain object for that/those principals
*/
return new AclImpl(...);
}
And the following in your applicationContext-security.xml:
<beans:bean id="permissionEvaluator"
class="org.springframework.security.acls.AclPermissionEvaluator">
<beans:constructor-arg ref="aclServiceImpl" />
</beans:bean>
<beans:bean id="expressionHandler"
class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<beans:property name="permissionEvaluator" ref="permissionEvaluator" />
</beans:bean>
<global-method-security pre-post-annotations="enabled">
<expression-handler ref="expressionHandler" />
</global-method-security>
Probably you should implement Spring security and work roles and permissions, in this way you will be able to ensure you wont get requests by users that are not admin (by delimiting the method with #Secured("ROLE_SOMEROLE")) , this can help you in case in the future you will have other roles.
Then you should work more with role permissions
Spring Security with roles and permissions
And then add permissions over stores, to read or write them. You can associate many permissions to a user, so, can read/write/whatever only to the stores you want.
Check this tutorial, it could help you.
http://slackspace.de/articles/roles-permissions-with-spring-security-3/
http://springinpractice.com/2010/10/27/quick-tip-spring-security-role-based-authorization-and-permissions
I think what you are talking about has more to do with validation than security.
As long as you store data for multiple clients/customers in the same database, you must take care to prevent users from inadvertently (or maliciously) accessing each other's data.
I suggest that you perform this validation at the web service layer and keep the business logic focused on the details of what needs to be done.
Related
I have a Spring Boot REST API that I'm building. Im slightly stuck on the correct way to design my API in a way that protects each individual users' data. For example, consider the following database relations:
User -> (Has Many) Projects -> (Has Many) Tasks. (A User has-many Projects, and a Project has-many tasks).
For example, if I design my endpoints in the following way:
GET /api/v1/projects/{projectId}
POST /api/v1/projects/{projectId}/tasks
Just as a simple example for the above, how can I make sure, when creating new tasks for a certain project, that the project belongs to the logged in user?
Currently, I am using JWT tokens via Spring Security as my authentication strategy, and included in the payload of the token I have my Users' id. So with every request I can retrieve the user, but surely that's incredibly inefficient to be making so many requests to the database and check if the user actually has a given project.
Some solution I was thinking about is to simply have endpoints designed like this:
/api/v1/users/{userId}/projects/{projectId}/tasks
And then I can use the user id in the JWT payload and compare it to the user id in the request parameter. But then that would mean with every new relation in my database, the length of the url is going to be massive :) Also I guess it would mean all the business logic would be inside the User service for the whole application, right? Which seems a little odd to me... but maybe I'm wrong.
Im not sure if thats an issue or not, but just trying to design the API to be as elegant as possible.
Thanks again!
Checking if the user has permissions to a project on every request is the correct solution. Consider cases when many other applications / users are calling your API. You want to make sure that your API is as secure as possible and cannot be manipulated from the frontend.
To make it more efficient you should have a way/query to check associations in your database like a simple query that returns true/false which should be quicker than retrieving all the data and comparing in Java code.
And when possible combine multiple database queries into one, like for one of your examples:
GET /api/v1/projects/{projectId}
in this case, don't run a query to get a user's information and a query for the requested project. Instead you could do a single query with a join between the user's table and the project table which should only return a project if the user is associated with it. The best way really depends on how your database is structured.
Adding a user id into the API URL is just redundant information. Just because the user id in the token matches the user id in the URL doesn't mean the user has any kind of permissions to any project.
Another solution to be avoided is to include the user's project ids in the JWT token which you can then compare without making a database request. This is bad for several reasons:
The token should only have required information for the user to access the API, it shouldn't have business logic
Depending on how much business logic you store in the token the token can become large in size. See this post for a discussion on size limits: What is the maximum size of JWT token?
If there is a way for the someone other than the user (like admin) to add/remove a user's association to a project then that change will not be reflected in the token until the token's data is refreshed
EDIT:
On the spring side I have used the #PreAuthorize annotation before to handle these types of method checks. Below is pseudo code as an example.
#RestController
public class MyController {
#PostMapping
#PreAuthorize("#mySecurityService.isAllowed(principal, #in)")
public SomeResponseType api1(SomeRequestType requestData) {
/* this is not reached unless mySecurityService.isAllowed
returns true, instead a user gets a 401/403 HTTP response
code (i don't remember the exact one) */
}
}
#Service
public class MySecurityService {
/*
Object principal - this is spring's UserDetails object that is
returned from the AuthenticationProvider. So basically a Java
representation of the JWT token which should have the
user's username.
SomeRequestType requestData - this is the request data that was
sent to the API. I'm sure there is a way to get the project ID
from the URL here as well.
*/
public boolean isAllowed(Object principal, SomeRequestType requestData) {
/*
take the user's username from the principal, take the
project ID from the request data and query the database
to check authorization, return true if authorized
make this check efficient
*/
return false;
}
}
The annotation and the security service can then be applied to multiple methods. You can have many different security services depending on what your are checking.
There are other ways available too https://www.baeldung.com/spring-security-method-security and this has to be enabled in spring's configuration (also explained in the link).
Hi so if I understood it correctly you want to automatically assign the task that is going to be created with "POST /api/v1/projects/{projectId}/tasks" to the current logged in user.
You could try to add a Parameter 'Principal principal' to your rest controller. The Principal is the user that is sending the request.
After you have your Prinicipal, you could write a simple convert method(for example: convertPrincipalToUser(Principal principal) which returns you the user. Finally you can add your user to the corresponding task)
Here is some more information about it:
https://www.baeldung.com/get-user-in-spring-security
I'm using Spring Security to Secure my Web App.
I have a page where I show foo objects for administrators.
<intercept-url pattern="/show_foo/**" access="hasRole('ROLE_ADMIN')" />
But now I have a requirement that a foo cannot be seen by all the Administrators, for example only administrators with city="New York" can access to the element.
I've did something in my controller to solve this :
#RequestMapping(method=RequestMethod.GET,value="/show_foo"
public ModelAndView showfunction(Principal user)
{
User user2 = userService.getUserByName(user.getName());
if(/* some checks on user2 */)
/* show page */
else
/* show error page*/
}
So my question is : can I avoid the database call, because I need this almost in all of my pages and I find it ugly to check each time at the top of any controller the same thing over and over. Is there a Spring Security feature for this kind of use cases?.
With Expression based rules you can accesss principal even on rule. See: http://docs.spring.io/spring-security/site/docs/3.0.x/reference/el-access.html
For example if you can include needed information on principal object.
<intercept-url pattern="/show_foo/**" access="hasRole('ROLE_ADMIN') and principal.name=='xyzzy' " />
you have to put in some logic.
1.) Either load the user and country mapping and store somewhere in Static HashMap, remember to update the map if any changes done in mapping, can store same at session level.
2.) Load entries in 2nd level cache, or make queries cacheable, enable query caching as well.
You need to integrate Spring Security with Domain-ACLs. See a full explanation here.
Yo can consider mapping the relationship between Administrators and Cities using
ACL_OBJECT_IDENTITY instances.
I'm using Shiro to secure my Spring MVC webapp. I'm using Hibernate for persistence and so I have a HibernateRealm to get and populate an AuthenticationInfo object.
#Override
#Transactional
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
Account account = accountDao.findByUsername((String)token.getPrincipal());
SimplePrincipalCollection principals = new SimplePrincipalCollection(account, getName());
SimpleAccount info = new SimpleAccount(principals, account.getPassword());
return info;
}
Account is my custom user class. I use the DAO to retrieve an Account by username. I was wondering if there is any point in making this method #Transactional. This is a read only operation after all.
I'm also having the following problem: the DAO does sessionFactory.getCurrentSession() to get a session, but I'm getting a
HibernateException: No Session found for current thread
when the method gets called. I have these in my application context:
<tx:annotation-driven transaction-manager = "transactionManager" />
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
I can't understand why Spring isn't opening a session for me.
Edit: To login, we do this in a Spring #Controller method using Shiro's Subject
#RequestMapping(value = "/account/login", method = RequestMethod.POST)
public String login(#RequestParam("username") String username, #RequestParam("password") String password) {
Subject currentUser = SecurityUtils.getSubject();
if (!currentUser.isAuthenticated) {
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
currentUser.login(token);
return "profile";
}
return "home";
}
Internally, Shiro uses the realm method I have above to get the stored username/password information. It uses an #Autowired DAO to check my database for the right account. It then matches the passwords with a CredentialsMatcher implementation.
So you have two problems. It is usually better to split such questions into two, since these problems are not really connected to each other.
No Session found for current thread
It seems that #Transactional annotations does not work. To be sure you may run you code or tests in Debug mode and look for the JdkDynamicAopProxy or something similar in the stack - if it is present, than your Realm is invoked through transactions-intercepting proxy, but I suppose that there is no proxy curently. For it to work you need to take from the SpringContext not the HibernateRealm directly but the interface that this realm is implementing. This is due to the fact that built-in standard java library proxies can deal only with interfaces.
As for making the read-only service methods transactional.
There are several valid reasons to do so:
Since you are using Hibernate it is really possible that you actually use more than one query to get your Account object. And if this account is modified concurrently it may lead to inconsistent state:
first query for Account retrieval
Account is modified or deleted
second query for Account retrieval - this query will see the results of modification that together with the results of the first query may lead to inconsistent behavior, but if first and second query were in the same transaction with the proper level of transaction isolation second query would not see the modifications.
Uniform access to the database - it is really helpful when all your database connectivity layer access the DB in one and the same way - I greatly simplifies maintaining and extending of the application.
Using some transactional hints like #Transactional(readOnly=true) may improve your performance with proper configuration (e.g. for the really high-loaded application readOnly queries may use secondary replica of the DB Server). It is really easier to setup the java.sql.Connection.setReadOnly() method as part of the Spring transactions, than in the other way.
It appears that Spring isn't creating a transactional proxy for your Realm bean. This is the only reason that I can see why a Hibernate Session isn't available - because the backing infrastructure isn't there (on the thread) ready for use.
As to your question, if you do want to mark it #Transactional, you might consider specifying #Transactional(readOnly=true)
Shiro creates it's own instance of my Realm and therefore Spring has no power over it to wrap it in a proxy. That's why it can't add the transactional behavior.
I need to make a legacy application start using spring security 3.
This app already has its security data model with:
Very simple by far. I can write my custom usersByUsernameQuery and authoritiesByUsernameQuery.
The thing is that there is another table indicating the operation (i.e. #Service layer method) that a Role can execute:
So the administrator can enable/disable a role from accessing an operation through a web interface, without redeploying the app.
I still can annotate the business methods with #Secure('ROLE_ADMIN') for example, but my custom UserDetailsService must know at least the method name that is being secured, so I can perform the right query.
So, the question is: is there a way that my custom UserDetailsService can intercept the method's name that is being secured?
It sounds like your access-decision is based on the "operation role", rather than the user roles, so it might be better to use the "operational role" directly in the Spring Security constraints. That is essentially an RBAC approach, where there is a mapping between the user roles and the operations they are allowed to perform.
You would address the issue in the AuthenticationProvider rather than the UserDetailsService, by adding a mapping layer in there which translates the user roles (supplied by the UserDetailsService) into the rights that the user has within the application. These would make up the collection of authorities that are returned in the Authentication object created by the AuthenticationProvider.
The mapping layer would directly use the data which your administration interface provides.
You might want to take a look at this presentation, by Mike Weisner, which covers similar material, amongst other things.
Not also that Spring Security 3.1 will include an additional GrantedAuthorityMapper strategy to make it easier to plug in a mapping of this kind.
I'm using spring-security framework.When I update the permissions,It does not take effect immediately.I have to quit the current user(means logout), and then re-visit(means login) will be to update the user's permission.
Is a way that immediately enable the authority after update user authority in spring security?
You can set alwaysReauthenticate in your
AbstractSecurityInterceptor like this
<bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="alwaysReauthenticate" value="true"/>
...
</bean>
Of course you should pay attention because 99,9% you don't need reauthentication. As authentication might use a database or something else your performance might degrade. But usually you have a cache, like 2nd Level with hibernate, so loading the userdetails everytime should be an memory only operation in all cases where authorities havn't changed.
Gandalf solution is valid but not complete. In order for the new permissions to be considered by spring security (eg. allow access to pages previously not available), you need to create a new authentication object (eg. new UsernamePasswordAuthenticationToken) containing the new list of authorities.
The following thread explains how to reload your user/principal every request:
Reload UserDetails Object from Database Every Request in Spring Security
Full disclosure I am the author of the question and answer of the question in the above link.
Is not enough to reset the Thread Local context, you need to update the session too:
UserContext userContext = (UserContext) context.getAuthentication().getPrincipal();
if (userContext != null && userContext.getUsername() != null) {
//This is my function to refresh the user details
UserDetailsBean userDetails = getUserDetailsBean(userContext.getUsername());
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication instanceof UsernamePasswordAuthenticationToken) {
UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;
auth.setDetails(userDetails);
}
return userDetails;
} else {
throw new ServiceException("User not authenticated");
}
request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());
At least in google appengine the session is not a reference and by modifying the thread local doesn't get updated automatically, you have to manually session.set your object.
Since you didn't quite provide the exact details in your question, I assume that you have a situation where:
You are supplying a UserDetailsService to load up a UserDetails when a user attempts to login
As a part of that service, you are querying a database/DAO to load up details about a user's permissions, and are setting the granted authorities based on this
That when you say "When I update the permissions" you are referring to updating the user's permissions in the database (or whatever you are storing data in).
If so then what you are seeing is by design - Spring Security only loads the UserDetails for the user the first time, when they attempt to login, and then stores it in Session from then on. Generally this makes sense, as it avoids the application from having to perform the same queries about user details on each request. Also, a user's permissions are generally not changing throughout 99.9% of their visits.
To change this behavior, you might want to look into adding a "refresh" command/page somewhere that will trigger some code (which you will have to write) which will re-query the UserDetailsService and replace the UserDetails in SecurityContext. I don't believe there is any built-in way to do this.
You could create your own implementation of the UserDetails interface that is returned from your authentication mechanism, that allows access to the GrantedAuthorities[]. Then add your new authority directly to that UserDetails object. You may run into issues if a user can have multiple sessions open at once (or if multiple users share the same generic login), that the new permissions will only be seen on the session you have altered.
You can try this
SecurityContextHolder.getContext().setAuthentication(SecurityContextHolder.getContext().getAuthentication());