using afIoc::Injectusing afIocConfig::ConfigSourceusing afBedSheet::HttpRequestusing afBedSheet::HttpResponseusing afBedSheet::BedSheetServer** Guards against CSRF attacks by checking that the 'Referer' or 'Origin' HTTP header matches the 'Host'.** ** The idea behind the same origin check is that standard form POST requests should originate from the same server.** So the 'Referer' and 'Origin' HTTP headers are checked to ensure they match the server host. ** The 'Host' parameter is determined from [BedSheetServer.host()]`afBedSheet::BedSheetServer.host` and is usually picked up** from the 'BedSheetConfigIds.host' config value.** ** Requests are also denied if neither the 'Referer' and 'Origin' HTTP header are present. ** ** See [Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet]`https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Verifying_Same_Origin_with_Standard_Headers` for details.** ** ** ** Ioc Configuration** ******************* ** table:** afIocConfig Key Value** --------------------------------- ------------** 'afSleepSafe.sameOriginWhitelist' A CSV of alternative allowed origins.** ** Example:** ** syntax: fantom ** @Contribute { serviceType=ApplicationDefaults# }** Void contributeAppDefaults(Configuration config) {** config["afSleepSafe.sameOriginWhitelist"] = "http://domain1.com, http://domain2.com"** }** ** To configure the BedSheet host:** ** syntax: fantom ** @Contribute { serviceType=ApplicationDefaults# }** Void contributeAppDefaults(Configuration config) {** config["afBedSheet.host"] = `https://example.com`** }** ** To disable CSRF referrer checking, remove this class from the 'SleepSafeMiddleware' configuration:** ** syntax: fantom ** @Contribute { serviceType=SleepSafeMiddleware# }** Void contributeSleepSafeMiddleware(Configuration config) {** config.remove(SameOriginGuard#)** }** constclass SameOriginGuard : Guard { @Inject privateconst BedSheetServer bedServerprivateconst Uri[] whitelistprivatenew make(ConfigSource configSrc, |This| f){ f(this) csv := (Str) configSrc.get("afSleepSafe.sameOriginWhitelist", Str#) whitelist = csv.split(',').map { Uri(it, false)}.exclude {it == null || it.toStr.isEmpty }} @NoDocoverrideconst Str protectsAgainst := "CSRF" @NoDocoverride Str? guard(HttpRequest httpReq, HttpResponse httpRes){if(CsrfTokenGuard.fromVunerableUrl(httpReq)){ host := bedServer.host referrer := httpReq.headers.referrer?.plus(`/`)// delete the path component// referrer is optional and may be relative - see https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.36if(referrer != null && referrer.isAbs){if(host != referrer && !whitelist.contains(referrer))return csrfErr("Referrer does not match Host: ${referrer} != ${host}" + (whitelist.isEmpty ? "" : ", " + whitelist.join(", ")))} origin := httpReq.headers.origin?.plus(`/`)// delete any path component (not that there should be any)if(origin != null){if(host != origin && !whitelist.contains(origin))return csrfErr("Origin does not match Host: ${origin} != ${host}" + (whitelist.isEmpty ? "" : ", " + whitelist.join(", ")))}if(referrer == null && origin == null)return csrfErr("HTTP request contains neither a Referrer nor an Origin header")}returnnull}private Str csrfErr(Str msg){"Suspected CSRF attack - $msg"}}