vapor-community/csrf
CSRF stands for cross-site request forgery; it is also called XSRF, a one-click attack, and session riding.
Further Reading
- OWASP.org has a good resource on CSRF)
- Wikipedia, as usual, has a good overview
Protecting Against CSRF Attacks
There are a few ways to protect against this sort of vulnerability. Since the attack exploits the site's trust of some user, most prevention techniques add authentication information to each request. Doing so helps the site to disambiguate between authorized and unauthorized requests.
The direction taken by this package is to use sessions. The session will hold a secret. The secret will be used to create a hashed token. The token will be sent back to clients in the response's header. Tokens will last as long as the session is viable.
For example, the server will generate a token and set the "csrf-token" key in the header like so:
response.headers.add(name: "csrf-token", value: "some-very-secret-token")Clients are then responsible for sending this key and token with each request for the duration of their session.
The CSRF middleware will then guarantee three things:
- That there is a session
- That the request contains a key (there are a number of keys used for CSRF prevention)
- That the key's token matches the secret held by the session
If any of these conditions fail, then the CSRF middleware will throw an error describing the problem.
Using CSRF in Vapor
The following provides instructions on how to use this package on your site.
Usage
- Add the CSRF to your
Package.swift
dependencies: [
...,
.package(url: "https://github.com/vapor-community/CSRF.git", from: "3.0.0")
]- Add
SessionsMiddlewareandCSRFmiddlware inconfigure.swift(or your route group…)
app.middleware.use(app.sessions.middleware)
app.middleware.use(CSRF())This will create an instance with two important defaults:
ignoredMethodswill be set to[.GET, .HEAD, .OPTIONS]. These methods will not be submitted to the checks mentioned above. This is fine because these methods are not used to change server state.defaultTokenRetrievalwill be set to((Request) throws -> Future<String>). That is, it will be a function, provided by default, that will take in aRequestand return aFuture<String>holding the token if it is found; otherwise, the method will throw an error.
You can customize either of these properties on CSRF by passing your preferred values to this initializer.
- Create the token and set it in the response header
router.get("test-no-session") { request in
let response = ...
response.headers.add(name: "csrf-token", value: CSRF.createToken(from: request))
return response
}Usage with Leaf and forms
To use this package in combination with Leaf to protect forms, there is a tag provided for convenience:
- Add
CSRFFormFieldTaginconfigure.swift
app.leaf.tags["csrfFormField"] = CSRFFormFieldTag()- Use
CSRFFormFieldTagin Leaf templates, e.g. like this
<form method="post">
<input type="text" name="username">
<input type="text" name="password">
[…]
#csrfFormField()
<input type="submit" value="Login">
</form>Package Metadata
Repository: vapor-community/csrf
Default branch: master
README: README.md