Security in the Enterprise
All along, we have been looking at mobile applications from the perspective of individual developers. Although I believe that individual developers or smaller developer firms far outweigh enterprise developers, I think it would be useful to focus a bit on the enterprise developer and the unique challenges he can face. You might be tempted to skip this chapter because you do not fit into the “enterprise developer” category; however, I would urge you to consider this: most enterprises these days look at outsourcing their development work.
It might not make sense for an enterprise or business to have an in-house mobile development team, unless that is the company’s core business focus. I have seen numerous businesses outsourcing their development work to individuals or smaller companies, just so they don’t need to worry about managing an in-house mobile development team.
If there comes a time when a business hires you to develop a mobile application for it, then you might want to consider a few areas before you jump in and start developing. In most respects, your target base is much smaller than if you were releasing your application to the public.
One important thing, however, is that, in the case of the enterprise, you could be dealing with a lot more than a loss of personal information. For instance, in an enterprise environment, the likelihood that you will deal with confidential information (e.g., trade secrets, corporate financial information, or sensitive server credentials) is much higher than when you’re dealing with an application that is released to the general public. Additionally, your application could become more of a target given that, presently, many attackers view the mobile platform as “easy pickings” due to its lower level of security. Let’s first look at some of the key differences in enterprise applications when compared with an application released to the public.
Connecting to the enterprise environment from a remote location has become commonplace lately. Telecommuting, remote support, and outsourcing have all led to enterprise technology teams allowing authorized users into their organization’s networks. This doesn’t mean that the network admin just leaves the firewall wide open for telnet and remote desktops; the inbound connectivity is subject to certain security controls. To ensure the safest route, an organization will usually use a VPN, or virtual private network (see Figure 7-1), to allow remote users to join its network.
Figure 7-1. A virtual private network (VPN) (courtesy of Wikipedia)
A VPN is typically an additional logical or virtual network that a network administrator will usually create on her border network devices. This network acts as a bridge between a public network, such as the Internet, and the private internal network of an enterprise. Users can connect to the VPN over this public network and use the internal resources of the enterprise (including file servers, internal applications, and so on) just as if they were physically connected to the internal network.
VPNs are gradually making their way into the mobile space, as well. Devices such as the BlackBerry, iPhone, and Android are now able to connect to corporate networks and transfer data securely. Keep this in mind when you design for the enterprise. It’s quite likely that an enterprise network administrator will tell you that you need to use the VPN; but in the event that she fails to mention it, you should bring the subject up. Making an enterprise expose more than it should to the Internet is not the goal, here.
If, for some reason, you encounter an organization that does not have or use a VPN, then you might want to spend a bit of time arguing the merits of one. If it is an absolute no-go, then you’re going to have to encrypt data between your application and the server. Bear in mind, however, that it is costly to do so, especially if you have significant data exchange. In cases like this, you might also want to consider data compression. I give you an example of data compression later in this chapter. All this adds up to processor usage, though, and you will need to consider that this will, in almost all cases, drain your end user’s device battery.
Enterprise Applications
So just what are these enterprise applications I keep talking about? Rest assured, they’re not mythical like the unicorn; they do exist. If you’ve not had much opportunity to work within enterprises, then you might not immediately recognize an enterprise system. There are many different types, but here we will focus on enterprise resource planning (ERP) applications, mainly because they tend to cover a broad spectrum of uses in the enterprise. Your typical ERP applications usually cover one of the following areas:
It is quite likely that the ERP applications you will have to work with are mature and well established. It is also likely that, as the new developer, you will have to write your application to work with the existing systems. This can be a bit frustrating, especially when it means you have to compromise on some form of functionality in your mobile application. One of the best ways around this, in my opinion, is to adopt and use some form of mobile middleware.
Mobile Middleware
Instead of driving yourself insane trying to make your mobile application work with a legacy enterprise application, you might do better if you spend some time developing your own mobile middleware platform. Simply put, the mobile middleware platform acts as the go-between in your mobile app’s communication with the enterprise system. The goal is to allow your mobile app to be able to work with the data in the enterprise app without compromising on operating system features or the limited resources available on a mobile device.
I once tested the security of a banking mobile application. The mobile application developer followed the idea of using mobile middleware when integrating with a very proprietary, closed, and inadequately documented application. The developer created a mobile middleware component in the form of a screen translator. Essentially, this was a server-based application that would fetch the website from the banking application, mine or copy all the text on specific pages, and then convert these pages into mobile-formatted text.
Take a look at Figure 7-2. It shows how a mobile application can connect to a middleware system that abstracts the data and user interface of a legacy application. In some cases, the mobile client can access the legacy application directly through the mobile browser, but it would not provide the ideal user experience in this case. Thus, by interfacing with mobile middleware, an application’s communications infrastructure can be standardized. Most of the interaction with the legacy application will be done on more powerful hardware.
Figure 7-2. Mobile middleware example
With this in mind, we need to identify some of the key scenarios that we will encounter when we decide to develop enterprise mobile applications. In this chapter, I look at two areas that have proven to be a challenge when developing enterprise mobile apps: database access and data representation. These specific areas have proven to be a challenge during mobile enterprise application development. Let’s start with database access.
Android supports the javax.sql and java.sql packages that you can use to access a database server. Let’s start with a very straightforward, but insecure, example application—just to show you how this approach falls short. Next, we will look at some better techniques. You may wonder why I am wasting your time by looking in some detail at an insecure solution. The point is to see why it is insecure; it is only when you understand how it is insecure that will you fully appreciate the advantages of the correct approach. Feel free to skip forward—at your own peril!
The application will connect to a MySQL database and read the data from the table called apress. To execute this correctly, both the Android device and the database server should reside on the same network. I will leave the database setup and creation up to you. Make sure that you set up the database server to listen on the public IP address. You can do this by editing the my.cnf file in your MySQL installation. Listing 7-1 contains the database schema. Make sure you create the database named android first. After you create the table, enter some test data into it, so that you can retrieve it when you connect to it with your Android app.
Listing 7-1. A MySQL SQL Statement to Create the apress Table
CREATE TABLE `apress` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL DEFAULT '',
`email` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE = MyISAM AUTO_INCREMENT = 4 DEFAULT CHARSET = latin1;
Let’s get started with our app development now. Create a new project called MySQLConnect. In your project folder, create a new folder called lib. Now download the latest version of MySQL Connector/J from www.mysql.com/products/connector/. Next, decompress the archive and copy the .jar file to your lib directory. The file should look something like mysql-connector-java-5.1.15-bin.jar. If you’re using Eclipse to develop, then your project layout will look something like the one in Figure 7-3. In my layout, you can see that I have several versions of the MySQL Connector, but I’m using the latest version.
Figure 7-3. The MySQLConnect project structure
In this example, we create a ListView layout. This renders a nice full-screen list of the data we retrieve from our database. Inasmuch as the ListView will contain individual items, we have to tell Android what each item is. To do this, we create a new XML file called list_item.xml containing the text in Listing 7-2, and then save this under the layout folder, as shown in Figure 7-3.
Listing 7-2. The list_item.xml File Contents
<?xml version = "1.0"encoding = "utf-8"?>
<TextView xmlns:android = "http://schemas.android.com/apk/res/android"
android:layout_width = "fill_parent"
android:layout_height = "fill_parent"
android:padding = "10dp"
android:textSize = "16sp">
</TextView>
This tells Android that each list item is of a text type and gives it some further details about its text padding and font size. Next comes the code for the MySQLConnectActivity.java file (see Listing 7-3). Make a note to change the host IP address, username, and password to what you have created.
Listing 7-3. The MySQLConnectActivity.java Source Code
package net.zenconsult.android;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Hashtable;
import android.app.ListActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class MySQLConnectActivity extends ListActivity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Connection conn = null;
String host = "192.168.3.105";
int port = 3306;
String db = "android";
String user = "sheran";
String pass = "P@ssw0rd";
String url = "jdbc:mysql://" + host + ":" + port + "/" + db + "?user = "
+ user + "&password = " + pass;
String sql = "SELECT * FROM apress";
try {
Class.forName("com.mysql.jdbc.Driver").newInstance();
conn = DriverManager.getConnection(url);
PreparedStatement stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery();
Hashtable < String, String > details = new Hashtable < String, String > ();
while (rs.next()) {
details.put(rs.getString("name"), rs.getString("email"));
}
String[] names = new String[details.keySet().size()];
int x = 0;
for (Enumeration < String > e = details.keys(); e.hasMoreElements();) {
names[x] = e.nextElement();
x++;
}
conn.close();
this.setListAdapter(new ArrayAdapter < String > (this,
R.layout.list_item, names));
ListView lv = getListView();
lv.setTextFilterEnabled(true);
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView < ? > parent, View view,
int position, long id) {
Toast.makeText(getApplicationContext(),
((TextView) view).getText(), Toast.LENGTH_SHORT).show();
}
});
} catch (ClassNotFoundException e) {
Log.e("MYSQL", "Class not found!");
} catch (SQLException e) {
Log.e("MYSQL", "SQL Exception " + e.getMessage());
} catch (InstantiationException e) {
Log.e("MYSQL", "Instantiation error " + e.getMessage());
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Because we’re accessing the network, you have to make sure that your app has the android.permission.INTERNET permission set in the AndroidManifest.xml file.
Save your project and run it on your Android simulator. Your app should start up, connect to the database, retrieve the data, and display it in a full-screen list view similar to that shown in Figure 7-4.
Figure 7-4. The output when the app is executed correctly
As you can see, even though we are able to read data directly from a database, there seems to be a lot of cumbersome code that we need to write, in addition to packaging large JDBC driver libraries with our app.
In some cases, if you have to connect to a database without pure JDBC drivers, then you’re stuck. If you look at the security implications, then you need to consider that your database server has to be exposed to the Internet or on the VPN because both the mobile device and the database server should be able to talk to each other. Finally, you can see that the database credentials are stored within the application.
Look at the following section of code:
Connection conn = null;
String host = "192.168.3.105";
int port = 3306;
String db = "android";
String user = "sheran";
String pass = "P@ssw0rd";
String url = "jdbc:mysql://" + host + ":" + port + "/" + db + "?user = "
+ user + "&password = " + pass;
String sql = "SELECT * FROM apress";
The lines starting with String user and String pass show how the database credentials are hardcoded in the application. If the phone is compromised, an attacker can read the database credentials from your app’s data and use them to connect from another computer and attack your database directly.
Therefore, it is not the best approach to use native JDBC connectivity in your Android app. It is better to write a mobile middleware module to allow the app to access the data in a more convenient and secure manner.
How can we improve the database access process? One of the simplest and possibly most mature request/response mechanisms is HTTP. By using HTTP, we can certainly simplify and improve the security of our data access methods. Android already has a very capable HTTP client built in; we have SSL to protect our data; and, if required, we can add an additional layer of encryption to the data going back and forth. You might say it’s a no-brainer to use HTTP, so let’s do just that.
But how are we supposed to use HTTP to request data from a database? We can use web services to fetch data from our back end. Rather than making very complex web services, we can use REST (representational state transfer) to communicate. Exposing a RESTful API will greatly simplify how our mobile application requests data. Consider this example:
https://192.168.3.105/apress/members
By making this get request, we can fetch the same set of data that we fetched in our MySQLConnect example earlier. It is definitely much simpler to use an HTTP request to fetch the data. Of course, the next step is in retrieving the data. Because we’ve picked HTTP as our transport mechanism, we have to use a response mechanism that is also HTTP-friendly. This brings us to the problem of data representation. We look at that in the next section.
I hope you’re building your own set of libraries for reuse later on. It is a very good practice to get into. I have several different libraries that I create for different tasks when I develop. I have one that handles database connections, one that handles data encoding and decoding, and many other small utility libraries that I use when I build apps. They speed up my development cycles and generally keep everything in a consistent state. I bring this point up now because, if you are going to embark on the journey to build your own custom mobile middleware, then you would be better off if you designed it so that you can plug it into as many deployment scenarios as possible. From there, you can just tweak configuration settings, so you can get up and running quickly.
Note Custom Libraries
Developing your own libraries as you go is a good practice. For me, writing my own libraries means I will never forget a particular implementation that I did months ago. I can simply call up my shared library function and integrate it with few or no concerns.
Bear in mind, however, that all your external library functions should be extremely simple. These basic functions can later be strung together to perform one complex function. Thus, you can build upon your libraries and completely speed up your development time.
Imagine you spent a lot of time and effort in writing your client an e-commerce application. After your project is completed, there might not be an explicit requirement to keep the source code around. This could matter to you, however, if you meet another customer that wants you to build a similar e-commerce store. Provided you have undisputed ownership of the code you wrote in the earlier application, you can reuse it and, thus, drastically reduce the time required to prepare new applications.
Having got that out of the way, let’s talk about data representation. By data representation, I’m referring to how your mobile application receives data from the back-end web application. In our case, we’re trying to standardize how our mobile app will receive and treat the data. The most common data representation formats available today are XML (eXtensible Markup Language) and JSON (JavaScript Object Notation). So, let’s aim to write our mobile application framework to receive and process this type of data. Refer to the appendix for a quick primer on XML and JSON. Another reason to select this type of data representation is that there are many third-party, open source libraries that you can either use or adapt to suit your purpose.
Getting back to our RESTful API request, let’s look at the following two potential responses we could have from our mobile middleware:
XML
<?xml version = "1.0" encoding = "UTF-8"?>
<apress>
<users>
<user name = "Sheran" email = "[email protected]" />
<user name = "Kevin" email = "[email protected]" />
<user name = "Scott" email = "[email protected]" />
</users>
</apress>
JSON
{
users:{
user:[
{
name:'Sheran',
email:'[email protected]'
},
{
name:'Kevin',
email:'[email protected]'
},
{
name:'Scott',
email:'[email protected]'
}
]
}
}
The good part is you won’t need to write so much code to read the XML and JSON representations. Android includes libraries for parsing both formats. Let’s look at some source code. Once again, create a new project and call it RESTFetch. Create the list_item.xml file as you did in the previous example, and then assign the android.permission.INTERNET permission to the app. Listing 7-4 contains the code to the app that will make a request, process the XML response, and render the results in a list. Figure 7-5 contains the output.
Listing 7-4. Fetching Data Using the RESTful API and Processing XML Output
packagenet.zenconsult.android;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import android.app.ListActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class RESTFetchActivity extends ListActivity {
@Override
public voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BufferedReader in = null;
try{
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet();
request.setURI(new URI("http://192.168.3.105/apress/members"));
HttpResponse response = client.execute(request);
in = new BufferedReader(new InputStreamReader(response.getEntity()
.getContent()));
StringBuffer sb = new StringBuffer("");
String line = "";
String newLine = System.getProperty("line.separator");
while ((line = in.readLine()) ! = null ) {
sb.append(line + newLine);
}
in.close();
Document doc = null ;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(sb.toString()));
doc = db.parse(is);
NodeList nodes = doc.getElementsByTagName("user");
String[] names = new String[nodes.getLength()];
for (int k = 0; k < nodes.getLength(); ++k) {
names[k] = nodes.item(k).getAttributes().getNamedItem("name")
.getNodeValue();
}
this .setListAdapter(new ArrayAdapter < String > (this ,
R.layout.list_item, names));
ListView lv = getListView();
lv.setTextFilterEnabled(true );
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView < ? > parent, View view,
int position,long id) {
Toast.makeText(getApplicationContext(),
((TextView) view).getText(), Toast.LENGTH_SHORT)
.show();
}
});
}catch (IOException e) {
Log.e("REST", "IOException " + e.getMessage());
}catch (URISyntaxException e) {
Log.e("REST", "Incorret URI Syntax " + e.getMessage());
}catch (ParserConfigurationException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}catch (SAXException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Figure 7-5. The output from the RESTful API query with XML response
For the JSON request/response code and output, take a look at Listing 7-5 and Figure 7-6, respectively.
Listing 7-5. Fetching Data Using the RESTful API and Processing JSON Output
package net.zenconsult.android;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.ListActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class RESTJSONActivity extends ListActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
BufferedReader in = null ;
try {
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet();
request.setURI(new URI("http://192.168.3.105/apress/members.json"));
HttpResponse response = client.execute(request);
in = new BufferedReader(new InputStreamReader(response.getEntity()
.getContent()));
StringBuffer sb = new StringBuffer("");
String line = "";
while ((line = in.readLine()) ! = null ) {
sb.append(line);
}
in.close();
JSONObject users = new JSONObject(sb.toString())
.getJSONObject("users");
JSONArray jArray = users.getJSONArray("user");
String[] names = new String[jArray.length()];
for (int i = 0; i < jArray.length(); i++) {
JSONObject jsonObject = jArray.getJSONObject(i);
names[i] = jsonObject.getString("name");
}
this .setListAdapter(new ArrayAdapter < String > (this ,
R.layout.list_item, names));
ListView lv = getListView();
lv.setTextFilterEnabled(true );
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView < ? > parent, View view,
int position,long id) {
Toast.makeText(getApplicationContext(),
((TextView) view).getText(), Toast.LENGTH_SHORT)
.show();
}
});
}catch (IOException e) {
Log.e("RESTJSON", "IOException " + e.getMessage());
}catch (URISyntaxException e) {
Log.e("RESTJSON", "Incorret URI Syntax " + e.getMessage());
}catch (JSONException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Figure 7-6. The output from the RESTful API query with a JSON response
If required, you can combine both the XML and JSON examples into one class file. To distinguish between the response types, you can usually append a file extension to the members request. Thus, for an XML response, call http://192.168.3.105/apress/members.xml; and, for a JSON response, call http://192.168.3.105/apress/members.json. Again, we can modify our examples so that we analyze the response data to discover the structure automatically. This will free us to extract data based on certain keywords, regardless of where they appear. In most cases, however, it doesn’t hurt to define your data structure in your code because, after all, your mobile app will only talk to your mobile middleware.
Speaking of mobile middleware, where exactly is the server-side code to generate the XML and JSON responses? At the present time, such code is beyond the scope of this book. But in order to give you a better understanding of how you can implement this type of mobile middleware, take a look at the appendix for a very basic example that also shares deployment instructions.
Summary
We took a very quick look at two of the problems you would face if asked to develop a mobile application that works with a legacy enterprise system. No doubt, you might come across different challenges when you set foot in the realm of mobile enterprise app development. In almost all cases, you can overcome these problems by building translation or bridge modules in your mobile middleware.
As far as security is concerned, at the beginning of this chapter, we discussed that opening up the enterprise environment to the public is a bad idea. The best approach is to reduce the exposure that enterprise systems have by using middleware. We decided to use HTTP, not only for its simplicity, but also because we don’t need to do anything magical to secure it. The same security controls as SSL can be applied without having to change any of our code. Of course, we could also create additional layers of encryption and compression for our data.
3.147.57.145