开发者

Building a security library, I think I'm overbuilding it

So I'm building a custom security library, which interfaces with our database. It's supposed to provide basic access control for in-house apps: certain users can do X, others can't. My needs at the moment are pretty basic, but the library will eventually be used by several apps and control a lot of securables.

My basic object model is that a User is a member of zero or more Groups. Those Groups bestow zero or more Permissions. In reality these will all be one-to-many but I don't want to referentially enforce that. Permissions are grant-only (if no groups give you the permission, you don't have it, but there isn't a "Deny" that overrides a granted permission like in Windows RBS), and groups can nest (a Tier 2 user has the rights of a Tier 1, plus some new ones). When attempting to access a securable area of the program, the application will imperatively assert that a user has the requisite permission via examining its group hierarchy.

However, I want there to be several levels of redundancy built into the library. Of particular importance is that a user who doesn't have permission to change security settings shouldn't be able to. So, I want to make this security hierarchy readonly in most cases, so a needed but denied permission can't be added in-memory. Only when a user has proven they have permission to modify security settings by providing a readonly user with that permission, should the properties become settable from even a code level.

That's where I think I've overbuilt it. In order to do this, the domain has developed a split personality; two copies of each of the objects, one readonly, the other not. The readonly version is the one produced by default; any operation that would produce a writable version requires the currently logged-in user to have permission to make security changes. This is resulting in two Users (User and ConfigurableUser), two Groups, Two Permissions, each with two interfaces (the configurable deriving from the readonly)... you get the idea.

I've realized that basically the only people all this extra cruft would stop is other developers, who are generally trustworthy (it's an in-house app and we control all the resources this app makes use of, and the developers generally get admin rights on a lot). If I can trust that people who touch the code know what they're doing, the apps can be read-write and there won't be a problem with the possibility that permissions can be "elevated" via a clever code snippet.

Help me make this sane. Is there a different pattern I should follow? Am I right to distrust other developers? I prefer not to integrate with Windows security, but the option has been discussed; the main drawback would be that the access rights would be maintained in Active Directory for the entire company, and the manager of the users of this app shouldn't have that kind of access to the overall system security.

EDIT: Some good comments and answers from all. AD or other integrated security is not totally off the table, but we did discuss it before I started developing, and found some drawbacks. So, here are some more of my/our thoughts on why we even decided to go with a "custom" security setup:

  • Use of the app is entirely in-house. 开发者_JAVA百科As such, security of this app is not so much about preventing outside attacks/hostile takeovers, but keeping Joe Officeworker from doing something he shouldn't according to business policy. If Joe ends up miraculously finding a way to make himself "god" in the app, his powers are still pretty limited, because the app itself has very limited access to the database and other resources, most of which he'd need to do his job anyway and are thus granted by virtue of being even the lowest-level user.

  • Despite that, if a user ever got his Windows password phished, cracked or keylogged, the attacker would gain some serious access to client data through the application for "free" if the app used integrated security instead of a traditional login. A separate security layer for the app provides at least a possibility of redundancy; they'd have to crack two sets of credentials to get in instead of one, and that second set of credentials is locked behind yet another layer of database security that the cracked user wouldn't have free access to.

  • Using Active Directory or other integrated security has a couple of problems from a development/administrative perspective.

    1. First, members of the department "swap desks" and do things on each others' computers quite a bit. Integrating with Windows security requires users to log all the way out of the OS to change the app's current logged-in user, instead of just closing, reopening and logging into the app as a different user.
    2. Second, the manager of the department who will be using the software is the logical person to handle security permissions within the app, but is NOT a logical person to whom to give AD admin access. Balancing that would require an extra layer of roles within AD to provide for a "sub-admin" that could access AD, but only grant certain rights to certain people. There is also a documented requirement that security management be integrated into the app, which IMO makes AD integration infeasible.
    3. Lastly, Windows Integrated Security as I understand it doesn't have that "permission" layer. Instead of asserting that a user has a stated permission to do something, you assert that a user is in a particular role, the inference being that that role has permission to continue. So I can either develop a system requiring AD to have a role for every specific securable in the app, nested into logical roles that the users will have (an administrative nightmare that I'm sure the network admin would fight to keep out of his security layer), or we define broad swaths of functionality that belong to a known role, nest the roles to create "user levels", and if a user ever needs access to one piece of functionality from the next level up, they have to get the whole level (or AD AND my app have to be changed to separate that functionality into a specific assignable role).

The simple solution, given all this, is to contain security within the bounds of the app instead of tying in to network security. We get a security structure we can maintain with relative ease, that stops at the app.

EDIT 2: As an epilogue to this question, the solution I eventually arrived at was to keep my mutable object hierarchy, but then create a simple interface, IAuthUser, with readonly members for the user's basic info and permissions. IAuthUsers are created in only one place - during login - by copying the User retrieved using the credentials into a private class that implements the public interface. They are used for all authentication and authorization by interrogating the contained list of permissions projected from the user's group membership at startup. They are never turned back into mutable users, and thus are never saved back to the DB. Meanwhile, the mutable Users cannot be turned into IAuthUsers outside of the login process, so are useless for authorization, and they cannot be saved back to the DB without providing an IAuthUser that has permission to make the type of changes detected in the object (by comparing it to the version currently in the DB)


Instead of having a split hierarchy, I would have a "user can modify security settings" Permission, and assert that any time a user tries to edit something.


"Complexity is the worst enemy of security." --Bruce Schneier

For the sake of building a very secure system I would rely upon Active Directory for access control company wide. The most important part of your proposed security system is that its not realying on obscure functionality to fool attackers. Instead the use Active Directory means your access control system will be actively maintained by Microsoft. Which is the platform you are tied to.

This solution isn't bullet proof. You still have to worry about LDAP Injection.


Yes, that sounds overbuilt.

It seems quite unnecessary, and excessively complex, to disallow modification of the objects defining your roles, authorizations, etc., based on the user's security. Unless you clearly have requirements to do so.

Will you be dynamically loading untrusted plug-in code at runtime? Why then the protections for code simply consuming your library.

You may want to look at other implementations of authorization libraries, take some cues from them if you cannot use them outright.

For instance, Rhino Security. Ayende has a handful of blog posts about it.

Another blog has a couple of articles on it, too.


That sounds about right, actually.

User has a context that keeps track of things they have changed. When they're ready to commit their transaction, it goes through security before doing it. Obviously there are some things you should do that I just didn't do, because this is just the 90 second view of what I'm thinking of, and I'll hack this as issues come up. But this is the basic approach I would take.

class UserContext
{

    IList<PersistentObject> thingsUserHasChanged;


    public void Commit()
    {
        foreach(PersistentObject pobj in thingsUserHasChanged)
        {
            if(Security.PermitsUserToCommit(currentUser,pobj)) pobj.Commit();
        }
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