Magazine PDF Issue Conference Forum Software & Support Verlag











from PHP Magazine - International Edition Issue: 03.2003

Pushing & Pulling

Pushing and Pulling GnuPG documents via FTP protocol
Michael Hüttermann

Highly sophisticated applications do not only consist of web forms processed interactively. With underlying modules interfacing external services and different technologies managing a clever workflow the application quickly becomes quite complex. This article introduces the technical details of setting up a batch system which uses FTP communication with encrypted files. Possibly even more important, a basic understanding of an approach which interfaces heterogeneous platforms with PHP as the

Web applications can be divided into different layers. Firstly, you have the presentation layer containing HTML, probably excluding PHP logic, supported by a template engine. The middle tier describes the business logic. It is responsible for data persistence, handling transactions and implementing the use cases. The basic tier is the data layer, normally in the form of a database. If you further divide these layers into further logical parts, your 3-tier application quickly becomes a n-tier application.

The most regular event in web applications is of course presenting data to the client (browser), and consequently receiving data or tasks from the client. These events are processed on-the-fly. The surfer clicks a button in a form. These user interactions result in web server requests. Data are forwarded using the HTTP protocol using GET or POST methods. This is the traditional PHP world.

The other way
On the other hand, some other tasks cannot be processed directly or are exclusively triggered in the middle tier. Such an example could be pushing generated files continuously to a remote server. For example special profiling files are exchanged with a host every 15 minutes. Another example is the nightly processing of data which were accumulated during the last day. Single items are processed together - single executions do not make sense or result in too much overhead. The solution is to trigger a module only at specific times -batch jobs. A batch job could, for example, submit your data to an interface of a remote system. These jobs run independently of any browser and, due to security requirements and workflow specifications, need a very highly sophisticated solution.

But what do you do if your solutions are normally PHP based? Your common business logic is already written in PHP. Do you have to switch your platform or language at least partly in order to implement these new requirements? No! Sure, although PHP allows for the integration of different languages and platforms such as COM or Java, this usually is only a good idea when you have to access some sort of legacy systems running on these platforms. However, if you have to develop a new solution from scratch you don't need to switch your paradigm, you can achieve your goal using PHP exclusively. And if PHP does not support exactly what you need, you can combine existing solutions accordingly.

Backend jobs not triggered via a web browser which interfaces several different technologies? In this article I want to illustrate a pragmatic solution under these conditions by discussing a practical application which secures documents via GnuPG and uploads and downloads documents to a remote host.

The reference implementation
In our use case BookKing Inc. is a traditional Internet company. Its main business is a B2C service selling books to online customers. All these transactions have to be charged. Submitting these events directly and individually to the bank would result in an unacceptable overhead. The remote bank system has to interpret incoming bookings separately and continuously. Therefore, a batch exchange system was set up. BookKing has to provide a special booking file each day which contains all the necessary sales of the last day. As this file is submitted over the public internet, a security feature is necessary in order to protect the highly sensitive customer payment data.

Technically, we will provide a module which has to run without any web server. This module uploads the booking files via FTP to the bank. These files are generated from database records of transactions created during the last 24 hours. The bank already has some standards: the request file has to obey a proprietary format. Therefore, we have to implement a converter which maps our data to this external format. The files are encrypted with GnuPG with our partner's public key then uploaded. The whole thing is controlled by software which is based on the following integrated units:
  • GnuPG
  • FTP communication
  • Request/Response broker

GnuPG
GnuPG, the open source alternative to the commercial asynchronous encrypting application PGP, enables you to securely encrypt files. GNU Privacy Guard (GnuPG) implements the OpenPGP standard without using the patented IDEA algorithm. A basic installation of GnuPG for private/public key encoding is required. This enables an own key pair to be generated and a public key to be imported to the local key ring. The public keys have to be exchanged with the business partner. Keys should be signed. The main program of GnuPG system is 'gpg'. See the GnuPG online manual for details. In the following we assume a fully configured GnuPG environment, but we will discuss further details later.

Listing 1

