Compiling Web Scripts for Apache Jacob Matthews Luke Hoban Robby Findler Rice University

Preview:

Citation preview

Compiling Web Scripts for Apache

Jacob MatthewsLuke Hoban

Robby FindlerRice University

The Goal (Version 1)

Write a CGI program like this:

(let ((n (read-from-web “Type a number: ”))

(m (read-from-web “and another: ”)))

(display-to-web “The sum is: ” (+ n m)))

(let ((n (read-from-web “Type a number: ”))

(m (read-from-web “And another: ”)))

(display-to-web “The sum is: ” (+ n m)))

(let ((n (read-from-web “Type a number: ”))

(m (read-from-web “And another: ”)))

(display-to-web “The sum is: ” (+ n m)))

(let ((n (read-from-web “Type a number: ”))

(m (read-from-web “And another: ”)))

(display-to-web “The sum is: ” (+ n m)))

(let ((n (read-from-web “Type a number: ”))

(m (read-from-web “And another: ”)))

(display-to-web “The sum is: ” (+ n m)))

(let ((n (read-from-web “Type a number: ”))

(m (read-from-web “And another: ”)))

(display-to-web “The sum is: ” (+ n m)))

(let ((n (read-from-web “Type a number: ”))

(m (read-from-web “And another: ”)))

(display-to-web “The sum is: ” (+ n m)))

(let ((n (read-from-web “Type a number: ”))

(m (read-from-web “And another: ”)))

(display-to-web “The sum is: ” (+ n m)))

An Observation

(let ((n (read-from-web “Type a number: ”))

(m (read-from-web “and another: ”)))

(display-to-web “The sum is: ” (+ n m)))

An Observation

(let ((n (read-from-web “Type a number: ”))

(m (read-from-web “and another: ”)))

(display-to-web “The sum is: ” (+ n m)))

n = 4

If we have the red and the blue box, we can resume the program at that point

as many times as we want.

CPS FormThere’s already a standard transformation that does what we want!CPS conversion, lambda-lifting, and closure conversion give us red boxes at every point and arrows connecting them

(let ((n (read-from-web “Type a number: ”))

(m (read-from-web “and another: ”)))

(display-to-web “The sum is: ” (+ n m)))

Read-from-web(let ((n (read-from-web “Type a number: ”))

(m (read-from-web “and another: ”)))

(display-to-web “The sum is: ” (+ n m)))

n = 4

<INPUT TYPE=“hidden” NAME=“environment” VALUE=“n=4”>

<INPUT TYPE=“hidden” NAME=“What’s Left?” VALUE=“A B C”>

So what can we handle?

Creating, invoking, and passing closures

Creating and passing other basic values (cons, vector, string, etc)

Basic control constructs (if, let, cond, etc.)

call/cc

What can’t we handle?

variable assignment mutable values generative structures exception handling dynamic evaluation input/output ports threads integration with native code …

Plus …

… we have to be efficient!… we have to be secure!

Variable Assignment

(let ((sum 0)) (let loop () (let ((i (read-from-web "Type a number"))) (set! sum (+ sum i)) (loop))))

Variable Assignment

(let ((sum 0)) (let loop () (let ((i (read-from-web "Type a number"))) (set! sum (+ sum i)) (loop))))

sum = 9

Variable Assignment

(let ((sum 0)) (let loop () (let ((i (read-from-web "Type a number"))) (set! sum (+ sum i)) (loop))))

Variable Assignment

(let ((sum 0)) (let loop () (let ((i (read-from-web "Type a number"))) (set! sum (+ sum i)) (loop))))

Variable Assignment

(let ((sum 0)) (let loop () (let ((i (read-from-web "Type a number"))) (set! sum (+ sum i)) (loop))))

Variable Assignment

(let ((sum 0)) (let loop () (let ((i (read-from-web "Type a number"))) (set! sum (+ sum i)) (loop))))

Variable Assignment

(let ((sum 0)) (let loop () (let ((i (read-from-web "Type a number"))) (set! sum (+ sum i)) (loop))))

sum = 12

But then, the user hits the ‘Back’ button ...

Variable Assignment

(let ((sum 0)) (let loop () (let ((i (read-from-web "Type a number"))) (set! sum (+ sum i)) (loop))))

sum = 9

sum = 9, not 12!

Variable Assignment

