on
Security by PAL
While auditing commercial software for security issues, you often come across the pattern of proprietary authentication, authorization and object access control. The software designers, or, in case it wasn't really designed, the developers, implement their own user credential and access permission mechanisms. Doing so always significantly degrades the security of the entire solution and the custom mechanisms almost always go down in flames the first time someone looks at them too hard.
I vividly remember the fascination of coming up with your own authentication mechanisms. 14 years ago, I was doing the same. This was at the times of MS-DOS, where there wasn't any such facility provided by the operating system. While it still didn't make too much sense to implement something that was called in autoexec.bat to check for credentials (remember the F5 key?), it wasn't mission critical or even commercial software either, so I hope it's forgivable. Interestingly enough, one year later, I was presented with an access control software that was used by the German postal service to secure access to their MS-DOS machines. The person showing it to me said: "You can't break into this, it is commercial grade software." while I rebooted the machine and pressed F5. Well, yes, I could.
When designing identity checks and object access control, the guiding principle should be: PAL. It is a German slang acronym and stands for "Problem anderer Leute", which can roughly be translated to "someone else's problem". In most cases, your underlying operating system or database does require authentication anyway and does offer a fine granularity of access control mechanisms based on the credentials used in the authentication process. Use them!
When moving into a new house or flat, nobody in his right mind would go into a hardware store (as in "Home Depot", not as in "Radio Shack") and buy metal, rasp and small drill bits to make himself a lock for the front door. The person would instinctively know that the expense in time and money exceeds buying a commercial door lock. Additionally, the person would normally realise that a homemade lock will not provide the same level of security.
In many applications, the homemade approach is favoured. By implementing it's own authentication and authorization schemes and using a single almighty user account on the operating system and/or database, the vendor makes his own door lock out of cardboard or wood and embeds the original key to the commercial door lock in the mechanism, so the original door lock still functions. Sounds silly? It is.
Imagine a kiosk type application, such as a cash register or device control system, running on a modern version of Microsoft Windows and compare the following two design approaches. First, we look at what's usually done, namely the kiosk application implements it's own authentication and authorization scheme:
- For the application to run, the Windows system must be configured with auto-logon, which means leaving the password of the operating system account in clear text in the registry.
- Since the application runs always with the same system account, you cannot use any of the access control mechanisms on file systems or the registry.
- The application must try to prevent access to any other functionality a logged on Windows user normally has (i.e. explorer, regedit). This usually fails and even if not, requires a lot of code writing and additional hooking to take place.
- Any additional credentials the kiosk application needs, for example a database account, must be stored somewhere. But since the application always runs in the same account context, the only place to prevent access to the information is by the application itself. It might even use a fancy crypto algorithm such as AES to protect the information, using a hidden static key.
- For any special functionality, such as archiving stored transactions, creating new users or modifying existing ones, the application needs new code and must check access permissions of the currently logged in application account to perform this operation.
It should be fairly obvious by now that, by implementing it's own authentication, the application actually disabled all build-in security provided. And, regardless of your opinion on Microsoft, it's fair to say that they probably spent significantly more time on making sure their authentication and authorization is correct than the developer(s) of the kiosk application.
If, on the other hand, the application would be designed to use the Windows user accounts as means of authentication, the picture is a lot different:
- If the user does not possess a valid Windows account, she cannot even logon and therefore not run the application nor access any data.
- Access to files in the file system and information in the registry can be restricted to read-only or read/write according to the privileges the account has. The access checks are done by the operating system, which requires no additional code and probably performs better. Also, there is no chance for the application developer to forget about a permission check.
- The application can be integrated as the shell for the respective user account. The user might still be able to access other native Windows functionality, but the harm caused is very limited, since access to the critical files is not allowed, regardless of the tools the attacker uses. The attacker would need a privilege escalation vulnerability to get there.
- The additional credentials the application requires can be stored on a per-user bases using integrated protected storage. The CryptProtectData API function can be used to store a user account for the database. Again, the one-to-one mapping allows for the database to use it's own integrated permission checking and not allow an unprivileged user to change any tables she's not supposed to.
- To manage user accounts, the existing tools of the Windows operating system can be used. The same holds true for the database. No additional code is required.
- Introducing containers, such as groups, into such a design is easy, because it's already there. Additionally, if the application suddenly needs to do central authentication, no code must be changed since the functionality essentially would not change by dropping an Active Directory into the picture.
The same approach works on almost every modern operating system, although the almighty root account on UNIX type systems makes it a little more complicated to securely store user related credentials. The approach also works with most web servers and databases. Side effects include less code to develop, higher security due to the use of well-audited code and kissing goodbye authentication bypass vulnerabilities.
Interestingly enough, some commercial web application developers have already learned this lesson and are using frameworks that handle authentication and session tracking for them. Next time you evaluate the design of a to-be-build solution or a to-be-purchased product, ask how authentication and authorization are implemented. If you hear stories about strong encryption and their own user management, at least ask why.