Skip to content

Comment on Authorization terminology is a mess: Let's fix itparent

Comments

If the subject can utter the action, then it can perform it.

This sounds like another layer of weird terminology that doesn't mean anything for someone who is not familiar with whatever capability system you're thinking of.

Say I am a user who can see a particular directory on a shared setup. I try to upload a file in this directory, using the same method that worked on another directory. The question of AuthZ is: will I be allowed to do it or not? In the plain sense of the words, I can absolutely "utter the action", I have all of the "verbs" (upload) and "nouns" (the file, the destination path). Still, I should not be allowed to perform the action if I was only given read-only access here.

Now sure, you can say that "upload to dirA" is a different verb than "upload to dirB". But this is just confusing terminology, it doesn't enlighten anything.

You seem to understand it just fine.

Your accessor, dirB, should not contain the “upload files” verb, while your dirA accessor (noun) should.

My favorite example is the home directory and the file picker. Why should a program have access to all your files by default then politely ask you which file it should read/write to? It would make more sense if the file picker was something the operating system ran when a program wants to edit a file, and what came back to the program after you selected was the accessor for that file (with read and/or write verbs).

So the program only have access to those files you have it access to. It cannot even ask the question to open another file, because it only has opaque accessors to those files it has been given.

What you're describing is essentially what the authorization system would need to do in order to answer the question "can this subject perform this action on this object?". If you're suggesting that the program should receive a list a priori, then there are potential scale issues since that list would need to be exhaustive of both nouns and verbs, which can be a large set.

The point is to flip the burden of proof.

Instead of an authorisation system trying to find a reason to give you permission, you have to carry the proof in the form of a “verb”. Which you use when you perform the action.

Right, but where do you get the proof to begin with? Using your OS example, it seems like the OS would need to precompute all of the possible accesses for the file picker? In this case, the OS is an authorization system.

Do you mean that the directory should not be responsible for making this decision and there should be a central authorization authority?

There a sort of “might makes right” principle here. If you can do it, you can mint a capability for it, which means you will perform the action when someone with that capability requests it.

A central authority is not a requirement. What is required is some way making sure capabilities are unforgeable.

This can be a central authority, which then has a completely mechanical task of registering capabilities and their ownership. But it can also be ensured “cryptographically” with a key.

The OS already has a capability system called “file descriptors”. Which works quite well, within its limited scope. This could be expanded out to more areas.

The question is - how do you get the ability to write to pathA and to pathB but not to pathC, while another user gets the ability to write to pathA and pathC but not to pathB?

Do you get a huge list of capability keys when you log into the system, one for each path? Do you ask a service for a capability when you want to perform the action?

I used to use the example of Dropbox’s chooser API to illustrate this: https://www.dropbox.com/developers/chooser

If you use this API (via a simple widget library) then the user simply picks a file in their dropbox and the app gets access to that one file. Vs OAuth where you grant the app broad access to the whole dropbox (or maybe some sub-folder).

What you're describing is the difference between Fine Grained Authorization (FGA) and traditional Role-based Access Control (RBAC). This article covers the difference: https://www.osohq.com/learn/what-is-fine-grained-authorizati... (disclaimer: I used to work there but continue to be a fan of their documentation).

Sort of, but not really. OAuth isn’t RBAC and the Dropbox chooser isn’t FGA in the sense of that article. My book (linked from my profile) covers the distinctions in more detail in chapters 7, 8 and 9.

  >  It would make more sense if the file picker was something the operating system ran when a program wants to edit a file, and what came back to the program after you selected was the accessor for that file (with read and/or write verbs).
You are describing the concept of XDG Desktop Portals [1] on Linux.

1. https://wiki.archlinux.org/title/XDG_Desktop_Portal

Indeed, that strikes me as a fine example of capability inspired design. The mechanism used is passing file descriptors, and for some reason file descriptors is the most "capability based" part of the Linux kernel.

First of all, this necessitates a certain data model, where instead of a "UploadFile(file, destPath)" operation, I have to have a "destPath.UploadFile(file)" operation. This would be ok for this case, but not all operations can be expressed in this simple parent -> child relationship.

Furthermore, even here, this doesn't cover another case: what if I am allowed to add files to destPath, but I'm not allowed to modify a specific file? This API still has to fail if `destPath/file.Name` already exists and I'm not allowed to modify it (or it at least has to do something different than when `destPath/file.Name` doesn't already exist).

And even if we accept that we can only ever write things in this way, this still leaves the problem of terminology intact. Depending on the technology, it's simply not true that I can't "utter this phrase" if I don't have the capability. For example, if this is an HTTP API, then I can always do a `POST /dest-path/upload-file` with the file I want, regardless of whether I have the authorization to access that or not. Sure, if it's a HATEOAS-style API, the `GET /dest-path` might not return a link to `./upload-file` at all, but that doesn't mean that I can't utter that sentence - i.e. issue that HTTP request.

I have designed capability based HTTP APIs before, it took some work but the end result was ergonomic. Of course over the web the capabilities must be secured in some way. I opted for keys to prove that you can perform a given action.

So, “utter” there means make a valid request with a key. On both the client side and the server side the keys and validations were invisible to the business logic, they just carried objects as usual.

You created capabilities by registering a handler for the operation, and got back a token object you could hand out and even send over the api to those who were meant to use them. And the clients got these objects which they could just manipulate and keep around for making API requests.

The cool part was that you could never forget to do an authorisation check. The keys were automatically checked when the request came in. An API request handler would have no privilege itself, it would only call the key-validated handlers created when the capability was minted.

Oh, I'm sure it can be done, and what you're describing sounds quite nice.

All I take issue with is the claim that this is a way to make the unauthorized actions "impossible to utter". The reality is that, at least at some level, you always have to evaluate a request and, based on some cryptography related to user identity, decide if you'll honor it or refuse it. That may be checking a cookie to look up the user and then checking a separate place to see if the user is authorized to perform the action (perhaps with an extra step of finding a role, etc), or it can be checking a "pre-approval" signature obtained at some earlier point as you're describing here, but it's ultimately the same concept, and isn't "implicitly handled" in one case anymore than the other.

I would hate to argue over semantics. But in addition to the keys being checked automatically at run time, the type system ensured that, unless the capability had been revoked, illegal requests would not type-check at compile time. Which I think is pretty close to unutterable.

The keys were there to stop an attacker, the type-checker helps the good guys stay in line.

the type system ensured that, unless the capability had been revoked, illegal requests would not type-check at compile time

I don't think I can imagine what you mean here. How would my compilation success depend on whether an Admin has given me the right to read or write to a certain path?

First, I am talking about the capability based HTTP APIs I have worked on here. Not some imagined operating system. I can see if I get around to your question in the other sub thread.

But the point is, you would not grant access to a “path” or some such. You gain access to a collection of actions, described by types. It can be simple “get/set property” or more complicated actions, maybe involving capabilities to several things at once.

To mint such an access token the “admin” will have say how the actions are performed, and then pass the token to you. The client code then will have a typed set of actions it can perform using the token.

This does not mean that we compile in the specific rights of each user or anything, it only means that we must ensure a valid flow of rights in order for the whole program to compile. You can get a capability in two ways: you mint a capability for something you already can do (you are the admin and own the data base, say), or you receive it (via an API call, say).

There is definitely a knack to design the system with this kind of flow. But once in place it feels very natural, you get the right to do something just in time for your need to do it. Very much principle of least privilege taken to an extreme.

AboutSource Built by g1lg1l

Hackerly is an independent reader for Hacker News, built on the public HN API. Not affiliated with Y Combinator.