Sharks in the Moat
Page 29
So, how exactly do we protect ourselves against CSRF attacks? It seems as if we are doomed to choose between usability or security with no acceptable middle ground.
First of all, never implement ‘remember me’ in which the full set of credentials are cached in the browser. A less-odorous use of this feature that can be acceptable from a security point of view is to implement a ‘remember username’ feature, in which we pre-populate the username on the logon screen but require the user to enter the password. This is still a security risk as the username might be stolen through other means. But, many modern websites take this approach as a compromise.
The second recommendation is to implement a form-level value that is unique for every page view. For example, when a user visits a page with an edit form, the server should generate a nonce, which is a one-time, unique and difficult to guess random value. When the form is submitted back to the server along with this nonce, the server should validate the nonce value and refuse to accept the form if it does not match the expected value. The next time a form is sent to the browser, a different nonce is generated and used. This is a very effective countermeasure to CSRF attacks, as the attacker would have to guess the nonce in real-time and send it back to the server. If the nonce is sufficiently random and complicated, guessing the correct value will be extremely unlikely. Unfortunately, implementing such a mechanism is fairly complicated. Some web server frameworks provide such a capability. For example, ASP.Net provides a ViewState feature. The use of such features will still result in considerable complexity and performance overhead though. Another approach along similar lines is to use a CAPTCHA feature, which is an abbreviation for Completely Automated Public Turing test to tell Computers and Humans Apart. The use of such a feature requires a human to submit the CSRF attack and tends to eliminate automated attacks via script.
An alternative approach to implementing a page-level nonce is to leverage a double-submitted cookie. In this method the cookie-based session token is embedded into the form so that the same value is retrieved from the cookie as well as the submitted form. If the two do not match, then the form is rejected. This requires the CSRF attacker to know the session token in order to craft an acceptable form to submit. This is a much better approach but does have to be implemented for all form submissions.
We can also check the URL referrer tag to ensure the request is coming from a legitimate web page. While this can certainly defeat the majority of attacks, there are two problems with this approach. First, an attacker can still modify the referred URL using any number of tools if they are directly crafting the request, including the use of XSS. Secondly, we might be guilty of denying access by legitimate users if they are behind a proxy that is intentionally suppressing the referred URL for privacy reasons.
For some extremely sensitive actions, such as changing passwords or email addresses, we can also force the user to reauthenticate again even if they have a valid session token. This is very effective in defeating CSRF attacks around these areas, but obviously we have to take into account usability and not overuse such mechanisms.
Finally, there are some good industry tools that can help defeat CSRF attacks such as OWASP CSRF Guard, and OWASP ESAPI which can help with generating unique session tokens. Code Igniter for the PHP MVC framework is also a great example of tools offering CSRF protection. In general, mitigating XSS vulnerabilities will go a long way in reducing the impact of CSRF attacks, as they often go hand-in-hand.
Following are some common-sense countermeasures employees can take that are not unique to CSRF but are generally good ideas:
Train users to not use ‘remember me’ functionality.
Do not use the same browser on the same machine to both surf the Internet and access sensitive websites.
Use private modes such as Incognito or InPrivate when visiting sensitive web sites.
Read standard emails in plain text, as this makes it much easier to detect malicious links.
Explicitly log off web applications when done, and ensure the server forcefully expires session tokens on explicit log offs.
Use browser extensions that mitigate CSRF attacks such as CSRF Protector.
Insecure Direct Object References
When an application exposes its internal plumbing in a way that can be manipulated by an attacker, it is guilty of exposing an insecure direct object reference. Put a little more directly, it has allowed an internal object to be directly manipulated in an insecure manner. Take as an example a website that uses the incoming URL as a way to enable or disable various menu options. In this case the existence of a querystring variable called ‘isAdmin’ controls whether an administrator menu will be visible. An attacker can easily change this value from a ‘no’ to a ‘yes’ to gain privileged access. The direct object in this example is an access control mechanism that decides if the current user is an administrator.
I highly doubt that any reader of this book would make such a blatant mistake, but what about making access control decisions based on an encrypted browser cookie value that turns administrator functionality on and off? You might think that since it is encrypted an attacker could not change it, but they could certainly delete the cookie. If our default behavior is to allow administrative access unless the cookie value says otherwise, then we have yet again given direct control of an internal function to an attacker. This is a much more likely scenario.
This flaw can lead to information disclosure, privilege escalation, authentication or authorization checks bypass, and restricted resource access, among others. Obviously, the best defense against this type of vulnerability is to simply not expose internal functionality in such a manner that can be controlled by the client. If you must absolutely use such a bad pattern, though, then at least employ the following suggestions:
Abstract the meaning of each possible value and map it to the correct value on the backend. For example, instead of saying ‘isAdmin=yes’, use ‘slvl=1004’.
Never put these objects in the URL or a hidden form field – use a cookie or browser local storage.
Always mask or protect the clear text value by using encryption or hashing, especially if the data is an obvious name/value pair.
Always validate any change in the value against a whitelist – never blindly accept a value just because it does not appear to be sinister.
Perform complete mediation any time a value is changed. This means that the backend must assert that the current user has the right to change such a value.
Use RBAC to map the changes against permissions the current role allows. This will at least protect against attackers targeting users at different role levels.
Use both context and content RBAC.
Ensure proper code reviews and parameter manipulation tests are carried out to detect weaknesses due to insecure direct object reference flaws. Keep in mind that automated tests for this behavior very often fail to detect a problem since they do not understand what objects require protection and which are safe.
Unvalidated Redirects and Forwards
When transferring control from one web page to another, we can use two mechanisms – a forward to another page on the same site or a redirect to an external site. When a static link pointing to another site is placed on a web page, a user can navigate by explicitly clicking the link. We can also choose to have the server return an HTTP directive instructing the browser to load a different page than the one currently reflected in the address bar. This is carried out using the 3XX series of HTTP codes, with 302 being the most-often used. We also have the option of embedding script in the returned web page that programmatically redirects the user:
Injection vulnerabilities and possible user-supplied values can be used by a server to carry out an unvalidated redirect to a malicious site, also known as an open redirect. The malicious site can then phish the user for sensitive information or trick the user into installing malware by clicking a link. To prevent this, before using a redirect an application should always check t
he destination URL against a whitelist.
You should never allow the user to specify a redirect using parameters, but if business requirements override this directive, at least ensure that the URL is fully validated before using it. In this case, mapping the specified URL to an internal list of actual targets prevents extraneous parameters from being added to the end, and adds an additional layer of obscurity to the operation by not allowing the actual URL to be exposed to the attacker.
If redirects or forwards are required, always inform the user BEFORE the action is taken by using an intermediate page. A modal warning is preferred as the user is required to interact with it before they can continue.
Canonicalization
Canonicalization is the process of converting data that can be presented in multiple forms into a single standard form. Since this word can be difficult to pronounce, and remember how to spell, we geeks often abbreviate and speak it as ‘C14N’, as there are 14 letters between the first and last letter. In plain speak, canonicalization represents the simplest or most standard form among all possible forms.
Perhaps the best example of canonicalization is the URL to IP address translation process. Since international character sets or code pages such as ASCII and Unicode can change the form of URLs, it is important that the proper character set and output locale are configured in your software. Let’s look at a potentially real-world example to better understand how important canonicalization is.
Let’s suppose our application is a multi-tenant application, and we use the URL in the address bar to determine which of our customer sites the user is attempting to access. We therefore simply do a lookup of the URL against a list of customers and serve up the appropriate page. We assume the user will always enter something like:
http://www.mysite.com
Unfortunately, a URL has multiple alternative forms that will all work in an address bar. Some of these are the following:
http://mysite.com
http://www%2emysite%2ecom
http://209.64.115.7
If our database only stores the canonical form of ‘http://www.mysite.com’ then we will be unable to find a match if an alternative form is used. We therefore must always convert all forms to the canonical form before performing the lookup. In this case we can have logic that will add ‘www.’ to the front of any URL if it is missing, but we probably do not want to start hard-coding IP addresses.
Network Vulnerabilities
Network vulnerabilities are those that occur as data is being transferred around a network. Two areas fall within this category – protecting sensitive data and ensuring file transfers are carried out properly.
Sensitive Data Exposure
We’ve already discussed several times that data can be in three different states – at-rest, in-transit and in-use. While it is possible for an attacker to access information in any of the three states, at-rest and in-transit are the most vulnerable if not properly protected. In addition to those two states, electronic social engineering is also a primary method by which thieves will steal data.
The most common method used by an attacker to steal data in-transit, sometimes called in-motion, is to use a passive sniffer. Traffic on a network is most often sent using an electronic signal along metal wires, such as twisted pair or Ethernet cables. Because it is a signal, we really have no control over the direction it travels, and every computer connected to the network will have a chance to ‘sniff’ the data. That is, unless we use switches or routers to physically segment the network. The end result is that any computer physically connected to a network can peek into passing data packets regardless of who it was intended for. This is accomplished by putting a network interface card, or NIC, into promiscuous mode.
Figure 88: The Result of Not Encrypting a Path End-to-End
Beyond proper network segmentation, the only real protection we can provide is to encrypt this data so even if the bytes are ‘sniffed’, the attacker cannot see inside of the encrypted data. Both TLS and IPSec technologies can be used for this type of security. Unfortunately, many times an application will encrypt the authentication process and then revert to clear text for the remainder of the conversation, which I hope you can see by now is just plain silly. An attacker can easily just launch a surf jacking attack in which he steals a session token, waits for the authentication process to complete, and then use the session as if he were the one who authenticated. This ultimately results in session hijacking in which the attacker takes over an authenticated session, as well as a replay attack, in which the attacker simply replays packets that have already been sent in hopes of tricking the system into giving up its precious data.
Even if we use TLS for part of the network traversal, MITM attacks can still happen unless we encrypt the entire path end-to-end. It is common for network administrators to encrypt any traffic with the outside world, but to be fairly lax when it comes to internal traffic. For example, suppose we have a 3-tier system consisting of a browser, web server and backend database. It is not uncommon to encrypt the browser/web server connection but leave the web server/database connection wide open as shown in Figure 88.
The good news is that we can easily use digital certificates in combination with encryption – such as that used by TLS – to secure these vulnerable pathways. That is, assuming we properly protect the certificates themselves as well. Certificates can be spoofed, opening up the end parties to multiple vulnerabilities. Have you ever tried to visit a web site and the browser told you that it was not safe? Usually, this is because the site is telling the browser to use encryption but giving it an expired certificate. This could also be the result of an attacker substituting their own certificate in the hopes you will ignore the warning, thereby allowing him to have complete access to all data transferred between you and the target site. The sad state is that users have become so accustomed to ignoring these warnings they will often go right ahead and visit the site anyway. Organizations should make it a priority to educate their employees on dangers such as these.
To combat this, newer versions of Chrome by default do not even allow you to bypass this warning. I recently was in a library working on this very book and wanted to access the guest Wi-Fi by authenticating through a browser, but their network was configured with an expired certificate. Since Chrome is my default browser, there was no way to get past this. Now, you would think that someone like myself would not even attempt to ignore such a blatant warning, as I pride myself on being pretty savvy when it comes to security. But did I do without an Internet connection? Absolutely not! After futzing with it for 10 minutes, I swallowed my pride and marched right up to the front desk to ask for help. Imagine my embarrassment when this elderly librarian simply brought up Microsoft Edge and connected me. So, two lessons learned:
1) Chrome is safer than Edge.
2) Never equate brainpower with common sense. Especially when one writes books on security.
Let’s quickly leave that awkward episode behind and now look at protecting data at-rest. If you think not encrypting data in-transit is bad, then leaving resting data in its clear text form is twice as bad. In fact, if we do not properly encrypt resting data, all of the TLS encryption and IPSec goodness in the world will be rendered completely useless. There are several top contenders when it comes to sources of this risk, which are:
Local storage and cache.
Browser settings.
Backups, logs and configuration files.
Comments in code.
Hardcoded secrets in code.
Unhandled exceptions and error messages.
Backend data stores.
When we use the term local storage, we are primarily referring to storage on the client’s computer, as opposed to on mid-tier or backend servers. Before HTML5 burst on the scene, local storage for browser-based apps was limited to cookies and Flash objects. One of the advancements that HTML5 has brought us is something called HTML5 local storage which provides significantly more capabilities for storin
g data on the browser client than was previously possible. Unlike cookies, local storage is not sent back and forth between the browser and server with each request. However, since that data can be accessed using JavaScript, it can be a source of exposure. In the same manner mobile apps can store data locally on a smart device, this data must be properly hashed and/or encrypted. Of course, this brings up the problem of key management, so any architect must look closely at the pros and cons of using such a mechanism. While it can greatly increase performance by allowing client-side caching, it is a double-edged sword that can leak information as well.
Browsers have had years to harden themselves against attacks, but they are still vulnerable in some cases. If an attacker is able to get in between the server and client in a MITM attack, he can use browser directives and headers that will cause the browser to give up the user’s browsing history. Browser settings can be used to defeat such an attack but only if properly configured.
Attackers will often look for backup, log or unreferenced files that get deployed unnoticed. These will often contain sensitive information that can be later used to exploit the software.