While it is always preferable to keep confidential information such as customer records away from a website, that is often not feasible. This is especially true in ecommerce, but is true in other areas as well. In order to protect this information, most people will employ some sort of authentication application to ensure that people can only access the data for which they are authorized. Despite this, attackers frequently find ways through or more often around this authentication mechanism.
Prevent an attacker from accessing customer records on a web site.
Solving confidentiality problems generally involves two activities. The first entails ensuring that whatever has to access the confidential data is not exploitable. This is a tall order, but is not impossible. The best strategy to take here is to make that program that has to access the confidential data as small and analyzable as possible. You can take great care in writing it and in analyzing it to ensure its correctness. So, I need to make my “guard” application that grants access to the data as small as possible. It should only contain code to do what it needs to do to authenticate the user and give back the data that user is authorized to see.
After ensuring that the application that needs access to the data is solid, the second activity in solving this problem is to ensure that nothing else can access the data. This is where I’ll use SELinux access controls. I’ll build an SELinux domain for the authentication application to run in as well as a type for the confidential records on disk. I’ll then allow only the authenitication application to access the confidential records. By doing this, I can prevent other things on the website (as well as the rest of the system) from accessing the records. An attacker may be able to compromise another part of my website, but he will not be able to get to my confidential records.1
I wrote a quick python script to use as my example application. It simply receives authentication data and displays the contents of the confidential records if the authentication is successful. This is admittedly an overly simple illustration, but those are often the best examples. I also wrote another simple script for demonstration purposes to serve as a vulnerable application also hosted on my website. I’ll have Apache execute these scripts as a cgi. Note that this will not work if I was using mod_python, as mod_python executes the script with the apache process. Consequently I’d be forced to try to confine apache itself, and I clearly can’t place all these restrictions on apache.
I created my SELinux policy with SLIDE, which filled in much of the details for me through a basic wizard. This wizard created a type for my application (records_t) and its corresponding executable (records_exec_t) and granted some basic accesses that almost everything needs (like access to shared libraries and locale). I then really only needed to add four things to make the application work.
- This creates a type for the confidential records, which separates it from the other files on the website so we can protect it.
- This simply gives the authentication application the ability to execute common binaries on the system (mostly in /bin and /usr/bin). In particular, this is necessary to execute python.
allow records_t record_data_t:file read_file_perms;
- This allows the application to read the confidential records. Note that the important part here is that I have not granted anything else the ability to read these records. Also, note that this is an allow rule rather than an interface call. Interface calls are used for interfacing with other modules within the policy. Here we are not interfacing with other policy modules, but are rather just allowing access internal to our policy module.
- This does a couple of things. First, it ensures that when Apache executes our web app, the web app runs in the proper domain (records_t). Secondly, it allows our web app to talk to Apache.
The full source code of the policy and the test applications are available here.
This is the authentication applicaiton granting access to the records after I enter proper credentials. This shows how things are supposed to work, as well as that our SELinux policy is letting the proper workflow occur without problems.
SELinux disabled SELinux enabled
This is a view of utilizing the vulnerable application (hacked.py) to get to the customer records. This application had no right to access this data, but without access controls stopping it, it can be used to bypass authentication. On the contrary, with SELinux enabled this program is denied access to the records.
1 Note that this example assumes that the confidential data is stored in a file that will be directly read by the web application. Another common scenario is that the confidential data is instead stored in a database which the web application accesses by talking to a database server. The operating system has no visibility into the database server, so SELinux can only control which web applications can talk to the database server, not what data the database server can give them. Thankfully, database server developers have thought of this already. In fact, there is a project to create a security enhanced postgresql server which cooperates with SELinux to enforce very powerful fine-grained mandatory access control over databases. With this, it’s possible to enforce access control over the individual entries in the database. Other databases, such as mysql, at least have a coarse-grained discretionary access control usually implemented as authentication credentials required for accessing a database. If this is a username and password stored in the web application’s config file, then I can use SELinux to protect that config file in the same way that I used SELinux to protect the records themselves above. So, it’s at least possible to indirectly control who can access the records stored in the database.
2 This policy is built on the upstream Fedora 7 targeted SELinux policy with one exception. The apache_cgi_domain() interface does not exist there yet. When I started working on this blog entry, this interface did not exist. So, I wrote it and upstreamed it to the Reference Policy project (which serves as upstream for most distros including Fedora). As of today, this interface hasn’t made it into Fedora yet, but it shouldn’t be long. For the above example, I backported this interface into the current Fedora policy.