Add referrer check responder, harden sessions against cross-site scripting

db4
Slava Pestov 2008-06-13 22:05:41 -05:00
parent a687b58226
commit 935d7d4321
12 changed files with 76 additions and 27 deletions

View File

@ -53,7 +53,7 @@ TUPLE: action rest authorize init display validate submit ;
] with-exit-continuation ; ] with-exit-continuation ;
: validation-failed ( -- * ) : validation-failed ( -- * )
request get method>> "POST" = [ f ] [ <400> ] if exit-with ; post-request? [ f ] [ <400> ] if exit-with ;
: (handle-post) ( action -- response ) : (handle-post) ( action -- response )
'[ '[
@ -70,12 +70,9 @@ TUPLE: action rest authorize init display validate submit ;
: revalidate-url-key "__u" ; : revalidate-url-key "__u" ;
: check-url ( url -- ? )
request get url>>
[ [ protocol>> ] [ host>> ] [ port>> ] tri 3array ] bi@ = ;
: revalidate-url ( -- url/f ) : revalidate-url ( -- url/f )
revalidate-url-key param dup [ >url dup check-url swap and ] when ; revalidate-url-key param
dup [ >url [ same-host? ] keep and ] when ;
: handle-post ( action -- response ) : handle-post ( action -- response )
'[ '[

View File

@ -5,12 +5,19 @@ furnace.cache
furnace.asides furnace.asides
furnace.flash furnace.flash
furnace.sessions furnace.sessions
furnace.referrer
furnace.db furnace.db
furnace.auth.providers ; furnace.auth.providers ;
IN: furnace.alloy IN: furnace.alloy
: <alloy> ( responder db params -- responder' ) : <alloy> ( responder db params -- responder' )
[ <asides> <flash-scopes> <sessions> ] 2dip <db-persistence> ; '[
<asides>
<flash-scopes>
<sessions>
, , <db-persistence>
<check-form-submissions>
] call ;
: state-classes { session flash-scope aside } ; inline : state-classes { session flash-scope aside } ; inline

View File

@ -51,7 +51,7 @@ ERROR: end-aside-in-get-error ;
dup [ dup session>> session get id>> = [ drop f ] unless ] when ; dup [ dup session>> session get id>> = [ drop f ] unless ] when ;
: end-aside* ( url id -- response ) : end-aside* ( url id -- response )
request get method>> "POST" = [ end-aside-in-get-error ] unless post-request? [ end-aside-in-get-error ] unless
aside get-state [ aside get-state [
dup method>> { dup method>> {
{ "GET" [ url>> <redirect> ] } { "GET" [ url>> <redirect> ] }

View File

@ -84,6 +84,17 @@ M: object modify-form drop ;
] } ] }
} case ; } case ;
: referrer ( -- referrer )
#! Typo is intentional, its in the HTTP spec!
"referer" request get header>> at >url ;
: user-agent ( -- user-agent )
"user-agent" request get header>> at "" or ;
: same-host? ( url -- ? )
request get url>>
[ [ protocol>> ] [ host>> ] [ port>> ] tri 3array ] bi@ = ;
SYMBOL: exit-continuation SYMBOL: exit-continuation
: exit-with ( value -- ) : exit-with ( value -- )

View File

