package ocsigenserver
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
A full-featured and extensible Web server
Install
dune-project
Dependency
Authors
Maintainers
Sources
8.0.0.tar.gz
md5=be7a28e3a79cc028d35c88eca538ee8b
sha512=08cdaab31864aeba743ad0c03077a6eccf1089e7b7c4078045dab9409d409144b70cc546df7881b64b780689eee642ca2391be84ab2a6a8ffee0fbed2ad5ac98
doc/src/securityheaders/securityheaders.ml.html
Source file securityheaders.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 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143(* Ocsigen * http://www.ocsigen.org * Module securityheaders.ml * Copyright (C) 2026 Vincent Balat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (* This module adds common security-related headers to responses, with one declaration instead of hand-rolled output filters. Safe-by-default headers (X-Content-Type-Options, X-Frame-Options, Referrer-Policy) are added unless disabled. Strict-Transport-Security and Content-Security-Policy are NOT added unless explicitly configured: HSTS is a sticky, HTTPS-only commitment (especially with [includeSubDomains]) that the server cannot decide safely on its own, and a wrong CSP breaks the application. *) let default_frame_options = "SAMEORIGIN" let default_referrer_policy = "strict-origin-when-cross-origin" (* Build the list of (header, value) pairs to add. A header is omitted when its option is [None]; [nosniff] adds [X-Content-Type-Options: nosniff]. *) let headers_of ~nosniff ~frame_options ~referrer_policy ~hsts ~content_security_policy = List.filter_map (fun x -> x) [ (if nosniff then Some ("X-Content-Type-Options", "nosniff") else None) ; Option.map (fun v -> "X-Frame-Options", v) frame_options ; Option.map (fun v -> "Referrer-Policy", v) referrer_policy ; Option.map (fun v -> "Strict-Transport-Security", v) hsts ; Option.map (fun v -> "Content-Security-Policy", v) content_security_policy ] (* Add each header to the response unless it is already set, so that a producer can override the default. *) let gen headers = function | Ocsigen.Extensions.Req_not_found (code, _) -> Lwt.return (Ocsigen.Extensions.Ext_next code) | Ocsigen.Extensions.Req_found (_ri, res) -> Lwt.return @@ Ocsigen.Extensions.Ext_found (fun () -> Lwt.return @@ List.fold_left (fun res (name, value) -> let name = Ocsigen_http.Header.Name.of_string name in match Ocsigen.Response.header res name with | Some _ -> res | None -> Ocsigen.Response.add_header res name value) res headers) (* ["no"], ["none"] and the empty string disable a header. *) let is_disabled s = match String.lowercase_ascii (String.trim s) with | "no" | "none" | "" -> true | _ -> false let parse_config config_elem = (* Reject CR/LF (and other control characters) in operator-supplied values to avoid header injection / response splitting. *) let checked name s = if String.exists (fun c -> Char.code c < 0x20) s then Ocsigen.Extensions.badconfig "Invalid control character in attribute %s of <securityheaders>" name; s in let nosniff = ref true in let frame_options = ref (Some default_frame_options) in let referrer_policy = ref (Some default_referrer_policy) in let hsts = ref None in let content_security_policy = ref None in let optional name r s = r := if is_disabled s then None else Some (checked name s) in Ocsigen.Extensions.( Configuration.process_element ~in_tag:"host" ~other_elements:(fun t _ _ -> raise (Bad_config_tag_for_extension t)) ~elements: [ Configuration.element ~name:"securityheaders" ~attributes: [ Configuration.attribute ~name:"nosniff" (fun s -> nosniff := not (match String.lowercase_ascii (String.trim s) with | "false" | "no" | "off" -> true | _ -> false)) ; Configuration.attribute ~name:"frame-options" (optional "frame-options" frame_options) ; Configuration.attribute ~name:"referrer-policy" (optional "referrer-policy" referrer_policy) ; Configuration.attribute ~name:"hsts" (optional "hsts" hsts) ; Configuration.attribute ~name:"content-security-policy" (optional "content-security-policy" content_security_policy) ] () ] config_elem); gen (headers_of ~nosniff:!nosniff ~frame_options:!frame_options ~referrer_policy:!referrer_policy ~hsts:!hsts ~content_security_policy:!content_security_policy) let () = Ocsigen.Extensions.register ~name:"securityheaders" ~fun_site:(fun _ _ _ _ _ _ -> parse_config) () let run ?(nosniff = true) ?(frame_options = Some default_frame_options) ?(referrer_policy = Some default_referrer_policy) ?(hsts = None) ?content_security_policy () _ _ _ = gen (headers_of ~nosniff ~frame_options ~referrer_policy ~hsts ~content_security_policy) (* Publish the safe-by-default instruction so that the one-command serve mode can add the security headers after loading this extension dynamically. *) let () = Ocsigen.Server.register_security_headers (run ())
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>