Sharks in the Moat
Page 11
Instead of trying to increase the performance or capabilities of each node, we can instead simply implement horizontal scaling by adding additional nodes to those already in-play. Horizontal scaling is the default modern way to provide scalability, but software must be designed to accommodate such a capability. For example, once we move from a one-node design to a multi-node design, we have to implement measures to share common resources and often we must add a queuing mechanism so multiple nodes respect the other’s presence without interfering with parallel processing. It is exceptionally difficult to horizontally scale legacy software that was not designed for such a thing.
It is a common problem to try and horizontally scale a solution by adding more nodes only to find out that a common resource, such as a database, cannot handle the increased load. We then find ourselves having to resort to vertical scaling at the
database. At this point, architects will normally turn to caching to solve the problem. Caching is a pattern in which data that is expensive to retrieve is stored in-memory to reduce the number of times the data must be fetched from the constrained resource. Again, the most common reason for this implementation is a resource-starved database that cannot be vertically scaled anymore, or at least not without a great cost. Unfortunately, while caching can greatly increase the ability to horizontally scale, it does present additional problems. For example, caching can easily expose sensitive data in an unsecure manner by storing data in clear text. A very robust scheme must be developed to prevent the cache from becoming stale, as the original data source may change at any time. This is usually addressed by implementing a time to live, or TTL, in which the data is discarded and refreshed after a certain amount of time has expired since it was last fetched from the original source. The rule of thumb is that TTL should decrease as the criticality of the data increases. In other words, the more important the cached data is, the more often it should be refreshed from the authoritative source.
Chapter 16: Authentication
Given the maturity of existing authentication mechanisms, any requirement that calls for a custom implementation should be closely examined. This doesn’t mean that there is never a need for a custom solution, but the need for it must be backed up and scrutinized. Let’s go over the most common forms of authentication to show why this is.
Many projects do not bother calling out what capabilities are accessible before any type of authentication is carried out, ultimately resulting in security gaps and missed requirements. In fact, we have a name for this type of access – anonymous authentication. Specifically, this type of authentication allows access to features and information without prompting for credentials such as a username and password. Anonymous access should be carefully considered before it is accepted as a valid requirement, as this capability will implicitly limit the use of an audit trail to know who did what and when. There will be no possibility of holding anyone accountable for their actions prior to authentication since we will have no idea who they are beyond possibly capturing an IP address, which is easily spoofed. This limitation is called unlinkability.
Since browsers have been around with the HTTP 1.0 specification, we have had access to an authentication mechanism (if it can be called that) called basic authentication. With this behavior, a browser will send credentials in a base-64 encoded form to the server. Don’t make the mistake of thinking that base-64 offers any security – it is easily decoded and is the equivalent of sending clear text across the Internet. Using this mechanism is a really bad idea from a security point of view.
A huge step up from basic authentication is something called digest authentication, in which credentials are hashed by the browser before being sent to the server. A salt based on a unique hardware property is used to further secure this mechanism. However, the credentials could easily be sniffed and replayed if the path between the browser and server is not secured using TLS.
A slight step further up the security chain is integrated authentication, also known as NTLM authentication or NT challenge/response authentication. It also uses a hash when sending credentials but can be used with Kerberos authentication. This is most useful when implementing a web-based application in an intranet.
Client certificate-based authentication is the next step in increasing security mechanisms, and requires a certificate authority, or CA, to work. Client certificates are extremely valuable for eCommerce applications.
Up to this point, credential prompts and negotiation with a web server has been pretty much an out-of-the-box experience. But know we turn to custom implementations. Forms authentication requires an application – usually a web application - to provide a form that collects and transmits credentials to the server. Obviously, the credentials must be communicated over an encrypted channel such as TLS or they will simply be stolen.
Forms authentication works really well, but we can’t require a user to provide his or her credentials every time the client needs to communicate with the server. And neither do we want the client to cache the credentials and send them for each transaction, as this would simply result in additional security holes. Instead, after a successful authentication, the server can simply return a token, which is a string of characters unique to the current client-server session. The client can cache this temporary token and simply send it back each time it needs to communicate with the server. Called token-based authentication, the server is free to declare a token expired at any point in time, rendering it useless to be used in an attack. Token-based authentication can also be used outside of a browser-based channel. Kerberos itself uses the pattern when it assigns a Security Assertions Markup Language, or SAML, XML token. This is a prime example of how SSO is implemented within an intranet – a user authenticates once, and all subsequent applications use the token without having to ever know the real credentials.
Authentication comes in three different flavors – something you know, have or are. “Something you know” might be a password or combination number for a lock. Everything we have discussed to this point falls under this category. “Something you have” is represented by a token generator such as an RSA device, or perhaps a magnetic key card. “Something you are” could also be phrased as “something you do” and will be based on some type of unique attribute your body has or does. For example, a fingerprint or the way you walk can be used as something you “are”.
Let’s cover a few authentication methods in the ‘something you have’ category. Smart card-based authentication uses a card with a programmable microchip that stores credentials, with the authentication process taking place on the card itself. While this prevents an attacker from stealing credentials entered into a computer, the amount of data a card can store is limited as is the ability to protect that data using cryptography.
A one-time password, or OTP, is considered to be the maximum strength of authentication security when used with multi-factor authentication – we’ll discuss what that is just a second. An OTP device generates a unique code that the user types into a form, which is then validated by an equivalent server-based process that can generate the exact same token. Because the token changes every few seconds, it is almost impossible for an attacker to steal the
authentication credentials.
Under the ‘something you are’ category, we have biometric authentication, which is based on a certain biological trait of a person. Examples include retina scans, facial features, fingerprints, and voiceprints. A biometric system measures the trait in real-time and compares it to a record created during an earlier enrollment process. The results must be very sensitive yet reliable. When a biometric system fails to properly identify an individual, it can result in two types of errors:
Type 1 error – rejects an authorized individual
Type 2 error – accepts an unauthorized individual
The frequency of a type 1 error results in a number called a false rejection rate, or FRR. Type 2 frequencies result in a false acceptance rate, or FAR. While the goal is to keep bot
h numbers low, type 2 errors are much more concerning – it is far better for authorized individuals to be occasionally forced to repeat the authentication step, than it is to occasionally provide unauthorized individuals access to the protected resource or facility.
When comparing the accuracy of various biometric systems, it is helpful to have some kind of objective way to measure their respective performance. That is why each system provides a crossover error rate, or CER, which measures the point at which the FRR equals the FAR and is expressed as a percentage. For example, a system could be configured to be so sensitive that the FAR is 0%, but at that level the FRR might be 90% - that means no unauthorized individuals are accepted, but it also means that 90% of authorized individuals are rejected as well. Let’s say a system has a CER of 3% - that means that it can be tuned so that both FRR and FAR are at 3%, but no lower than that. The closer to 0% the CER is the better the overall accuracy. A system with a CER of 3% is better than a system with a CER of 4%. However, an organization very concerned with security might purchase a biometric system with a CER of 3% but tweak it after installation to lower the FAR to 1% at the expense of raising the FRR to 10% - fewer unauthorized false positives at the expense of more authorized false negatives.
A major drawback of biometrics is the tendency for biological features to change overtime, requiring the individual to re-enroll.
In summary, we have the three authentication categories - something you know, have and are. If we combine more than one of the three approaches, then we have a multifactor authentication. For instance, if you are required to type in a password (something you know) and then provide a fingerprint (something you are), then we are requiring the use of more than one authentication factor, and so it is a multifactor approach.
Multi-factor authentication is considered to be a must in today’s risky world. While SSO can greatly increase usability, it also increases security risk as the theft of a single set of credentials provides access to multiple systems. If SSO is to be used, it must be designed from the very beginning, as all subsequent authorized access will be based on the authorization as provided by the SSO solution.
Some excellent examples of authentication requirements are the following:
“The system will be available only within the intranet, and the authenticated user should not need to authenticate again once they have logged on to the network.”
“The software will need to support SSO with the vendors and suppliers as defined in the stakeholder list.”
“Both intranet and Internet users should be able to access the software.”
“The authentication policy specifies the need for multi-factor authentication for all financial systems.”
One last note before we leave authentication. Many times within the upcoming chapters, we will reference the term ‘broken authentication’. Essentially, this means that an attacker has somehow compromised the authentication process, usually by stealing a session token, but it could also refer to the theft of credentials as well.
Chapter 17: Authorization
Just because someone or something has already authenticated using a known identity, it does not necessarily mean they should be able to access a given capability or bit of information. As an example, you might be able to authenticate into a time tracking system, but you will not be able to approve time sheets since you have not been granted access to that function. Authorization is the act of controlling access to objects based on rights and privileges that are granted by the object’s owner or according to a security policy. Authorization must always follow authentication – how can we possibly grant access to someone unless we know who they are? The only exception to this rule is if we decide to allow anonymous access, in which case authentication by definition is not required. Even then, we should explicitly establish rules that allow access for anonymous users.
When discussing authorization, the requestor is called a subject and the requested resource is referred to as the object. The subject may be a person, some kind of background process, or even another object. A subject does not have to represent a user either – it can be represented by a group or role such as an administrator, manager or even an anonymous user. An object can be anything in the world worthy of potential protection, such as a file, data in a database, a web service, a physical room, a car, or even a communications channel. But we need to take it even one step further. If a subject has access to an object, what permission does that access give to the subject? Although the possible access permissions vary, you will almost always find that at least four will be present – create, read, update, and delete permissions. These group of four are classically referred to as CRUD, representing the first letter of each permission. For example, an anonymous user might have the read permission (R) while an employee might have read and update permissions (CU). An administrator might have all four (CRUD).
There are a number of security models we can choose from when it comes to implementing a rights management capability (such as MAC and DAC), but we will not discuss those until the Architect role. But let’s review a few good examples of authorization requirements.
“Access to highly sensitive files will be restricted to users with secret or top-secret clearance levels only.”
“All unauthenticated users will inherit read-only permissions that are part of a guest user role while authenticated users will default to having read and write permissions as part of the general user role.”
“Only members of the administrator role will have all rights as a general user along with permissions to execute operations.”
It is quite common for subpar implementations of authorization to result in poor system performance. This is especially true when we achieve complete mediation, or checking every single access request against an authorization matrix. For example, if a system must perform a cross-table join in a database for each request to see if it is allowed, the system will quickly turn into molasses as database activity increases. Often an architect will turn to caching to solve this problem, where data is temporarily stored in volatile memory where it can be quickly accessed instead of having to touch the original source each time. This actually is not a bad design, but care must be taken to ensure the data does not become stale, resulting in access being granted based on an old configuration or data. As we previously discussed, to minimize the chance of this happening, a time-to-live, or TTL, needs to be implemented in which the cache expires after a certain amount of time and a reload is forced from the original source.
Care must also be taken to obey the principles of separation of duties and least privilege, concepts that are discussed in-depth later. If roles are to be used for granting access, the team must make sure that two roles do not create a conflict that violates separation of duties. For example, if the DBA group allows an employee to both write a SQL script AND approve the deployment of the script, then we have violated this principle as a single person could write a rogue script and move it to production without anyone else knowing. Instead, deployment approval should be removed from the DBA group. Along the same lines, to follow the concept of least privilege, anonymous roles such as ‘Guest’ or ‘Everyone’ should have read-only access at most.
Entitlement management is a term that describes the process that makes granular access decisions. Where this process runs is a crucial authorization design component. For example, is it coded directly into an application, or does it exist as a stand-alone capability that multiple applications can share? When dealing with a service-based cloud computing application – such as a SaaS offering – the answer will almost always be that entitlement management exists as a shared service that multiple applications can leverage. For applications running on Smartphones or tablets that have a mobile OS, this capability is
often built into the application. For example, permission to access the microphone or contacts is controlled at the OS level on smartphones, and therefore the capability to request access must exist within each deployed application.
For access decisions that are proprietary within the application, entitlement management could exist within the application or in a shared service that is accessible over the Internet.
For all other applications, the decision of where to implement entitlement management must be made by looking at the pros and cons. The obvious advantage to having a centralized capability is that there is only one place to manage changes and it can abstract the complexity away from the application. The disadvantages include creating this shared capability if it does not already exist and the risk of a single point of failure. If a shared entitlement management service goes down, it could render multiple applications useless until it is restored.
While we will cover this later in-depth, role-based access control, or RBAC, is a concept that will be frequently referenced. A role is nothing more than a named group to which we can assign both permissions and users. By using roles, we can assign permissions to a role instead of directly to a user, saving us from having to assign a bunch of permissions each time we create a new user. While this is a simplification, just remember that RBAC means we use roles to assign permissions.