@ -0,0 +1,16 @@
USING: accessors kernel
http.server http.server.filters http.server.responses
furnace ;
IN: furnace.referrer
TUPLE: referrer-check < filter-responder quot ;
C: <referrer-check> referrer-check
M: referrer-check call-responder*
referrer over quot>> call
[ call-next-method ]
[ 2drop 403 "Bad referrer" <trivial-response> ] if ;
: <check-form-submissions> ( responder -- responder' )
[ same-host? post-request? not or ] <referrer-check> ;

View File

@ -2,14 +2,14 @@
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: assocs kernel math.intervals math.parser namespaces USING: assocs kernel math.intervals math.parser namespaces
random accessors quotations hashtables sequences continuations random accessors quotations hashtables sequences continuations
fry calendar combinators destructors alarms fry calendar combinators destructors alarms io.server
db db.tuples db.types db db.tuples db.types
http http.server http.server.dispatchers http.server.filters http http.server http.server.dispatchers http.server.filters
html.elements html.elements
furnace furnace.cache ; furnace furnace.cache ;
IN: furnace.sessions IN: furnace.sessions
TUPLE: session < server-state uid namespace changed? ; TUPLE: session < server-state uid namespace user-agent client changed? ;
: <session> ( id -- session ) : <session> ( id -- session )
session new-server-state ; session new-server-state ;
@ -18,6 +18,8 @@ session "SESSIONS"
{ {
{ "uid" "UID" { VARCHAR 255 } } { "uid" "UID" { VARCHAR 255 } }
{ "namespace" "NAMESPACE" FACTOR-BLOB +not-null+ } { "namespace" "NAMESPACE" FACTOR-BLOB +not-null+ }
{ "user-agent" "USER_AGENT" TEXT +not-null+ }
{ "client" "CLIENT" TEXT +not-null+ }
} define-persistent } define-persistent
: get-session ( id -- session ) : get-session ( id -- session )
@ -31,10 +33,11 @@ M: dispatcher init-session* default>> init-session* ;
M: filter-responder init-session* responder>> init-session* ; M: filter-responder init-session* responder>> init-session* ;
TUPLE: sessions < filter-responder timeout domain ; TUPLE: sessions < server-state-manager domain verify? ;
: <sessions> ( responder -- responder' ) : <sessions> ( responder -- responder' )
sessions new-server-state-manager ; sessions new-server-state-manager
t >>verify? ;
: (session-changed) ( session -- ) : (session-changed) ( session -- )
t >>changed? drop ; t >>changed? drop ;
@ -66,9 +69,13 @@ TUPLE: sessions < filter-responder timeout domain ;
: touch-session ( session -- ) : touch-session ( session -- )
sessions get touch-state ; sessions get touch-state ;
: remote-host ( -- string ) remote-address get host>> ;
: empty-session ( -- session ) : empty-session ( -- session )
f <session> f <session>
H{ } clone >>namespace H{ } clone >>namespace
remote-host >>client
user-agent >>user-agent
dup touch-session ; dup touch-session ;
: begin-session ( -- session ) : begin-session ( -- session )
@ -107,8 +114,18 @@ M: session-saver dispose
{ "POST" [ post-session-id ] } { "POST" [ post-session-id ] }
} case ; } case ;
: verify-session ( session -- session )
sessions get verify?>> [
dup [
dup
[ client>> remote-host = ]
[ user-agent>> user-agent = ]
bi and [ drop f ] unless
] when
] when ;
: request-session ( -- session/f ) : request-session ( -- session/f )
request-session-id get-session ; request-session-id get-session verify-session ;
: <session-cookie> ( id -- cookie ) : <session-cookie> ( id -- cookie )
session-id-key <cookie> session-id-key <cookie>

View File

@ -269,7 +269,7 @@ SYMBOL: a
! Test flash scope ! Test flash scope
[ "xyz" ] [ [ "xyz" ] [
H{ { "a" "xyz" } { "__u" "http://localhost:1237/" } } "session-id" get session-id-key associate assoc-union H{ { "a" "xyz" } { "__u" "http://localhost:1237/" } } "session-id" get session-id-key associate assoc-union
"http://localhost:1237/" <post-request> "cookies" get >>cookies B http-request nip test-a "http://localhost:1237/" <post-request> "cookies" get >>cookies http-request nip test-a
] unit-test ] unit-test
[ 4 ] [ a get-global ] unit-test [ 4 ] [ a get-global ] unit-test

View File

