Enable build support by adding .onedev-buildspec.yml
gradle/wrapper Loading last commit info...
src
.gitignore
LICENSE.txt
README.md
bitbucket-pipelines.yml
build.gradle.kts
gradle.properties
gradlew
gradlew.bat
settings.gradle
README.md

CSRF protection for Ktor

See https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet and https://seclab.stanford.edu/websec/csrf/csrf.pdf for details on what CSRF is and available countermeasures.

Usage

Artifacts are available via maven central, shown here in Gradle style:

implementation("org.mpierce.ktor.csrf", "ktor-csrf", "<LATEST_VERSION>")

Your configuration may vary, but here's a solid starting point: checking that the Origin header is your actual site, and enforcing that a custom header is present (which your front-end JavaScript/etc is presumably sending).

install(CsrfProtection) {
    // protect all routes by default
    applyToAllRoutes()
    validate(OriginMatchesKnownHost("https", "my-service.com"))
    validate(HeaderPresent("X-Some-Custom-Header-Your-Frontend-Sends"))
}

// ... configure routing ... 

routing {
    get("/protectedByDefault") { 
        // ...
    }
    // exclude these routes from csrf protection
    noCsrfProtection {
        get("/unprotected") {
            // ...
        }
        // ... except for this one, which should be protected (overriding the enclosing noCsrfProtection)
        csrfProtection {
            get("/protected")
        }
    }
}

Details

A first step is to validate that the origin of the request (represented by the Origin header) matches the host that your service is deployed at, e.g. https://my-service.com. See OriginMatchesKnownHost for an out-of-the-box implementation.

If configuring the service to know its deployment host ahead of time is infeasible, another approach would be to inspect the Host or X-Forwarded-Host headers and ensure that they match Origin. See OriginMatchesHostHeader.

Referer (sic) is another header that can sometimes contain origin info, but with plain HTTP (not HTTPS) it is sometimes stripped out by proxies, and the ReferrerPolicy response header can configure browsers to not send a Referer, so we just stick with Origin.

Another layer of defense is to require a custom header to be sent. This is feasible when the intended client is a purely AJAX-driven or otherwise has tight control over requests. Typical CSRF request would not have the opportunity to set any headers beyond what the browser provides by default. See HeaderIsPresent for an implementation of this.

Please wait...
Page is in error, reload to recover