5

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?

Community
  • 1
  • 1
Stefan Falk
  • 23,898
  • 50
  • 191
  • 378
  • what if you query the database to see if the user is associated with the store is trying to modify, if yes, execute the query, if not, return something else? – jpganz18 Sep 18 '15 at 17:11
  • @jpganz18 That is what I am doing in my example code with `item.getStore().getId() == store.getId()` but I thought that Spring Security can help me to get rid of this kind of code. – Stefan Falk Sep 18 '15 at 17:15
  • is there any role in spring security associated with a specific store? – jpganz18 Sep 18 '15 at 17:18
  • @jpganz18 Well, no. At the moment I am not using Spring Security because I don't know how this could be done. But if there was a role, then the `StoreOwner` (in my code represented by `storeOwnerRepository`) is actually a logged in user that "owns" one or more stores. This user would have the role `ADMIN` basically but since I only got just one role I am not sure if that is important. The thing is that this user could simply send me arbitrary IDs and modify stores he does not own unless I make this check up there but I can't find any example code for Spring Security that could help me here. – Stefan Falk Sep 18 '15 at 17:21
  • 1. Do you care about "sharing" entities with other users, or only about user (or admin) seeing their own stuff? 2. Which database brand, and how do you feel about doing it in the db? 3. Spring Security 4? – Neil McGuigan Sep 18 '15 at 23:14
  • The many simple _role-based_ Spring Security tutorials/examples aren't suitable to control access to _individual_ resources in a multi-tenant system. If you want to use Spring annotations for row-level security, you'll need to implement something like [Eric's _ACL_](http://stackoverflow.com/a/32659145/2012372) approach. If you're open to use a different open-source framework, [OACC](http://oaccframework.org) was designed specifically with use-cases such as yours in mind. _(disclosure: I'm co-author and maintainer)_ – fspinnenhirn Sep 22 '15 at 20:16

3 Answers3

7

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>
Eric R. Rath
  • 1,939
  • 1
  • 14
  • 16
  • I am not sure if I'm getting this right. Let's say I own `storeId=1` and `storeId=2`. What happens if I send `storeId=3` (which is not owned by me but by another user) to the server and it calls `getMyItem(3);`? Would that throw an exception? Because it looks like I'd still have to check that inside the DAO object. I have READ/WRITE/DELETE access on `Item` .. but only on those with `itemId=1` and `itemId=2`. Is that ensured here? – Stefan Falk Sep 18 '15 at 20:36
  • Your AclService would look up the domain object by the identifier, and it would probably do so through the same DAO. It (the AclService) would then determine which permissions you get on that object. The annotation on the business service method then states what permissions are needed on the target domain object. When your business service method is invoked, the Spring ACL library calls the AclService, gets the granted perrmissions, compares them with the asserted permissions, and throws a security exception if some asserted perms are not granted. – Eric R. Rath Sep 18 '15 at 23:32
  • The key thing is that your DAO doesn't need to perform any permission checking, it just does retrieval (and other storage operations). Your business logic doesn't need any permission checking either; it just needs the appropriate security annotations on the service methods you want to protect. – Eric R. Rath Sep 18 '15 at 23:33
  • But *any* user with `READ` permission could pass *any* ID and get *any* `Item` here. What am I not seeing? I really don't get how that restricts a user to some certain IDs of an entity. I understand that only users with `READ` permission will get an `Item` returned. But how does this protect me from a user who changes `anyurl.com/index.html?getItem=1` to `anyurl.com/index.html?getItem=1337` (1337 is not his `Item`)? – Stefan Falk Sep 19 '15 at 11:20
  • Id 4337 would be passed to AclService, along with the user's identity. AclService then determines if user has READ permission on 4337; if not, the permission object returned by AclService doesn't have the READ bit set. When the @PostAuthorize annotation looks at that permission object, sees that READ is required (per the hasPermission expression) but is not set on the permission object returned from AclService, an exception is thrown. – Eric R. Rath Sep 20 '15 at 15:38
  • If user should be able to view object 1, but not object 4337, then AclService should return a permission object with the READ bit set when asked for permissions on 1 for the user, and return a permission object with the READ bit NOT set when asked for permissions on 4337. This happens in the AclService.readAclById(...) method. – Eric R. Rath Sep 20 '15 at 15:39
  • Sorry for my quite late response here! Thank you, I think I'm getting this now :) – Stefan Falk Jan 09 '16 at 21:12
-1

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

Community
  • 1
  • 1
jpganz18
  • 5,508
  • 17
  • 66
  • 115
  • Sorry but I don't see how this can do what I'm asking for. Assume that I only have one role and that is `ADMIN`. Anyone who is on my page is an admin but every administrator has his very own resources. I don't see where Spring Security prevents an admin to send me storeID 3 even though he owns only store 1 and 2. I'd still have to do that check up there - or I am really not getting it? I need smthng. like `@RejectAlwaysIf("storeOwner.id != store.storeOwner.id") public void deleteItem(/* .. */) {/* .. */};` - but of course this constraint should be somehow part of the configuration or whatever. – Stefan Falk Sep 18 '15 at 17:38
  • You can associate permissions over store, like write-store-1, write-store-2, write-store-3 and then check which permissions has – jpganz18 Sep 18 '15 at 17:41
  • But I would do that for each and every store, right? Because there will be *a lot* of stores. ^^ – Stefan Falk Sep 18 '15 at 17:43
  • well, in that case yeah, I really dont see any solutions owned only by spring security, since its only a security layer and this is more a business logic issue – jpganz18 Sep 18 '15 at 17:52
  • That is strange - I really thought Spring can help me here. It feels like this is a very common issue. I don't want to rely on a programmer that he does not forget this check at any point. It would be much easier to e.g. have a constraint on that relation. But maybe I have to put this constraint somewhere in Hibernates hands.. Thank you for trying to help me anyway! – Stefan Falk Sep 18 '15 at 17:55
  • You can do it with spring limitating the methods, but is not an option for you... – jpganz18 Sep 18 '15 at 18:06
  • Not if I would have to take every single store into account separately. – Stefan Falk Sep 18 '15 at 18:16
  • Yes. What I am doing up there is absolutely fine, works and does exactly what I want to have. But I don't want to have this check in my Service, Business or Repository Layer. Imho Hibernate should restrict the data access somehow using some sort of constrains or Spring Security should do that for me. – Stefan Falk Sep 18 '15 at 18:29
-1

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.

Jin Kim
  • 16,562
  • 18
  • 60
  • 86
  • Hi! What you are seeing up there is indeed server code. It is The Service Layer using Springs `@Transactional` for Hibernate Session management. In `storeOwnerRepository` is actual Hibernate code that deletes an `Item` that is owned by a `StoreOwner` in the database. – Stefan Falk Sep 18 '15 at 18:51
  • This is a classic **authorization** question: _how to control access to resources_ (in this case resources in a multi-tenant system), so classifying the question as general _validation_ instead of _security_ is incorrect. – fspinnenhirn Sep 22 '15 at 19:25