Saturday 24 September 2011

JNLP Application Security Assessment - Setting the scene

Hello all,

Some time ago I came across assessing a JNLP application. Since my initial search for some guidance on the web turned no valuable results I thought I would use a few blog posts to document my  process of performing the aforementioned assessment and provide some guidance to people in search of a similar document in the future.

The list below will turn to actual blog posts as the process moves along.

The rough structure of the posts is the following:

  • JNLP Application Security Assessment - Part 1 : Analysis of a typical JNLP file

  • JNLP Application Security Assessment - Part 2 : Runtime Mapping of a JNLP Application

  • JNLP Application Security Assessment - Part 3 : Application decomposition / Static analysis

  • JNLP Application Security Assessment - Part 4 : Dynamic analysis


To illustrate the various points I will use a sample application that consists of the following three files:

Auth2Service.java: This file handles all Authentication and Authorisation concepts of our application. In normal complex applications this component will most likely contain calls to the central component of the application, this may be a database, a corba server, a SOA endpoint, etc.

[sourcecode lang="java"]
package com.jnlptest;

import java.util.*;

public class Auth2Service {
private Map<String, String> logins = new HashMap<String,String>();
private Map<String, String> roles = new HashMap<String,String>();

public Auth2Service()
{
logins.put("admin", "admpwd");
logins.put("user", "userpwd");

roles.put("admin", "administrator");
roles.put("user", "normaluser");
}

public boolean authenticate( String u , String p)
{
if (!logins.containsKey( u ))
{
return false;
}
else
{
if (logins.get( u ).equals(p))
{
return true;
}
else
{
return false;
}
}
}

public String getrole( String u )
{
if (!roles.containsKey( u ))
{
return null;
}
else
{
return (String)roles.get( u );
}
}
}
[/sourcecode]

JNLPFrame.java: This file is the JFRAME that provides the GUI for our application.

[sourcecode lang="java"]
package com.jnlptest;

import java.awt.*;
import javax.swing.*;

import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class JNLPFrame extends JFrame {

private JLabel ulabel = new JLabel();
private JTextField usernameTxt = new JTextField();
private JLabel plabel = new JLabel();
private JTextField passwordTxt = new JTextField();
private JButton button = new JButton("Login");
private JButton admbutton = new JButton("Admin Function");
private JButton userbutton = new JButton("User Function");
private Auth2Service a2s = new Auth2Service();

public JNLPFrame(String title)
{
super(title);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container content = getContentPane();
content.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();

ulabel.setText("Username:");

c.gridx = 0;
c.gridy = 0;
content.add(ulabel,c);

c.gridx = 1;
c.gridy = 0;
c.fill = GridBagConstraints.HORIZONTAL;
content.add(usernameTxt,c);

plabel.setText("Password:");
c.gridx = 0;
c.gridy = 1;
content.add(plabel,c);

c.gridx = 1;
c.gridy = 1;
c.fill = GridBagConstraints.HORIZONTAL;
content.add(passwordTxt,c);

ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
String un = usernameTxt.getText();
String pw = passwordTxt.getText();
if (authenticate(un , pw ))
{
buildGUI( un );
}
};
};
button.addActionListener(listener);

c.gridx = 1;
c.gridy = 2;
content.add(button, c);

ActionListener admlistener = new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
JOptionPane.showMessageDialog(null, "Administrative action");
};
};

admbutton.addActionListener(admlistener);
admbutton.setVisible(false);
c.gridx = 0;
c.gridy = 3;
content.add(admbutton, c);

ActionListener userlistener = new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
JOptionPane.showMessageDialog(null, "Normal user action");
};
};

userbutton.addActionListener(userlistener);
userbutton.setVisible(false);
c.gridx = 1;
c.gridy = 3;
content.add(userbutton, c);

pack();
setVisible(true);
}

private boolean authenticate(String u, String p)
{
return a2s.authenticate( u, p );
}

private void buildGUI(String u)
{
String role = a2s.getrole( u );
if (role.equals("normaluser"))
{
userbutton.setVisible(true);
pack();
}
else if (role.equals("administrator"))
{
userbutton.setVisible(true);
admbutton.setVisible(true);
pack();
}

}
}
[/sourcecode]

TestJnlp.java: This file is the application bootstrap. It contains the main() function that initialises the application GUI.

[sourcecode lang="java"]
package com.jnlptest;

public class TestJnlp {

public static void main(String args[]) {
JNLPFrame jf = new JNLPFrame("JNLP Test Application");
}
}
[/sourcecode]

TestJnlp.jnlp: This file is the JNLP definition. It it the main file served from the web server prior to any other action taking place.

[sourcecode lang="xml"]
<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0+" codebase="http://localhost:8080/" href="TestJnlp.jnlp">
<information>
<title>Jnlp Testing</title>
<vendor>ZQYVES</vendor>
<homepage href="http://localhost:8080/" />
<description>Testing of JNLP Application</description>
</information>
<security>
<all-permissions/>
</security>
<resources>
<j2se version="1.6+" java-vm-args="-esa -Xnoclassgc"/>
<jar href="logic.jar" />
<jar href="gui.jar" />
<property name="jnlp.versionEnabled" value="true"/>
</resources>
<application-desc main-class="com.jnlptest.TestJnlp" />
</jnlp>
[/sourcecode]
Note: I am not actually a java developer and the aforementioned application has been thrown together in a couple of hours to illustrate a point. Furthermore the aforementioned .jnlp file assumes that it will be served from localhost to localhost. Should you want to publish it and access is from two different locations you need to change any reference to localhost above to the IP address/hostname of the server hosting the application.

The aforementioned application has been packaged into two jar files, application.jar (that contains the GUI and bootstrap classes) and logic.jar that contains the authentication and authorisation handling class. The two jars where than signed with the following sequence of commands since the java settings will prohibit them from running otherwise. The first command creates the keystore that will be used in signing the jars and the next two actually performs the signing.
keytool.exe -genkey -alias zqyves -keystore jnlpappkeystore

jarsigner.exe -keystore jnlpappkeystore -storepass jnlpappkeystore -keypass jnlpappkeystore gui.jar zqyves
jarsigner.exe -keystore jnlpappkeystore -storepass jnlpappkeystore -keypass jnlpappkeystore logic.jar zqyves

Finally the three aforementioned files, the two jars and the jnlp where placed in the root of an apache server so as to be served. The following lines had to be added to the httpd.conf for the files to be served with the correct mime type.
	AddType application/x-java-jnlp-file .jnlp
AddType application/x-java-archive .jar
AddType application/x-java-archive-diff .jardiff

The application binaries and source code can be downloaded from the following locations:

Source code.

Binaries.

I hope this turns into a useful and interesting series of posts.

Best regards,

./Z

No comments:

Post a Comment

Note: only a member of this blog may post a comment.