package sihl
The modular functional web framework
Install
Dune Dependency
Authors
Maintainers
Sources
sihl-queue-0.1.5.tbz
sha256=bfa7bde9af02bb83d5ca39d54797b05b43317f033d93d24ca86ca42ff8ef83a1
sha512=6bb8727f65116e8042aa1fb77b3c14851ce5238f7b412adadf0f8e5b52d5310e8f06056c96bf76a82ffd7096753f49b2b0482f41e18ee1ca94310b874fe81bf9
doc/src/sihl.middleware/middleware_csrf.ml.html
Source file middleware_csrf.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
open Lwt.Syntax (* Can be used to fetch token in view for forms *) let ctx_token_key : string Core.Ctx.key = Core.Ctx.create_key () (*TODO [aerben] optional*) let get_token ctx = Core.Ctx.find ctx_token_key ctx exception No_csrf_token of string (* TODO (https://docs.djangoproject.com/en/3.0/ref/csrf/#how-it-works) Check other Django specifics namely: - Testing views with custom HTTP client - Allow Sihl user to make views exempt - Enable subdomain - HTML caching token handling *) module Make (TokenService : Token.Sig.SERVICE) (SessionService : Session.Sig.SERVICE) (RandomService : Utils.Random.Service.Sig.SERVICE) = struct let csrf_token_length = 20 let create_secret ctx = let* token = TokenService.create ctx ~kind:"csrf" ~length:csrf_token_length () in (* Store the ID in the session *) (* Storing the token directly could mean it ends up on the client if the cookie backend is used for session storage *) let* () = SessionService.set ctx ~key:"csrf" ~value:token.id in Lwt.return token ;; let m () = let filter handler ctx = (* Check if session already has a secret (token) *) let* id = SessionService.get ctx ~key:"csrf" in let* secret = match id with (* Create a secret if no secret found in session *) | None -> create_secret ctx | Some token_id -> let* token = TokenService.find_by_id_opt ctx token_id in (match token with (* Create a secret if invalid token in session *) | None -> create_secret ctx (* Return valid secret from session *) | Some secret -> Lwt.return secret) in (* Randomize and scramble secret (XOR with salt) to make a token *) (* Do this to mitigate BREACH attacks: http://breachattack.com/#mitigations *) let salt = RandomService.base64 ~bytes:csrf_token_length in let token = salt ^ Utils.Encryption.xor salt secret.value in let ctx = Core.Ctx.add ctx_token_key token ctx in (* Don't check for CSRF token in GET requests *) (* TODO don't check for HEAD, OPTIONS and TRACE either *) if Http.Req.is_get ctx then handler ctx else let* value = Http.Req.urlencoded ctx "csrf" in match value with (* Give 403 if no token provided *) | None -> Http.Res.(html |> set_status 403) |> Lwt.return | Some value -> let token = Utils.Encryption.decrypt_with_salt ~salted_cipher:value ~salt_length:csrf_token_length in let* provided_token = TokenService.find_opt ctx token in (match provided_token with | Some tkp -> if not @@ Token.equal secret tkp then (* Give 403 if provided token doesn't match session token *) Http.Res.(html |> set_status 403) |> Lwt.return else (* Provided token matches and is valid => Invalidate it so it can't be reused *) let* () = TokenService.invalidate ctx tkp in handler ctx | None -> (* Give 403 if provided token does not exist *) Http.Res.(html |> set_status 403) |> Lwt.return) in Middleware_core.create ~name:"csrf" filter ;; end
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>