http://www.developer.com/

Back to article

Implementing an Anti-Virus File Scan in JEE Applications


November 15, 2007

This article will discuss one of the ways to implement antivirus file scanning in Java, particular in the JEE applications. Viruses, Trojan Horses, and different malware and spyware are a real problem for current computer environments, and especially for the Windows operating system. If you are designing any application in Java that has a requirement to be able to upload external files, you have a potential security risk. By uploading, I mean any way to get the external file inside of the corporate firewall be it via HTTP protocol or any other means. It is quite common to have this type of requirement in an enterprise application and with Java being one of the most popular web development platforms, it is unfortunate that this type of gaping security risk is quite often overlooked.

Java's Development Kit (JDK) does not have any means to do the antivirus scan right out of the box. This is primarily because Java is a programming language, and does not have any virus scanning packages. Furthermore, anti-virus software is not Sun's area of expertise or business model. Developing this type of software (or Java package), and more importantly maintaining it, would be a huge task for Sun. Mainly because viruses are constantly evolving and keeping virus definitions up-to-date is a daunting task. Large companies such as McAffee, Symantec, or Zone Labs develop virus detecting and combating products and spend a lot of resources to maintain them.

Application Environment

To implement a virus file scan in Java, a third-party package needs to be used. For the purposes of this article, I will use Symantec Scan Engine (SSE) package, which comes with Java APIs. This package is an application that serves as a TCP/IP server and has a programming interface and enables Java applications to incorporate support for content scanning technologies. For this article, I used Symantec Scan Engine 5.1, which is available as a Unix or Windows install.

If you are using an anti-virus package from the different vendor, you will need to investigate what kind of APIs are available; however, the general approach should be similar. Also, note that my implementation can be used with JEE technology and any modern MVC framework such as Struts or Spring.

The architecture is as follows: A server machine needs to have SSE running at all times. This can be the same machine that hosts your Application Server, but in an enterprise environment this should be a different machine. The Default Port needs to be open through the firewall to allow communication with the scan engine. All JEE applications that need to do file scanning can talk to the SSE server machine through a default port. Also, multiple applications running on different application servers can re-use the same scanning server. For more information, you should refer to the Symantec Scan Engine (SSE) Installation Guide, available on the Symantec web site.

When an external file that needs to be scanned is sent to the SSE via its programming interface (Java APIs using the default port), before any other operation on the file is performed, the SSE returns a result code. For instance, a file is uploaded by an external user into the web email type application as an attachment; then, the SSE API is invoked by the application and the return code of pass or fail determines the outcome of the upload and whether that email can actually be sent. If you have an account on Yahoo mail, you probably have seen that Yahoo is using Norton Antivirus to scan all attachments, although no Java.



Click here for a larger image.

Figure 1: Screen shot from Yahoo

For details on the Scan Engine Server Installationm please see the Symantec Scan Engine (SSE) Implementation Guide from Symantec.

