This site is the archived OWASP Foundation Wiki and is no longer accepting Account Requests.
To view the new OWASP Foundation website, please visit https://owasp.org

Category:OWASP Proxy

From OWASP
Revision as of 20:45, 5 January 2009 by RoganDawes (talk | contribs) (Update code explaining how to extend the library)

Jump to: navigation, search

Welcome to the OWASP Proxy

The OWASP Proxy aims to provide a high quality intercepting proxy library which can be used by developers who require this functionality in their own programs, rather than having to develop it all from scratch.

The library is developed in Java, making it most attractive to Java developers obviously, but also accessible to Python (Jython) and Ruby (JRuby) developers as well.

Overview

One of the priorities of this project is to allow developers to do whatever they choose, without enforcing RFC compliance. This is important for a security testing library, as often the most interesting behavior manifests outside the RFCs! Keep in mind that a lot of the safety nets that exist in libraries that enforce RFC compliance do not exist in this library, and that as the developer, you need to be prepared to deal with the consequences!

Another priority is to accurately deliver whatever is specified by the client, and similarly, to accurately reflect whatever is returned by the server, rather than coloured by the parsing and normalisation performed by the library.

Download

At the moment there is no packaged version of this library. Development is done in a git repository, located here.

Interested parties can download a snapshot of the code at any point using the snapshot link next to each revision, or clone the repository:

 $ git clone http://dawes.za.net/rogan/owasp-proxy/owasp-proxy.git/

Implementation details

In order to achieve byte for byte accuracy with what was sent by the client, and received from the server, OWASP Proxy does the bare minimum of message parsing. The basic storage of an HTTP message is as an array of byte (a byte for byte copy of what was read from the network), rather than parsed out into convenient pieces. The library does provide convenience methods for accessing interesting parts of the message, such as headers, content, etc, but the message itself is always stored as a large byte[].

The Request and Response objects that you may deal with also do not decode the message bodies for you. If the message was sent using chunked encoding, the message body will show the individual chunks that were sent. Of course, again, there are also classes which allow you to obtain the actual entity body, with appropriate decoding.

Future development

As mentioned above, one objective is correctness. By this I mean correctly handling whatever the major browsers send to it, and successfully retrieving whichever resource was requested. Failure to do so will be addressed as soon as possible.

Other than that, there is no intention to add major new features to the library above those required to fulfill its purpose as a Listener and a HTTP client implementation.

Extending the OWASP Proxy

The basic classes are Listener and SocksListener, which can be customised to add your own functionality. If you just create an instance of Listener (or SocksListener), it will accept connections on the specified port, and relay them to the requested destination.

   Listener l = new Listener(8008); // listens to localhost by default
   Listener sl = new SocksListener(1080);

or

   Listener l = new Listener(InetAddress.getByAddress(new byte[] { 127, 0,
                   0, 1 }), 8008);
   Listener sl = new SocksListener(InetAddress.getByAddress(new byte[] { 127, 0,
                   0, 1 }), 1080);

The first thing a developer might want to do is make some changes to the request or response. This is done by means of a ProxyMonitor. This is an abstract class that can be extended to provide the specific functionality required. I'll show it here as an interface, just to reduce verbosity.

   Response requestReceived(Request request)
       throws MessageFormatException;
   
   Response errorReadingRequest(Request request, Exception e)
       throws MessageFormatException;
   
   boolean responseHeaderReceived(Conversation conversation)
       throws MessageFormatException;
   
   void responseContentReceived(Conversation conversation, boolean streamed)
       throws MessageFormatException;
   
   Response errorFetchingResponseHeader(Request request, Exception e)
       throws MessageFormatException;
   
   Response errorFetchingResponseContent(Conversation conversation, Exception e)
       throws MessageFormatException;
   
   void wroteResponseToBrowser(Conversation conversation)
       throws MessageFormatException;
   
   void errorWritingResponseToBrowser(Conversation conversation,
       Exception e) throws MessageFormatException;

These methods are called at various stages of a request's lifecycle, if appropriate. Obviously error methods won't be called if there are no errors! Each method that returns a Response object can be customized to return a custom Response to the browser. e.g. overriding requestReceived(Request) can allow a developer to provide a browser-based interface to their program, by returning Response objects for specific requests, e.g. to a host called "proxy":

   public Response requestReceived(Request request)
       throws MessageFormatException {
       if (request.getHost().equals("proxy")) {
           return handleRequest(request);
       }
       return super.requestReceived(request);
   }

The library provides a LoggingProxyMonitor class, which simply logs the Requests and Responses that pass through. It also prints out any exceptions that may occur (not shown).

   class LoggingProxyMonitor extends ProxyMonitor {
   
       @Override
       public void wroteResponseToBrowser(Conversation conversation)
               throws MessageFormatException {
           try {
               int resp = conversation.getResponse().getMessage().length;
               long time = conversation.getResponseBodyTime()
                       - conversation.getRequestTime();
   
               Request request = conversation.getRequest();
               StringBuilder buff = new StringBuilder();
               buff.append(request.getMethod()).append(" ");
               buff.append(request.isSsl() ? "ssl " : "");
               buff.append(request.getHost()).append(":")
                       .append(request.getPort());
               buff.append(request.getResource()).append(" ");
               buff.append(conversation.getResponse().getStatus()).append(" - ")
                       .append(resp);
               buff.append(" bytes in ").append(time).append("(").append(
                       resp / (time * 1000));
               buff.append(" bps)");
               System.out.println(buff.toString());
           } catch (MessageFormatException mfe) {
           }
       }
   }

One thing that a developer might want to do is teach the proxy how to use an upstream proxy to reach the target. OWASP Proxy uses the standard Java ProxySelector mechanism for this:

   final Proxy upstream = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, port);
   
   final ProxySelector ps = new ProxySelector() {
   
       @Override
       public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
           System.err.println("Proxy connection failed! "
                   + ioe.getLocalizedMessage());
       }
   
       @Override
       public List<Proxy> select(URI uri) {
           return Arrays.asList(upstream);
       }
   };

Having created the ProxySelector, we need to make use of it. OWASP Proxy has its own Http Client implementation, that specifically makes NO unnecessary changes to the requests or responses. Some libraries try to make sure that the requests it sends are well-formed, for instance, but for a security tool, sometimes it is interesting to send malformed requests.

So, we can tell the Listener how to configure its HttpClient implementations, by providing a customised HttpClientFactory:

   HttpClientFactory hcf = new DefaultHttpClientFactory() {
   
       @Override
       public HttpClient createHttpClient() {
           HttpClient client = super.createHttpClient();
           client.setProxySelector(ps);
           return client;
       }
   };
   listener.setHttpClientFactory(hcf);

By default, OWASP Proxy does not intercept SSL connections (HTTP CONNECT verb), and if it encounters an SSL encrypted channel, it simply refuses to continue. Developers can enable SSL interception by providing a CertificateProvider implementation.

This is what the interface looks like:

   public interface CertificateProvider {
   
       SSLSocketFactory getSocketFactory(String host, int port) throws IOException;
   
   }

A simple implementation is provided in DefaultCertificateProvider that makes use of a single certificate for all connection attempts. A certificate location and passwords can be provided; alternatively, OWASP Proxy's default certificate can be used.

   listener.setCertificateProvider(new DefaultCertificateProvider());

Developers may wish to make use of something like the CyberVillains CA library to automatically generate certificates on the fly for each site visited.

Project Contributors

The OWASP Proxy project is run by Rogan Dawes of Corsaire Security. He can be contacted at rogan AT dawes.za.net

This category currently contains no pages or media.