summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG3
-rw-r--r--include/urweb.h1
-rw-r--r--lib/js/urweb.js21
-rw-r--r--src/c/urweb.c35
-rw-r--r--src/cjr_print.sml7
-rw-r--r--src/jscomp.sml12
-rw-r--r--src/mono.sml8
-rw-r--r--src/mono_print.sml10
-rw-r--r--src/mono_reduce.sml2
-rw-r--r--src/mono_util.sml4
-rw-r--r--src/monoize.sml6
-rw-r--r--tests/cookieJsec.ur27
-rw-r--r--tests/cookieJsec.urp5
-rw-r--r--tests/cookieJsec.urs1
14 files changed, 110 insertions, 32 deletions
diff --git a/CHANGELOG b/CHANGELOG
index ee860622..d57c52fc 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -5,6 +5,9 @@ Next
- Reimplement constructor class resolution to be more general and Prolog-like
- SQL table constraints
- URLs, with configurable gatekeeper function Basis.bless
+- Client-side error handling callbacks
+- CSS
+- Signing cookie values cryptographically to thwart cross site request forgery
========
20090405
diff --git a/include/urweb.h b/include/urweb.h
index e20533a0..99ae3e69 100644
--- a/include/urweb.h
+++ b/include/urweb.h
@@ -55,6 +55,7 @@ const char *uw_Basis_get_script(uw_context, uw_unit);
uw_Basis_string uw_Basis_maybe_onload(uw_context, uw_Basis_string);
void uw_set_needs_push(uw_context, int);
+void uw_set_needs_sig(uw_context, int);
char *uw_Basis_htmlifyInt(uw_context, uw_Basis_int);
char *uw_Basis_htmlifyFloat(uw_context, uw_Basis_float);
diff --git a/lib/js/urweb.js b/lib/js/urweb.js
index 877ed77f..a29914b9 100644
--- a/lib/js/urweb.js
+++ b/lib/js/urweb.js
@@ -353,7 +353,9 @@ function getXHR(uri)
}
}
-function requestUri(xhr, uri) {
+var sig = null;
+
+function requestUri(xhr, uri, needsSig) {
xhr.open("GET", uri, true);
if (client_id != null) {
@@ -361,10 +363,17 @@ function requestUri(xhr, uri) {
xhr.setRequestHeader("UrWeb-Pass", client_pass.toString());
}
+ if (needsSig) {
+ if (sig == null)
+ whine("Missing cookie signature!");
+
+ xhr.setRequestHeader("UrWeb-Sig", sig);
+ }
+
xhr.send(null);
}
-function rc(uri, parse, k) {
+function rc(uri, parse, k, needsSig) {
uri = flattenLocal(uri);
var xhr = getXHR();
@@ -389,7 +398,7 @@ function rc(uri, parse, k) {
}
};
- requestUri(xhr, uri);
+ requestUri(xhr, uri, needsSig);
}
function path_join(s1, s2) {
@@ -438,7 +447,7 @@ function listener() {
var connect = function () {
xhr.onreadystatechange = orsc;
tid = window.setTimeout(onTimeout, timeout * 500);
- requestUri(xhr, uri);
+ requestUri(xhr, uri, false);
}
orsc = function() {
@@ -490,8 +499,8 @@ function listener() {
}
else {
try {
- servError("Error querying remote server for messages: " + xhr.status);
- } catch (e) { servError("Error querying remote server for messages"); }
+ servErr("Error querying remote server for messages: " + xhr.status);
+ } catch (e) { servErr("Error querying remote server for messages"); }
}
}
};
diff --git a/src/c/urweb.c b/src/c/urweb.c
index bd42352f..6266e12d 100644
--- a/src/c/urweb.c
+++ b/src/c/urweb.c
@@ -300,7 +300,7 @@ struct uw_context {
const char *script_header, *url_prefix;
- int needs_push;
+ int needs_push, needs_sig;
size_t n_deltas, used_deltas;
delta *deltas;
@@ -336,6 +336,7 @@ uw_context uw_init() {
ctx->script_header = "";
ctx->url_prefix = "/";
ctx->needs_push = 0;
+ ctx->needs_sig = 0;
ctx->error_message[0] = 0;
@@ -589,6 +590,10 @@ void uw_set_needs_push(uw_context ctx, int n) {
ctx->needs_push = n;
}
+void uw_set_needs_sig(uw_context ctx, int n) {
+ ctx->needs_sig = n;
+}
+
static void buf_check_ctx(uw_context ctx, buf *b, size_t extra, const char *desc) {
if (b->back - b->front < extra) {
@@ -717,16 +722,30 @@ uw_Basis_string uw_Basis_maybe_onload(uw_context ctx, uw_Basis_string s) {
}
}
+extern uw_Basis_string uw_cookie_sig(uw_context);
+
const char *uw_Basis_get_settings(uw_context ctx, uw_unit u) {
- if (ctx->client == NULL)
- return "";
- else {
- char *r = uw_malloc(ctx, 59 + 3 * INTS_MAX + strlen(ctx->url_prefix));
- sprintf(r, "client_id=%u;client_pass=%d;url_prefix=\"%s\";timeout=%d;listener();",
+ if (ctx->client == NULL) {
+ if (ctx->needs_sig) {
+ char *sig = uw_cookie_sig(ctx);
+ char *r = uw_malloc(ctx, strlen(sig) + 8);
+ sprintf(r, "sig=\"%s\";", sig);
+ return r;
+ }
+ else
+ return "";
+ } else {
+ char *sig = ctx->needs_sig ? uw_cookie_sig(ctx) : "";
+ char *r = uw_malloc(ctx, 59 + 3 * INTS_MAX + strlen(ctx->url_prefix)
+ + (ctx->needs_sig ? strlen(sig) + 7 : 0));
+ sprintf(r, "client_id=%u;client_pass=%d;url_prefix=\"%s\";timeout=%d;%s%s%slistener();",
ctx->client->id,
ctx->client->pass,
ctx->url_prefix,
- ctx->timeout);
+ ctx->timeout,
+ ctx->needs_sig ? "sig=\"" : "",
+ sig,
+ ctx->needs_sig ? "\";" : "");
return r;
}
}
@@ -1998,8 +2017,6 @@ uw_Basis_string uw_Basis_makeSigString(uw_context ctx, uw_Basis_string sig) {
return r;
}
-extern uw_Basis_string uw_cookie_sig(uw_context);
-
uw_Basis_string uw_Basis_sigString(uw_context ctx, uw_unit u) {
return uw_cookie_sig(ctx);
}
diff --git a/src/cjr_print.sml b/src/cjr_print.sml
index a47bb587..69332b49 100644
--- a/src/cjr_print.sml
+++ b/src/cjr_print.sml
@@ -2497,6 +2497,13 @@ fun p_file env (ds, ps) =
string (!Monoize.urlPrefix),
string "\");",
newline]),
+ string "uw_set_needs_sig(ctx, ",
+ string (if couldWrite ek then
+ "1"
+ else
+ "0"),
+ string ");",
+ newline,
string "uw_login(ctx);",
newline,
box [string "{",
diff --git a/src/jscomp.sml b/src/jscomp.sml
index f839a67d..e6da3d4b 100644
--- a/src/jscomp.sml
+++ b/src/jscomp.sml
@@ -113,7 +113,7 @@ fun varDepth (e, _) =
| ESignalReturn e => varDepth e
| ESignalBind (e1, e2) => Int.max (varDepth e1, varDepth e2)
| ESignalSource e => varDepth e
- | EServerCall (e, ek, _) => Int.max (varDepth e, varDepth ek)
+ | EServerCall (e, ek, _, _) => Int.max (varDepth e, varDepth ek)
| ERecv (e, ek, _) => Int.max (varDepth e, varDepth ek)
| ESleep (e, ek) => Int.max (varDepth e, varDepth ek)
@@ -156,7 +156,7 @@ fun closedUpto d =
| ESignalReturn e => cu inner e
| ESignalBind (e1, e2) => cu inner e1 andalso cu inner e2
| ESignalSource e => cu inner e
- | EServerCall (e, ek, _) => cu inner e andalso cu inner ek
+ | EServerCall (e, ek, _, _) => cu inner e andalso cu inner ek
| ERecv (e, ek, _) => cu inner e andalso cu inner ek
| ESleep (e, ek) => cu inner e andalso cu inner ek
in
@@ -956,7 +956,7 @@ fun process file =
st)
end
- | EServerCall (e, ek, t) =>
+ | EServerCall (e, ek, t, eff) =>
let
val (e, st) = jsE inner (e, st)
val (ek, st) = jsE inner (ek, st)
@@ -967,7 +967,11 @@ fun process file =
str ("), function(s){var t=s.split(\"/\");var i=0;return "
^ unurl ^ "},"),
ek,
- str ")"],
+ str (","
+ ^ (case eff of
+ ReadCookieWrite => "true"
+ | _ => "false")
+ ^ ")")],
st)
end
diff --git a/src/mono.sml b/src/mono.sml
index dedb41ea..94314774 100644
--- a/src/mono.sml
+++ b/src/mono.sml
@@ -62,6 +62,9 @@ datatype javascript_mode =
| Script
| Source of typ
+datatype effect = datatype Export.effect
+datatype export_kind = datatype Export.export_kind
+
datatype exp' =
EPrim of Prim.t
| ERel of int
@@ -109,15 +112,12 @@ datatype exp' =
| ESignalBind of exp * exp
| ESignalSource of exp
- | EServerCall of exp * exp * typ
+ | EServerCall of exp * exp * typ * effect
| ERecv of exp * exp * typ
| ESleep of exp * exp
withtype exp = exp' located
-datatype effect = datatype Export.effect
-datatype export_kind = datatype Export.export_kind
-
datatype decl' =
DDatatype of string * int * (string * int * typ option) list
| DVal of string * int * typ * exp * string
diff --git a/src/mono_print.sml b/src/mono_print.sml
index 9e819e5f..b01442e8 100644
--- a/src/mono_print.sml
+++ b/src/mono_print.sml
@@ -308,11 +308,11 @@ fun p_exp' par env (e, _) =
p_exp env e,
string ")"]
- | EServerCall (n, e, _) => box [string "Server(",
- p_exp env n,
- string ")[",
- p_exp env e,
- string "]"]
+ | EServerCall (n, e, _, _) => box [string "Server(",
+ p_exp env n,
+ string ")[",
+ p_exp env e,
+ string "]"]
| ERecv (n, e, _) => box [string "Recv(",
p_exp env n,
string ")[",
diff --git a/src/mono_reduce.sml b/src/mono_reduce.sml
index 4c337e14..c124a7b4 100644
--- a/src/mono_reduce.sml
+++ b/src/mono_reduce.sml
@@ -371,7 +371,7 @@ fun reduce file =
| ESignalBind (e1, e2) => summarize d e1 @ summarize d e2
| ESignalSource e => summarize d e
- | EServerCall (e, ek, _) => summarize d e @ summarize d ek @ [Unsure]
+ | EServerCall (e, ek, _, _) => summarize d e @ summarize d ek @ [Unsure]
| ERecv (e, ek, _) => summarize d e @ summarize d ek @ [Unsure]
| ESleep (e, ek) => summarize d e @ summarize d ek @ [Unsure]
in
diff --git a/src/mono_util.sml b/src/mono_util.sml
index c7309df4..017b86ca 100644
--- a/src/mono_util.sml
+++ b/src/mono_util.sml
@@ -354,14 +354,14 @@ fun mapfoldB {typ = fc, exp = fe, bind} =
fn e' =>
(ESignalSource e', loc))
- | EServerCall (s, ek, t) =>
+ | EServerCall (s, ek, t, eff) =>
S.bind2 (mfe ctx s,
fn s' =>
S.bind2 (mfe ctx ek,
fn ek' =>
S.map2 (mft t,
fn t' =>
- (EServerCall (s', ek', t'), loc))))
+ (EServerCall (s', ek', t', eff), loc))))
| ERecv (s, ek, t) =>
S.bind2 (mfe ctx s,
fn s' =>
diff --git a/src/monoize.sml b/src/monoize.sml
index 5a164831..62a46277 100644
--- a/src/monoize.sml
+++ b/src/monoize.sml
@@ -2668,7 +2668,11 @@ fun monoExp (env, st, fm) (all as (e, loc)) =
(L'.ERel 0, loc)), loc),
(L'.ERecord [], loc)), loc)), loc)), loc)
val ek = (L'.EApp (ekf, ek), loc)
- val e = (L'.EServerCall (call, ek, t), loc)
+ val eff = if IS.member (!readCookie, n) then
+ L'.ReadCookieWrite
+ else
+ L'.ReadOnly
+ val e = (L'.EServerCall (call, ek, t, eff), loc)
val e = liftExpInExp 0 e
val unit = (L'.TRecord [], loc)
val e = (L'.EAbs ("_", unit, unit, e), loc)
diff --git a/tests/cookieJsec.ur b/tests/cookieJsec.ur
new file mode 100644
index 00000000..46668cf8
--- /dev/null
+++ b/tests/cookieJsec.ur
@@ -0,0 +1,27 @@
+table t : {Id : int}
+
+cookie c : int
+
+fun setter r =
+ setCookie c (readError r.Id);
+ return <xml>Done</xml>
+
+fun writer () =
+ ido <- getCookie c;
+ case ido of
+ None => error <xml>No cookie</xml>
+ | Some id => dml (INSERT INTO t (Id) VALUES ({[id]}))
+
+fun preWriter () = return <xml><body onload={onConnectFail (alert "RPC error")}>
+ <button onclick={writer ()} value="Write to database"/>
+
+ <a link={main ()}>Back</a>
+</body></xml>
+
+and main () = return <xml><body>
+ <form>
+ <textbox{#Id}/> <submit value="Get cookie" action={setter}/>
+ </form>
+
+ <form><submit action={preWriter} value="Prepare to write to database"/></form>
+</body></xml>
diff --git a/tests/cookieJsec.urp b/tests/cookieJsec.urp
new file mode 100644
index 00000000..fc5044e5
--- /dev/null
+++ b/tests/cookieJsec.urp
@@ -0,0 +1,5 @@
+debug
+database dbname=cookiejsec
+sql cookieJsec.sql
+
+cookieJsec
diff --git a/tests/cookieJsec.urs b/tests/cookieJsec.urs
new file mode 100644
index 00000000..6ac44e0b
--- /dev/null
+++ b/tests/cookieJsec.urs
@@ -0,0 +1 @@
+val main : unit -> transaction page