Here are some key things to remember about SSE:

  • Java 2 SE Runtime (JRE) 5.0 Update 6.0 or later must be installed on the server before the SSE installation is done.
  • After installation, verify that the Symantec Scan Engine daemon is running. At the Unix command prompt (if it's a Unix install), type the following command:
  • ps –ea | grep sym.
    A list of processes similar to the following should appear:
    • 5358 ? 0:00 symscan
    • 5359 ? 0:00 symscan
    If nothing is displayed the SSE process did not start.

    If the SSE process did not start, type the following command to restart SSE:
    /etc/init.d/symscan restart
  • Keeping the virus definition up to date is the most important task and if new updates are not installed, the whole scan becomes ineffective. Symantec automatically downloads the most current file definitions through LiveUpdate. Please make sure that firewall rules are in place to allow the host server to connect to the Symantec update service.

Project Setup

For the purposes of this article, I included a wrapper for the Symantec SSE APIs, av.jar, which has Symantec Java APIs and serves as a client to the SSE server and takes care of all communications with the server. Please refer to the download source section. The av.jar should be included in the Java CLASSPATH to work with the SSE. This jar contains a class called AVClient that takes care of actually sending the files to SSE as byte arrays and returning the result.

In my project setting, I added three variables to be accessed via the System.getProperty mechanism. For example:

AV_SERVER_HOST=192.168.1.150
AV_SERVER_PORT=1344
AV_SERVER_MODE=SCAN

The AV_SERVER_HOST is the host name or IP of the machine where Scan Engine is installed.

The AV_SERVER_PORT is the port where Scan Engine listens for incoming files.

The AV_SERVER_MODE is the scan mode which can be:

  • NOSCAN: No scanning will be done (any keyword that does not start with "SCAN" will result in ignoring the call to the Scan Engine and no files will be transferred for scanning).
  • SCAN: Files or the byte stream will be scanned, but the scan engine will not try to repair infections.
  • SCANREPAIR: Files will be scanned, the scan engine will try to repair infections, but nothing else will be done.
  • SCANREPAIRDELETE: Files will be scanned, the scan engine will try to repair infections, and irreparable files will be deleted.
Note: For the file stream (byte array) scanning, the only meaning full values are "SCAN" and "NOSCAN".

Using the SSE Scanning Java APIs

In any class where scan is required, call the scanning API provided in the AVClient object located in the av.jar. The AVClient object will establish connection to the Scan Engine server and has the following APIs:

Figure 2: The significant APIs for the communication with to the Scan Engine Server.

If scanning a file on the file system, in SCAN only mode, use the call that accepts filename only.

If scanning a file on the file system, with SCANREPAIR or SCANREPAIRDELETE, use the call that accepts input and output file names.

If scanning an in-memory file (byte array), use the call accepting byte array.

For example:

import com.av.*;

Initialize setup parameters:

static String avMode =
   (System.getProperty("AV_SERVER_MODE") != null)
   ? (String) System.getProperty("AV_SERVER_MODE") : "NOSCAN";

static boolean scan = avMode.startsWith("SCAN");

static String avServer =
   (String) System.getProperty("AV_SERVER_HOST");

static int avPort =
   Integer.parseInt( (String) System.getProperty("AV_SERVER_PORT"));

Scan check example for an in-memory file byte array:

public void scanFile(byte[] fileBytes, String fileName)
   throws IOException, Exception {

   if (scan) {
      AVClient avc = new AVClient(avServer, avPort, avMode);
      if (avc.scanfile(fileName, fileBytes) == -1) {
         throw new VirusException("WARNING: A virus was detected in
            your attachment: " + fileName + "<br>Please scan
            your system with the latest antivirus software with
            updated virus definitions and try again.");
      }
   }
}

Note that if you are using this code inside of the MVC handler, you can throw a custom VirusException and check for it in the calling method and perform any necessary cleanup. I have included the class in the AV Jar as well.

For example:

catch (Exception ex) {
   logger.error(ex);
   if (ex instanceof VirusException) {
      // do something here
   }
   else {
      // there was some other error – handle it
   }
}

For more details on the Scan Engine Client API, please see Symantec Scan Engine Software Developers Guide.

SSE Response Codes and Notification Options

Your Java code should have hooks to auto generate alerts or emails to the system support people in the event of failures to connect to the SSE, invalid, or has expired SSE license, and other events. To generate such alerts, when incorporating the AV client code in your application, you may need to use javamail or another email package to enable this functionality. Please look at the connect (String host) API in the AVClient object and the helper class AVRespond method request(String server_response).

Here is the list of all available codes.

  • private String one = "ICAP/1.0 100";
  • private String two = "ICAP/1.0 200";
  • private String three = "ICAP/1.0 201";
  • private String four = "ICAP/1.0 204";
  • private String five = "ICAP/1.0 400";
  • private String six = "ICAP/1.0 403";
  • private String seven = "ICAP/1.0 404";
  • private String eight = "ICAP/1.0 405";
  • private String nine = "ICAP/1.0 408";
  • private String ten = "ICAP/1.0 500";
  • private String eleven = "ICAP/1.0 503";
  • private String twelve = "ICAP/1.0 505";
  • private String thirteen = "ICAP/1.0 533";
  • private String fourteen = "ICAP/1.0 539";
  • private String fifteen = "ICAP/1.0 551";
  • private String sixteen = "ICAP/1.0 558";

I added a new response string from the SSE server—ICAP/1.0 558—to designate expired license. The main ones you should care about are: two and three which indicate a virus, and four which means the file is clean.

Please see the Symantec Scan Engine Software Developers Guide (page 30, Table 3-4 Status codes) for a compete list of the definitions of the response codes.

Example code to send email to the administrator if license in invalid:

if (server_response.equals(fourteen) ||
   server_response.equals(sixteen)) {
   // license is bad
   // if you want to let files through without actually scanning
   the_return = "clean";
   // send email – this must be working if option is "clean"
   String body = "Note the license for Symantec Scan Engine is
                 invalid or has expired!\n\rPlease see - class
                 AVRespond for more info.\n\rThis email is sent
                 every time a client uploads an un-scanned file";
   try {
      sendMail("symantec_scan_engine@mycompany.com",
               "YOU@mycompany.com", "Invalid or expired license for
               Symantec Scan Engine", body);

   }
   catch (Exception ex) {
      logger.error("AV client could not send email notification\nMake
                    sure properties has \"mail.smtp.host\" entry in
                    it \n " + ex);
   }
}

Download the Source Code

Conclusion

In this article, I have discussed how to do anti-virus file scanning from JEE applications. Surprisingly, not many developers know about this mechanism, even though leaving external files un-scanned can be a potential security risk. The scanning implementations currently available on the market are proprietary to the third-party vendors that provide custom Java APIs. For Java developers, it would be better for Sun to develop one standard protocol for the scanning APIs and enforce it among the vendors, similarly as it did with the Java Messaging Service (JMS) protocol. Even though scanning is not as large as the JMS is in the enterprise world, it's still and important aspect for some enterprise applications.

References

About the Author

Vlad Kofman works on enterprise-scale projects for the major Wall Street firms. He has also worked on defense contracts for the U.S. government. His main interests are object-oriented programming methodologies, UI, and design patterns.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date