Wednesday, June 18, 2008

HTTPS support in http.server, some notes on HTTP request/response parsing

Generic servers in Factor


The io.servers.connection vocabulary supersedes the io.server vocabulary, adding support for a connection count limit, SSL, and more configuration flags.

The best way to explain how it works is with an example:
USING: accessors io io.servers.connection ;

<threaded-server>
"hello-world" >>name
100 >>max-connections
1080 >>insecure
1090 >>secure
<secure-config>
"cert/server.pem" >>key-file
>>secure-config
[ "Hello, world." print ] >>handler
start-server

This is a server which sends "Hello, world." to the client, then closes the connection. It accepts standard TCP/IP connections on port 1080, and SSL connections on port 1090. For SSL, it identifies itself to the client with the certificate in certs/server.pem. We set a limit of 100 concurrent connections, and connections are logged to resource:logs/hello-world/*.log.

Here is another example:
USING: accessors io io.servers.connection io.launcher ;

<threaded-server>
"telnet" >>name
10666 >>insecure
[
<process>
"/bin/sh -i" >>command
input-stream get >>stdin
output-stream get >>stdout
+stdout+ >>stderr
run-process
] >>handler
start-server

Please do not run this on a machine facing the Internet; it starts a server on port 10666 which spawns a bash shell, with I/O redirected to the client connection. This example won't work on Windows, or with SSL connections, because those types of streams cannot be stored in the stdin and stdout slots of a process tuple yet; however that will be fixed eventually, and then code such as the above might serve as a prototype of a Factor re-implementation of inetd.

So that's io.server.connections for you: a lot of functionality wrapped up in a small package. Eduardo Cavazos is working on a DNS server in Factor right now, and at some point we're going to isolate some UDP/IP abstractions from his code which are not specific to DNS, and move those to io.servers.packet.

HTTPS support in http.server


Using io.servers.connection, I implemented https support in the HTTP server. This was pretty easy, now that all the components are in place. The main changes I made were to the authentication system in the web framework. It now redirects you to the SSL port when a page requiring login is requested, however this can be disabled (although I would not encourage this).

For web applications not using the authentication framework, there is another useful utility: any HTTP responder can be wrapped in a <secure-only responder; this upgrades the connection SSL if a non-SSL request is made.

To improve performance, I added session resumption support to io.sockets.secure, for both client and server connections; this can be disabled in the <secure-config> instance passed to with-secure-context. The SSL library still needs support for client authentication and it also needs to run on Windows; once those two items are done, it will pretty much be feature-complete. I might write bindings to the OpenSSL cyphers allowing them to be used stand-alone, too, however this is low-priority since I don't need that for anything right now.

Parsing HTTP requests and responses


I also replaced the hand-coded code for parsing HTTP requests and responses with new code that uses Parser Expression Grammars (PEGs). While the new code was not a lot shorter, it does have a more declarative flavor, and is also more correct.

During the course of this exercise, I learned how far modern web browsers deviate from the specifications, particularly in the area of cookies. Cookies are specified by a pair of RFCs, RFC2109 and RFC2965, however the latter seems to be almost universally ignored, and browser and server developers seem to treat the former as more of a set of ideas than a formal spec.

Mark Nottingham blogged about this a little under two years ago, and seemingly, not much has changed since then. There is also this bug in Firefox.

After I mentioned these difficulties in the #concatenative IRC channel, Zed Shaw joined the ensuing discussion and noted that the RFCs themselves are quite vague and seemingly contradict themselves.

The general design of cookies makes it easy to expose vulnerabilities such as this one in the Tomcat application server. I hope my code is secure but I will need to review it later in more detail to be sure.

No comments: