A simple library for protecting POST requests from CSRF (cross-site request forgery) attacks.
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.
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"]),
// ...
]
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.
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>
You can protect your POST routes either with Middleware or manually verifying the token.
VaporCSRF provides a middleware that checks the token for you. You can apply this to your routes with:
let csrfTokenPotectedRoutes = app.grouped(CSRFMiddleware())
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()
// ...
}
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")