(let ((sum 0)) (let loop () (let ((i (read-from-web "Type a number"))) (set! sum (+ sum i)) (loop))))

sum = 9

Variable Assignment

(let ((sum (box 0))) (let loop () (let ((i (read-from-web "Type a number"))) (set-box! sum (+ sum i)) (loop))))

a = 9

sum = [a]

Variable Assignment

(let ((sum (box 0))) (let loop () (let ((i (read-from-web "Type a number"))) (set-box! sum (+ sum i)) (loop))))

a = 9

Variable Assignment

(let ((sum (box 0))) (let loop () (let ((i (read-from-web "Type a number"))) (set-box! sum (+ sum i)) (loop))))

a = 12

Variable Assignment

(let ((sum (box 0))) (let loop () (let ((i (read-from-web "Type a number"))) (set-box! sum (+ sum i)) (loop))))

a = 12

Variable Assignment

(let ((sum (box 0))) (let loop () (let ((i (read-from-web "Type a number"))) (set-box! sum (+ sum i)) (loop))))

a = 12

Variable Assignment

(let ((sum 0)) (let loop () (let ((i (read-from-web "Type a number"))) (set! sum (+ sum i)) (loop))))

a = 12

If the user hits the back button now, everything still works!

sum = [a]

So where does the purple box go?

We need some place that’s associated with a particular user, but not a particular web page

Browser cookies might work

Mutable Values

H do we handle other mutable values like cons cells, hash

tables, and vectors?

Mutable Values(let ([lst '(#f)]) (let loop () (let ((r (read-from-web "Type a value"))) (append! lst (list r)) (loop))))

Mutable Values(let ([lst '(#f)]) (let loop () (let ((r (read-from-web "Type a value"))) (append! lst (list r)) (loop))))

lst = (cons #f ‘())

Same problem, different primitive

Mutable Values(let ([lst '(#f)]) (let loop () (let ((r (read-from-web "Type a value"))) (append! lst (list r)) (loop))))

lst = (cons [a] [b])

a = #fb = ‘()

Mutable Values

But if we add to the purple box every time we make a list, we’ll have problems:

Even lists that never need to be saved get added

The purple box is never garbage-collected There are too many constructors anyway!

Mutable Values

So instead, we get lazy! Only add or update the purple box when we

actually call read-from-web

Mutable Values(let ([lst '(#f)]) (let loop () (let ((r (read-from-web "Type a value"))) (append! lst (list r)) (loop))))

lst = (cons #f ‘())

Mutable Values(let ([lst '(#f)]) (let loop () (let ((r (read-from-web "Type a value"))) (append! lst (list r)) (loop))))

lst = (cons [a] [b])

a = #fb = ‘()

In fact, we add all new mutable values reachable from the

environment

But Won’t the Store Still Be Too Big?

Yes!Even worse: the store never shrinks!Cookies aren’t feasibleFor now, put (some of) the store on

the server

Security

As it stands, attackers can make up anything as the blue and

purple information!

Security

(if (valid? (read-from-web "Password:”)) (display-secret-page) (display-error-page))

Security

(if (valid? (read-from-web "Password:”)) (display-secret-page) (display-error-page))

The attacker can’t choose the red boxes, but can choose where the

arrows point …

Security

(if (valid? (read-from-web "Password:”)) (display-secret-page) (display-error-page))

… And that’s bad enough!

Security

A solution:Encrypt the contents of the hidden

fields and the cookies Keep a secret key only on the

server

Efficiency

We’ve got too many red boxes!They make the program largerMore arrows means larger values

in the hidden fields and longer page download times

A Solution

“Full” CPS is too much - we don’t need all the red boxes!

Efficiency

(let ((n (read-from-web “Type a number: ”))

(m (read-from-web “and another: ”)))

(display-to-web “The sum is: ” (+ n m)))

The program never reaches (+ n m) without going directly on

to display-to-web …

Efficiency

(let ((n (read-from-web “Type a number: ”))

(m (read-from-web “and another: ”)))

(display-to-web “The sum is: ” (+ n m)))

… so we can combine the two boxes!

Security

(if (valid? (read-from-web "Password:”)) (display-secret-page) (display-error-page))

This also helps with security:

No guarantees

The attacker can’t name the display-secret-page box anymore

Conclusions

Even in a real language, we can compile direct-style programs into CGI style so they can run on Apache

It’s important to try out theories by scaling them to real-sized applications

Thank You!