class process{
function process($SET)
{
$this->path_gpg = $SET['path_gpg'];
$this->path_files = $SET['path_files'];
$this->path_curl = $SET['path_curl'];
$this->host = $SET['host'];
$this->path_upload = $SET['path_upload'];
$this->path_download = $SET['path_download'];
$this->username = $SET['username'];
$this->password = $SET['password'];
$this->mantra_file = $SET['mantra_file'];
$this->recipient = $SET['recipient'];
$this->plain_file = $SET['plain_file'];
}

function do_gpg_crypt()
{
$this->file_crypted=$this->plain_file.".pgp";
$gpg_opt = "-a --no-tty --batch --yes
--no-secmem-warning
-o {$this->path_files}.
"/".{$this->file_crypted}
-r $this->recipient
-e {$this->path_files}."/".
{$this->plain_file}";
$gpg_cmd = $this->path_gpg." ".$gpg_opt;
exec($gpg_cmd,$return,$exit_code);
return $exit_code;
}


The input for the encrypting module is a plain file located in the file system. It is generated from your persistence layer according to the partner's data exchange format. To start the process, an instance from our basic class is created. The constructor sets the necessary pre-settings: the path to the GnuPG and cURL executables, the path where our file is located, the settings of the remote FTP server, the location where the GnuPG mantra is stored and the GnuPG recipient for whom we want to secure the file. Then, the function call of do_gpg_crypt() does the encrypting. If the GnuPG execution was successful 0 is returned. Listing 1 shows the constructor and the do_gpg_crypt() method.

What does this function do exactly? Well, at the beginning the new target file is named, concatenating the plain file name with the PGP extension. After that the GnuPG call is prepared by setting all necessary options which ensure a most quiet and independent batch execution. Finally, GnuPG is executed and the exit code is returned, which can be checked. Generally, you should make sure that you don't use exec() frivolously. It could be a big security hole if the user is able to manipulate its input in any form. In our case, exec() is used only from the backend and called with well-formed, pre-defined values. The Table 1 shows the GnuPG options used here.


Table 1: GnuPG encrytion options

gpg -a create ASCII armoured output
gpg --no-tty do not use any terminal for output
gpg --batch batch mode, no interactions
gpg --yes assume yes for most questions
gpg --no-secmem-warning don't mention any memory warnings
gpg -o file write output to file
gpg -r name encrypt for user id name - recipient (public key)
gpg -e file encrypt file

FTP
Upload is done using the FTP protocol. Although PHP is normally compiled with FTP, here we employ another option. Inside our application some modules communicate with each other via HTTP POST, e.g. the presentation layer and the business logic. Thus, we have already prepared a PHP environment which includes cURL.

cURL is a command line tool to transfer data in URL syntax. It has a full-featured library and supports several different protocols, such as HTTP and FTP. In order to minimize our technical requirements (the more complex your application is, the more errors may occur), we use cURL for our FTP transactions as well.

Listing 2

function do_upload ()
{
$curl_opt="-T {$this->path_files}."/".
{$this->file_crypted}
-u $this->username:$this->password
$this->host."/".$this->path_upload";
$curl_cmd = $this->path_curl." ".$curl_options;
exec ($curl_cmd, $return, $exit_code);
return $exit_code;
}

OK, we decided to use cURL. Now we have to do the coding. There are two possible approaches. Firstly, we could do it straightforwardly via a PHP interface for cURL. Due to some lack of documentation for all possible features and the general overhead, we chose another way here, to directly access cURL via command line. Therefore, we introduce another member function which does this job, shown in Listing 2. Remember that we only show the core functionality here without any nice error handling, documentation and so on.


Table 2: cURL upload options

curl -T file file to be uploaded
curl -u username:password credentials concatenated with a semicolon

At the beginning of our upload method the cURL options are set. Then, cURL command is prepared while bringing together the cURL executable and its options (please see Table 2 for an overview of options used). Finally, it is executed and the result returned. Hopefully the result is 0 which means it was executed successfully. Now we only need the PHP environment to employ our functionality.

Request Broker
Basically, the request broker is a PHP module doing the whole job of encrypting, communication handling and error handling. This request broker should be triggered by a small shell script at determined times.

Let's examine this more carefully, beginning with the command line environment. Remember, you need this to execute your PHP script via shell. In general, this is not always the best or recommended solution. In particular, this allows for the smooth execution of the PHP binary via shell. PHP-GTK, for instance, uses the command line PHP as well. Two different options are available. Firstly, the traditional way is to use the CGI version of PHP. Basically, you achieve this by skipping options such as -with-apache or -with-apxs while configuring PHP.

The other approach has been stable since version 4.3.0 of PHP. Here, a different SAPI than the CGI one is provided - the command line interface CLI. It will be compiled alongside your chosen SAPI while installing PHP. The behaviour is a little bit different from that of the CGI, e.g. the CGI version does send any headers like: X-Powered-By: PHP/4.1.2 Content-type: text/html. Our solution was developed with the CGI version, but the CLI version should also do the job.

In addition to our request broker and the service class, we should create a small shell script. This shell script could contain some basic settings relating to path, database libraries and so on. But mainly, the bash script could be named call_request_broker.sh and it calls the PHP module as shown in Listing 3. The date output is helpful for logging purposes.

Listing 3

date;
root=/var/lib
/usr/php -d include_path=
.:$root $root/request_broker.php

As we do not want to do the call manually we need to add an entry to our crontab. This could look like the line shown below. You edit crontab with crontab -e and list it with crontab -l. In the example our script is called each day of the year at 3 a.m. The output of the call is added to the log file request.log. The date will be written nicely and shows the time of execution.

0 3 * * * /var/lib/call_request_broker.sh >>
/var/log/request.log 2>&1


Now you are finished. Every day the request broker is triggered by your shell script initiated by cronjob. Of course, you should polish your whole solution with detailed status protocols.

The other way around
The previous sections introduced the first direction of communication. You successfully uploaded encrypted files to a remote host with batch mode. Let's consider the case where some sort of response document is received. This document is also placed on the remote host in encrypted format. Therefore, you need to download, encrypt and process it. In the following we discuss the core snippets which does this work similarly to the first direction.

Firstly, the download. The response file could have any arbitrary name which is passed to our service class method via a parameter. After setting the options, the call is made and the exit_code returned as shown in Listing 4.

Listing 4

function do_download ($response_file)
{
$this->r_crypted = $response_file;
$curl_opt="-o {$this->path_files}."/".
{$this->r_crypted}
-u {$this->username}:{$this->password}
{$this->host}."/".{$this->path_download}."/".
{$this->r_crypted}";
$curl_cmd=$this->path_curl." ".$curl_opt;
exec ($curl_cmd,$return,$exit_code);
return $exit_code;
}

Table 3 explains the cURL option used which is new here. When the encrypted file is located in our local file system (we should remove all files after processing for security reasons), we can decrypt it. Our remote server did the encrypting the same way we did it: it used our public key.


Table 3: cURL download option

curl -o file file download target (name and position)

The decrypting is done with our private key together with the password. These credentials are stored in a file on the server. This file is of course not in the document root and should not be readable by every person. The private key together with the password in the wrong hands is the worst case!

Listing 5

function do_gpg_decrypt($r_crypted)
{
$this->response_file =$r_crypted;
$this->final_file=
str_replace (".pgp", "", $this->response_file);
$gpg_opt="--no-secmem-warning
--no-tty --batch --yes
--passphrase-fd 0
-o {$this->path_files}."/".{$this->final_file}
-d {$this->path_files}."/".{$this->r_crypted}";

$sec_key = $this->mantra_file};
$fd = fopen ($sec_key, "r");
$contents = fread ($fd, filesize ($sec_key));
fclose ($fd);
$this->mantra=trim($contents);
$echo_pass="echo $this->mantra|";
$gpg_cmd={$echo_pass}.
{$this->path_gpg}." ".
{$gpg_opt};
exec($gpg_cmd,$return,$exit_code);
return $exit_code;
}

As shown in Listing 5, the decrypting method of our service class is called with the source file name. Firstly, the parameter is shifted to a member variable. Next, the target file name is prepared: it has the same name as the source file but does not have the extension pgp. This is, of course, no core feature and could be adjusted to individual needs. After that the options are set for running GnuPG. Please look at Table 4 for details about the options used here.


Table 4: GnuPG decryption options

gpg --passphrase-fd passphrase will be read from stdin
gpg -o file output to target file
gpg -d file decrypt file


For decrypting the file the secret key is needed which is stored in a file. The file name is stored in the variable $sec_key. The file is read and the content saved in the member variable mantra. Now we have to prepare the GnuPG command by piping two shell commands: the echo of our passphrase is the input for the following GnuPG call with its options. The rest could be arranged as explained above: you create a shell script which calls the response broker and add it to crontab.

Conclusion
PHP is not only a form interpreter. It is perfect for use in the presentation layer, but we can also rely on PHP in the middle tier. Applications can be written which contain batch modules and are independent of any web browser. If full-fledged PHP does not satisfy your needs directly, you may also interface external modules straightforwardly. This solution can be easily to suit your needs. This article has hopefully shown you some new ways to achieve this goal.

Michael has been coding with languages such as Cobol, C++ and databases for about 15 years. Recently he has been busy with server side scripting on multiple platforms. Now he is involved in state-of-the-art enterprise application development. His e-mail is michael@huettermann.net.

Links and Literature

Software & Support Verlag - Global Alliance Program!







-- Advertisement --
Kelkoo price comparison in Germany
- Mobiles
- Furniture
- Notebooks
- Hotels
- Flights
- Digital cameras
Software & Support Verlag GmbH