@ -5,8 +5,6 @@ combinators arrays io.launcher io http.server.static http.server
http accessors sequences strings math.parser fry urls ; http accessors sequences strings math.parser fry urls ;
IN: http.server.cgi IN: http.server.cgi
: post? ( -- ? ) request get method>> "POST" = ;
: cgi-variables ( script-path -- assoc ) : cgi-variables ( script-path -- assoc )
#! This needs some work. #! This needs some work.
[ [
@ -34,7 +32,7 @@ IN: http.server.cgi
request get "user-agent" header "HTTP_USER_AGENT" set request get "user-agent" header "HTTP_USER_AGENT" set
request get "accept" header "HTTP_ACCEPT" set request get "accept" header "HTTP_ACCEPT" set
post? [ post-request? [
request get post-data>> raw>> request get post-data>> raw>>
[ "CONTENT_TYPE" set ] [ "CONTENT_TYPE" set ]
[ length number>string "CONTENT_LENGTH" set ] [ length number>string "CONTENT_LENGTH" set ]
@ -53,7 +51,7 @@ IN: http.server.cgi
"CGI output follows" >>message "CGI output follows" >>message
swap '[ swap '[
, output-stream get swap <cgi-process> <process-stream> [ , output-stream get swap <cgi-process> <process-stream> [
post? [ request get post-data>> raw>> write flush ] when post-request? [ request get post-data>> raw>> write flush ] when
input-stream get swap (stream-copy) input-stream get swap (stream-copy)
] with-stream ] with-stream
] >>body ; ] >>body ;

View File

@ -20,6 +20,8 @@ html.elements
html.streams ; html.streams ;
IN: http.server IN: http.server
: post-request? ( -- ? ) request get method>> "POST" = ;
SYMBOL: responder-nesting SYMBOL: responder-nesting
SYMBOL: main-responder SYMBOL: main-responder

View File

@ -135,6 +135,8 @@ PRIVATE>
GENERIC: >url ( obj -- url ) GENERIC: >url ( obj -- url )
M: f >url drop <url> ;
M: url >url ; M: url >url ;
M: string >url M: string >url

View File

@ -61,9 +61,7 @@ TUPLE: factor-website < dispatcher ;
: init-factor-website ( -- ) : init-factor-website ( -- )
"factorcode.org" 25 <inet> smtp-server set-global "factorcode.org" 25 <inet> smtp-server set-global
"todo@factorcode.org" lost-password-from set-global "todo@factorcode.org" lost-password-from set-global
init-factor-db init-factor-db
<factor-website> main-responder set-global ; <factor-website> main-responder set-global ;
: start-factor-website ( -- ) : start-factor-website ( -- )

View File

@ -39,8 +39,6 @@ TUPLE: article title revision ;
article "ARTICLES" { article "ARTICLES" {
{ "title" "TITLE" { VARCHAR 256 } +not-null+ +user-assigned-id+ } { "title" "TITLE" { VARCHAR 256 } +not-null+ +user-assigned-id+ }
! { "AUTHOR" INTEGER +not-null+ } ! uid
! { "PROTECTED" BOOLEAN +not-null+ }
{ "revision" "REVISION" INTEGER +not-null+ } ! revision id { "revision" "REVISION" INTEGER +not-null+ } ! revision id
} define-persistent } define-persistent
@ -111,14 +109,17 @@ M: revision feed-entry-url id>> revision-url ;
{ wiki "view" } >>template ; { wiki "view" } >>template ;
: amend-article ( revision article -- )
swap id>> >>revision update-tuple ;
: add-article ( revision -- )
[ title>> ] [ id>> ] bi article boa insert-tuple ;
: add-revision ( revision -- ) : add-revision ( revision -- )
[ insert-tuple ] [ insert-tuple ]
[ [
dup title>> <article> select-tuple [ dup title>> <article> select-tuple
swap id>> >>revision update-tuple [ amend-article ] [ add-article ] if*
] [
[ title>> ] [ id>> ] bi article boa insert-tuple
] if*
] bi ; ] bi ;
: <edit-article-action> ( -- action ) : <edit-article-action> ( -- action )