Field Level Security (in rails) "Done Right"

Posted on Nov 9, 2024

I’m a huge proponent of believing that security shouldn’t be something that’s baked in after the fact – it’s something that should be all or nothing from day one. I realize that this is probably a wild and opinionated point of view, but, let me try to explain:

When your application starts out, user account security is usually one of the things that you think of last unless you’re utilizing a framework that has some type of tenant segregation in from day one. While security is usually brought into the product at some point after the core functionality of the MVP is introduced, this is the point in the development lifecycle that can really make or break your application: make the right choice: a library that is well maintained, well documented, and well-tested, and hope that it not only has the support of the community, but, pray that it doesn’t commercialize itself later down the road breaking your core functionality…or, roll your own. Rolling your own means ensuring that you’ve got a well-built set of definitions and goals for your authorization stack and that you’ve thought of everything your application needs from day one. Realizing that it’s a step in futility, most good engineers will try to add some things to your application to allow it to ‘future-proof’ the application, basically locking himself to be the ‘forever authz guy’ or worse, detrimental to your engineering team when that engineer leaves and all of that domain knowledge of the library or gem leaves after everyone on his team has left. (Oddly specific because I’ve lived through it. Twice). How do you solve for that? In my mind, that’s getting as close to the data as you can, and building forward from there. That’s where Soter comes in. Soter’s opinionated, secure by default, and easy to implement. Soter works on the belief that all data should be graded from the moment where it’s defined in the Model – if the model hasn’t been graded, then the data is unavailable to display to the user. Grading the data is mandatory, but, granting access is optional. You could have fields that could only be available to the application, prohibited by rules in being returned as a JSON or other object, keeping those fields secure by default.

All content in your application – from public information to private information/PII/PHI – should be appropriately graded, and role access explicitly given for that information as either part of system design policies, or (for bonus) a role-based access control system. These grades should be transparent to the point where a data protection officer or information security assessor can manage and audit who can and who has accessed any given category of data. For example, in a medical session, your healthcare professional should be able to view your health records, the receptionist should be able to view and verify personal information, and the accounting team should be able to view payment and insurance information, and you, as the user, should be able to identify which users from which departments were able to identify that data via audit log endpoints. This graded system allows a developer to tag a field as private, PII, PHI, or PCI, and then enforces that the developer has properly identified the fields (via the PR process – the tool shouldn’t assume). If the field isn’t configured at all, it isn’t available for display to the user.

While data labeling and grading is mandatory, the issuance of a role to a user is optional. A user can have zero permissions – so they have no permission to view any data that isn’t explicitly graded as public – or a user can have all of the permissions – something that I would recommend handing out to a select few number of users, if at all. Grading restricts access by requiring a user to have a particular role in order to access the data, and only data protections officers have the ability to grant additional roles or give data owners the ability to expand their grading permissions – if someone ‘owns’ the data, they cannot remove the grading or grant additional roles to view that grading without the ‘allow access’ role. Data can have more than one grade, but, in order for that user to view it, the user must have all grades – grading uses the boolean AND for conjunctive resolution.

Grading is inherited – all resources that have been graded will assume child fields and files unless the grading is explicitly lowered in sensitivity. While the application can have role-based access of any derived data gleaned from the graded fields, the data grading travels with the data. Grading is considered an elevated action, since the grading shall be immediately inherited with all of the file and data dependencies. Similarly, removing or downgrading is also considered an elevated action, as the new permissions will also be immediately applied.

With that said, I’ve started doing some development for a rails gem so that this will work for a default Ruby on Rails application, and have posted that on my public github.

Feel free to take a look, provide comments, or, let me know which direction this gem should take.