Using SSH Tunneling in Your Application

Introduction

This article is dedicated to the task of securing a MySQL client-server connection using functionality provided by the Secure Shell (SSH) protocol. To be exact, the SSH tunneling concept is utilized. You first will review the steps needed to build secure MySQL client applications and implement a sample one yourself.

MySQL traffic is not the only kind of data that can be tunneled by the Secure Shell. SSH can be used to secure any application-layer TCP-based protocol, such as HTTP, SMTP, and POP3. If your application needs to secure such a protocol by tunneling it through a protected SSH connection, this article will be useful to you.

Background

Imagine that you are developing an enterprise application that needs to send requests to a number of SQL servers all over the world and get responses from them (imagine that it's a super-powerful bank system that stores information about millions of accounts).

Take a look at what you have:

As you see, all the data between the application and SQL servers are transferred via the Internet "as is". Because most protocols used by SQL servers do not provide data integrity and confidentiality (and those that do, do it in a quite nontransparent way), all the transferred requests and responses may (and be sure, they will!) become visible to a passive adversary. An active adversary can cause much more serious problems—he can alter the data and no one will detect it!

SSH (Secure Shell) is a protocol that may help solve this problem. One of its outstanding features is its ability to tunnel different types of connections through a single, confident, and integrity-protected connection.

It works in the following way:

Now you do not have to worry about securing the data transferred over the Internet; SSH will handle this for you. In particular, SSH will take care of the following security aspects:

  • Strong data encryption according to the latest industry-standard algorithms (AES, Twofish)
  • Authentication of both client and server computers
  • Data integrity protection
  • Stability with regard to different kinds of network attacks
  • Compression of the data being tunneled
  • Complete independence of the operating system and network specifics

Tunneling (or forwarding) works in the following way:

  1. The SSH client opens a listening port on some local network interface and tells the SSH server that he wants to forward all connections accepted on this port to some remote host.
  2. When another connection is accepted on the listening port, the SSH client informs the SSH server about this fact and together they establish a logical tunnel for it. At the same time, the SSH server establishes a new TCP connection to the remote host agreed upon in Step 1.
  3. The SSH client encrypts all the data it receives from the accepted connection and sends it to the SSH server. The SSH server decrypts the data received from the SSH client and sends it to the remote host.
Note: The SSH client acts as a TCP server for the connections it accepts, and the SSH server acts as a TCP client for the connections it establishes to the remote host.

A single SSH connection can tunnel as many application layer connections as needed. This means that you can defend your server by moving all the listening ports (for example, database and application server ports) to a local network, leaving only the SSH port open. It is much easier to take care of a single port rather than a dozen different listening ports.

Using SSH Tunneling in Your Application

Into the Fire!

Now, you can develop a small application that illustrates the use of SSH forwarding capabilities. You will consider an important task of securing a connection between a MySQL client application and a MySQL server. Imagine that you need to get information from the database server, which is located a thousand miles away from you, in a secure way.

The following picture explains the scheme you will utilize:

[SSHTunneling_html_3f36b0ec.gif]

SecureMySQLClient is the application you plan to implement. It includes the following modules:

  • SSH client-side module with forwarding capabilities
  • MySQL client-side module
  • User interface for configuring application settings and displaying query results

The SSH server runs in a remote network and is visible from the Internet. The database (MySQL) server runs in the same network as the SSH server and may not be visible from the Internet.

The process of performing secure data exchange between SecureMySQLClient and the Database server goes as follows:

  1. The SSH client module negotiates a secure connection to the SSH server and establishes forwarding from some local port to the remote MySQL server.
  2. The MySQL client module connects to the listening port opened by the SSH client module.
  3. The SSH client and server set up a logical tunnel for the accepted connection.
  4. The MySQL client sends SELECT to the port opened by the SSH client module, which encrypts it and sends it to the SSH server. The SSH server decrypts the request and sends it to the MySQL server.
  5. The SSH server receives a response from the MySQL server, encrypts it, and sends it back to the SSH client, which decrypts it and passes it to the MySQL client module.

Looks too complex? Implementing this is easier than you think. So, go and do it.

You will need the following products installed on the computer before creating the application:

Now, open Microsoft Visual Studio .NET (I will use the 2005 version) and try to build such an application from scratch.

[SSHTunneling_html_m3f11365d.gif]

Using SSH Tunneling in Your Application

Start by creating a simple user interface:

[SSHTunneling_html_m2dea8e69.gif]

After the GUI design has been finished, you can go on with the business logic code itself. First, add references to the following assemblies to your project:

  • SecureBlackbox
  • SecureBlackbox.PKI (only in SecureBlackbox 5. SecureBlackbox 6 doesn't have this assembly)
  • SecureBlackbox.SSHClient
  • SecureBlackbox.SSHCommon
  • MySql.Data

Place the ElSSHLocalPortForwarding component on the form and give it the SSHForwarding name:

[SSHTunneling_html_3bf25864.gif]

SSHForwarding notifies you about certain situations via its events, so you need to create handlers for some of them:

OnAuthenticationSuccess Is fired when the client authentication process has been completed.
OnAuthenticationFailed Is fired if the client was unable to authenticate using the particular authentication method. In general, this does not mean that the authentication process completely failed; the client may try several authentication methods consequently and one of them may succeed.
OnError Is fired if some protocol error occurs during the session. Usually, this leads to a connection closure. The exact error can be detected via the error code passed to it.
OnKeyValidate

Is used to pass the received server key to the application. Please note that incorrect handling of this event may result in a serious security breach. The handler of this event should verify that the passed key corresponds to the remote server (and warn the user if it does not). If the key is valid, the handler should set the Validate parameter to true.

The sample does not perform key checkup for the sake of simplicity.

OnOpen Is fired when the SSH connection is established and the component is ready to tunnel data. You will use the handler of this.
OnClose Is fired when the SSH connection is closed.
OnConnectionOpen Is fired when a new tunnel is created. The corresponding tunneled connection object is passed as a parameter.
OnConnectionClose Is fired when an existing tunnel is closed.

Implement two core methods, SetupSSHConnection() and RunQuery(). The first one initializes the SSHForwarding object and establishes an SSH session to the remote server by calling its Open() method; the second one sends the query to the MySQL server.

The logic is displayed in the following picture:

[SSHTunneling_html_m7b09ec7d.gif]

Using SSH Tunneling in Your Application

The code of the SetupSSHConnection() method is pretty simple:

private void SetupSSHConnection()
{
   // Specifying address and port of SSH server
   Forwarding.Address = tbSSHAddress.Text;

   Forwarding.Port = Convert.ToInt32(tbSSHPort.Text);
   // Setting credentials for authentication on SSH server
   Forwarding.Username = tbUsername.Text;

   Forwarding.Password = tbPassword.Text;

   // Specifying network interface and port number to be
   // opened locally

   Forwarding.ForwardedHost = "";
   Forwarding.ForwardedPort = Convert.ToInt32(tbFwdPort.Text);

   // Specifying destination host where the server should
   // forward the data to.
   // Please note, that the destination should be specified
   // according to SSH servers point of view. E.g., 127.0.0.1
   // will stand for SSH servers localhost, not SSH clients one.
   Forwarding.DestHost = tbDBAddress.Text;
   Forwarding.DestPort = Convert.ToInt32(tbDBPort.Text);

   // Opening SSH connection
   Forwarding.Open();
}

A bit more complex is the code of the RunQuery() method (to be exact, the code of RunQueryThreadFunc() method, which is invoked in a separate thread by the RunQuery() method):

private void RunQueryThreadFunc()
{
   MySqlConnection MySQLConnection = new MySqlConnection();

   // forming connection string
  string connString = "database=" + tbDBName.Text + ";
      Connect Timeout=30;user id=" + tbDBUsername.Text + "; pwd="
      + tbDBPassword.Text + ";";
   if (cbUseTunnelling.Checked)

   {
      // specifying local destination if forwarding is enabled

      connString = connString + "server=127.0.0.1; port=" +
         tbFwdPort.Text;
   }
   else
   {
      // specifying real MySQL server location if forwarding is
      // not used
      connString = connString + "server=" + tbDBAddress.Text + ";
         port=" + tbDBPort.Text;
   }

   MySQLConnection.ConnectionString = connString;
   try
   {
      // opening MySQL connection
      MySqlCommand cmd = new MySqlCommand(tbQuery.Text,
                                          MySQLConnection);
      Log("Connecting to MySQL server...");

      MySQLConnection.Open();
      Log("Connection to MySQL server established. Version: " +
          MySQLConnection.ServerVersion + ".");

      // reading query results

      MySqlDataReader reader = cmd.ExecuteReader();
      try
      {
         for (int i = 0; i < reader.FieldCount; i++)
         {
            AddQueryColumn(reader.GetName(i));
         }
         while (reader.Read())
         {
            string[] values = new string[reader.FieldCount];
            for (int i = 0; i < reader.FieldCount; i++)
            {
               values[i] = reader.GetString(i);
            }
            AddQueryValues(values);
         }
      }
      finally
      {
         // closing both MySQL and SSH connections
         Log("Closing MySQL connection");
         reader.Close();
         MySQLConnection.Close();
         Forwarding.Close();
      }
   }

   catch (Exception ex)
   {
      Log("MySQL connection failed (" + ex.Message + ")");
   }
}

And, that's all!

Using SSH Tunneling in Your Application

But, there is one more thing I need to draw your attention to. Because both SSH and MySQL protocols run in separate threads and access GUI controls from those threads, you need to handle the GUI access in a special way to prevent cross-thread problems. I will illustrate this with an example of the Log() method:

delegate void LogFunc(string S);

private void Log(string S)
{
   if (lvLog.InvokeRequired)
   {
      LogFunc d = new LogFunc(Log);
      Invoke(d, new object[] { S });
   }
   else
   {
      ListViewItem item = new ListViewItem();
      item.Text = DateTime.Now.ToShortTimeString();
      item.SubItems.Add(S);
      lvLog.Items.Add(item);
   }
}

Finally, the application is finished, and you may try it. So, press F5 and specify the following settings in the text fields of the application form:

  • SSH server location, username, and password used to authenticate to it.
  • Database server address, port, username, password, database name, and query. Remember that the database server address should be specified because it is visible from the SSH server.
  • Turn on the "Use tunneling" checkbox.

Now, click the Start button and wait for the query results. If all the parameters have been specified correctly, you should get something like this:

[SSHTunneling_html_m5cc99902.gif]

Features and Requirements

SSH protocol provides (and SecureBlackbox implements) the following features:

  • Strong data encryption using AES, Twofish, Triple DES, Serpent, and many other symmetric algorithms with key lengths up to 256 bits
  • Client authentication using one or multiple authentication types (password-based, public key-based, X.509 certificate-based, or interactive challenge-response authentication)
  • Server authentication
  • Strong key exchange based on DH or RSA public key algorithms
  • Data integrity protection
  • Compression of tunneled data
  • Multiplexing several tunneled connections through a single SSH connection

SecureBlackbox provides the following functionality as well:

  • Comprehensive standards-compliant implementation of the SSH protocol (both client and server sides)
  • Support for cryptographic tokens as storage for keys and certificates
  • Windows system certificate stores support
  • Professional and fast customer support

SecureBlackbox is available in .NET, VCL, and ActiveX editions. This means that you can use the components in projects implemented in the C#, VB.NET, Object Pascal (Delphi and Kylix), FreePascal, VB6, and C++ languages.

SecureBlackbox (.NET edition) is available for Microsoft .NET Framework 1.1, 2.0, 3.0, and 3.5, and the .NET Compact Framework.



Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • With JRebel, developers get to see their code changes immediately, fine-tune their code with incremental changes, debug, explore and deploy their code with ease (both locally and remotely), and ultimately spend more time coding instead of waiting for the dreaded application redeploy to finish. Every time a developer tests a code change it takes minutes to build and deploy the application. JRebel keeps the app server running at all times, so testing is instantaneous and interactive.

  • Live Event Date: May 6, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT While you likely have very good reasons for remaining on WinXP after end of support -- an estimated 20-30% of worldwide devices still are -- the bottom line is your security risk is now significant. In the absence of security patches, attackers will certainly turn their attention to this new opportunity. Join Lumension Vice President Paul Zimski in this one-hour webcast to discuss risk and, more importantly, 5 pragmatic risk mitigation techniques …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds