/vapor-csrf

Easy CSRF protection for your Vapor websites

Primary LanguageSwiftMIT LicenseMIT

Vapor CSRF

Vapor CSRF

Language Language Build Status Code Coverage MIT License

A simple library for protecting POST requests from CSRF (cross-site request forgery) attacks.

What is CSRF?

In simple terms it's tricking a user into making requests that a web application accepts. Imagine a bank website that has a POST request to transfer money into an account. If a malicious site can force the user to send that POST request (when they're logged in) then an attacker could trick a user into transferring money.

CSRF tokens protect against this by ensuring the POST request is legitimate. The website provides a token to the GET request which it then checks when handling the POST request to ensure it matches.

Modern solutions such as SameSite cookies provide a similar protection but aren't supported on all browsers.

Installation

Add the CSRF library in your dependencies array in Package.swift:

dependencies: [
    // ...,
    .package(name: "VaporCSRF", url: "https://github.com/brokenhandsio/vapor-csrf.git", from: "1.0.0")
],

Also ensure you add it as a dependency to your target:

targets: [
    .target(name: "App", dependencies: [
        .product(name: "Vapor", package: "vapor"), 
        // ..., 
        "VaporCSRF"]),
    // ...
]

Usage

You must be using the SessionsMiddleware on all routes you interact with CSRF with. You can enable this globally in configure.swift with:

app.middleware.use(app.sessions.middleware)

For more information on sessions, see the documentation.

GET routes

In GET routes that could return a POST request you want to protect, store a CSRF token in the session:

let csrfToken = req.csrf.storeToken()

This function returns a token you can then pass to your HTML page. For example, with Leaf this would look like:

let csrfToken = req.csrf.storeToken()
let context = MyPageContext(csrfToken: csrfToken)
return req.view.render("myPage", context)

You then need to return the token when the form is submitted. With Leaf, this would look something like:

<form method="post">
    <input type="hidden" name="csrfToken" value="#(csrfToken)">
    <input type="submit" value="Submit">
</form>

POST routes

You can protect your POST routes either with Middleware or manually verifying the token.

Middleware

VaporCSRF provides a middleware that checks the token for you. You can apply this to your routes with:

let csrfTokenPotectedRoutes = app.grouped(CSRFMiddleware())

Manual Verification

If you want to control when you verify the CSRF token, you can do this manually in your route handler with try req.csrf.verifyToken(). E.g.:

app.post("myForm") { req -> EventLoopFuture<Response> in
    try req.csrf.verifyToken()
    // ...
}

Configuration

By default, VaporCSRF looks for a value with the key csrfToken in the POST body. You can change the key with:

app.csrf.setTokenContentKey("aDifferentKey")