As discussed in the last post, the purpose of authentication, using mechanisms such as passwords, is to ensure that the person in front of the keyboard is indeed the user she claims to be by testing her knowledge of some secret data. This is typically necessary in situations where users are not there to check that no one physically tampers with their computers, such as when a computer is left powered off, sleeping, or crunching numbers on its own with the screen locked.
Obviously, due to their sensitive nature, authentication secrets should be used sparingly. The more often they are used, the more likely they are to be intercepted by an attacker willing to perform identity theft, with potentially disastrous consequences. Yet I don’t know about you, but in a typical week, the two system dialogs which I interact with most often on my computers are these two fellows:
Both of them are dialogs with a simplistic appearance that any user application can replicate, which request me under a vague justification (“make changes”, “modify essential parts of [my] system”) to input a sensitive authentication credential giving full access to my system.
To Microsoft’s credit, their own implementation is a bit better, in that when setup properly, the dialog does not necessarily require a password to be input, appears less frequently, and can have a quite distinctive appearance (translucent black background) that is hard for malicious software to replicate. But I still think that these privilege elevation dialogs, and the habits they give users to input their (administrative) password whenever they are requested to without a clear justification, are a bit of a security issue.
Excuses for re-authentication
As a member of this blog’s audience, you are probably aware of the purpose of these privilege elevation dialogs. They are displayed when an application needs to run at a higher privilege than it currently does, for example when an application installer/updater running at regular user privilege needs admin privilege in order to modify shared regions of the hard drive, or when a control panel process needs to modify system-wide configuration.
This mechanism is usually considered beneficial to security, in that it reduces the amount of harm which a process gone amok can do in the event of a bug or an exploit, because most of the time, said process only has limited system access. It is, however, important to note that the concept of privilege elevation does not, in and of itself, require the input of authentication credentials. For example, in one of its many modes of operation, Windows UAC takes the form of a simple dialog with a “Yes” and a “No” button (along with a “close window” button that is taken by the system as a “No”) :
Moreover, programmers who are considering the use of run-time privilege elevation dialogs should remind themselves that if users are led to regularly and frequently click “yes” or input their password in such dialogs, the practice will enter their muscle memory, and after a while, they will not think before doing so. This, in turn, is extremely detrimental to the operating system’s security. So the use of run-time privilege elevation dialogs should be reserved for truly exceptional situations, and never become a routine habit for users.
For this reason, I believe that if a given program is known to frequently require high privileges to operate, as is for example the case for control panels and software updaters, a better design is to decompose said program into at least two separate processes. One process will be in charge of all the low-privilege jobs, such as interacting with the user, whereas the other will be in charge of performing the tasks that actually require high security privileges.
Of course, for such a separation to be useful, the interface between the two processes requires some thought: if the high-privilege process simply does whatever the low-privilege process tells it to, then we have a confused deputy situation on our hands and little security has been gained with respect to the scenario where there is only one single high-privilege process.
Still, this does not answer the salient question of why we need to authenticate at all in privilege elevation dialogs, on most operating systems, in spite of such authentication being actually largely unnecessary.
My take on this matter is that what we are watching here is again a remnant of the mainframe era, where there was no such thing as personal computers and all practical computer systems were shared by many users. In this situation, it was necessary to have an administrator user account that is distinct from all regular machine users, and so administrative action actually required the input of an administrative password that is distinct from the regular user’s password.
This ship has sailed, though, in the area of personal computing. Even though we are trained, by sysadmins who are specialized in large multi-user computer systems, to see the lack of a separate admin account as a bad security practice, this view actually originates from the fact that on those many-users systems, the worst that can happen is a global outage of service for all users caused by a single malicious user.
This problem does not exist on mono-user systems, however, because in this realm a botched OS is sure a problem, but the worst-case situation is actually closer to a wipe of all user data or the installation of a Remote Access Trojan, two outcomes which current security models only offer weak protections against.
An alternate proposal
As mentioned earlier, on personal computer systems, user-visible avenues for multi-user operation is actually closer to a “nice to have” than to an absolute “must-have”. We did without for most of the lifetime of Windows, DOS, Android and Mac OS, and as of today, iOS is still doing just fine commercially without having any support for multi-user operation. This is because most personal computer systems are mono-user, and multi-user systems are actually more of an exception than a rule.
However, what personal computer operating systems badly need, and currently largely lack, is a way to restrict the authority of untrusted programs and processes. It is possible to implement such a sandboxing mechanism because the only two system components that actually require full user privilege or higher to operate are the operating system kernel and the various user interfaces which the system exposes to its users. All other processes, including fairly low-level ones such as disk drivers, can run within a strict subset of an administrator user’s privilege.
This is important, because a user interface shell does not need to transmit its full user privilege to every program it starts. It could, instead, be more selective about it. For example, when an office suite program is started by opening a specific document, instead of giving that program full access to all of the user’s files, the user interface shell could instead only give the office program access to the specific file that is being opened. In such a framework, processes could acquire three distinct kinds of privileges:
- Intrinsic privileges would be handed to them because they need them to work, as part of their basic operation. For example, most programs need to be able to store their configuration data someplace private.
- Contextual privileges would be handed to the processes by the user interface shell, or some other client process, depending on the specific task that they are to perform.
- Exceptional privileges, finally, would as usual be requested by processes at run time as a last-chance option, in cases where neither intrinsic nor contextual privileges would allow them to fully perform their task.
Where this view would require a bit of a paradigm shift, is in the fact that it requires one to consider user interfaces as partially privileged. For example, file open and save dialogs could only be implemented as privileged operating system dialogs, and should not be left up to the specific application, except when they are used to access private application data. Privilege elevation dialogs should also obviously remain the responsibility of the operating system, and not be either drawn or forgeable by applications.
The contextual privilege policy would also require user interface shell processes to have a way to transmit part of their privileges to the processes that they start. This can be done using an authority delegation mechanism such as capabilities, which were presented in a recent post.
Note however that none of this requires authentication. As long as a user is logged in, with his screen unlocked, he makes an implicit contract with the operating system that he is not going to leave his computer unattended, at the mercy of unsupervised third-party use. In this scenario, the operating system would fully leverage this contract in order to avoid the constant re-authentication that plagues today’s personal computer operating systems.
The issue of guest access
It is worth concluding this post by considering an edge case for today’s authenticated privilege elevation dialogs. That is, the scenario where a user A wants to hand over his computer to an untrusted user B, so as to let this user browse the web or show him something that he is working on.
In this scenario, authenticated privilege elevation dialogs reduce the amount of damage that such an untrusted user can do, albeit somewhat unsatisfactorily. This is, however, a bit of privilege elevation misuse. The only ways that one has, today, to ensure that an untrusted guest does no harm to his computer, is to either stay close to the person as she is using the computer and supervise what she is doing, or to use a special “Guest” low-privilege user account of the operating system for this kind of purposes.
In practice, none but the most paranoid of us will pick the latter option, because it is not very practical from a usability point of view. As the Guest account is rarely used, it will struggle to keep in sync with the machine software and hardware evolution, and will end up full of obsolete data and application shortcuts. In addition, when the purpose of handing over the computer is to share some ongoing work that user A is doing, the process of opening a dedicated session for user B and putting it back in the state where user A’s session was will be unreasonably involved.
If we wanted to account for this edge use case, which I am not sure is really necessary, a way to go about it would be to (mis)use privilege elevation interfaces by allowing a user to temporarily restrict his own privilege level on a running system, and require authentication for the privilege restriction to be canceled. Kind of like a partial screen lock, where the user interface is still active, but some functionality is temporarily disabled.
However, this way of operating would come with its own share of problems. Consider, for example, all the potential for private information leakage that a partially locked session would still offer. Consider the situation where the remaining privilege level would be unacceptably small with respect to what a user actually needs. And finally, consider the amount of confused deputy scenarii that could emerge from a user interacting with applications that assume control from a legitimate user, and delegate in turn some of their tasks to lower-level system services which are unaware of the situation.
In my view, this is not a problem worth solving, and it is best to keep things simple instead. Users should be given as many privileges as they are going to need, operate under their own user account, and be trusted to delegate their privilege wisely. And to help this, user interfaces should be designed to make it hard to carry out dangerous actions without a second thought, including when the second thought comes from a second user watching over what’s going on on his computer’s screen.