Compiling Web Scripts for Apache
description
Transcript of Compiling Web Scripts for Apache
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!