-
-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
139 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
# CSRF protection | ||
|
||
Cross-site request forgery[^1] (or CSRF) is a type of attack which uses your website's forms to trick the user into submitting data to your application from somewhere else. Rwf comes with [middleware](../controllers/middleware.md) to protect your application against such attacks. | ||
|
||
## Enable CSRF protection | ||
|
||
CSRF protection is enabled by default. When users make `POST`, `PUT`, and `PATCH` requests to your app, Rwf will check for the presence of a CSRF token. If the token is not there, or has expired, the request will be blocked and `HTTP 400 - Bad Request` response will be returned. | ||
|
||
## Passing the token | ||
|
||
The CSRF token can be passed using one of two methods: | ||
|
||
- `X-CSRF-Token` HTTP header | ||
- `<input name="rwf_csrf_token" type="hidden">` inside a form | ||
|
||
If you're submitting a form, you can add the `rwf_csrf_token` input automatically: | ||
|
||
```html | ||
<form method="post" action="/login"> | ||
<%= rwf_token() %> | ||
</form> | ||
``` | ||
|
||
If you're making AJAX requests (using `fetch`, for example), you can pass the token via the header. If you're using Stimulus (which comes standard with Rwf), you can pass the token via a data attribute to the Stimulus controller: | ||
|
||
=== "HTML" | ||
```html | ||
<div | ||
data-controller="login" | ||
data-csrf-token="<%= rwf_token_raw() %>" | ||
> | ||
<!-- ... --> | ||
</div> | ||
``` | ||
=== "JavaScript" | ||
```javascript | ||
import { Controller } from "hotwired/stimulus" | ||
|
||
export default class LoginController extends Controller { | ||
|
||
// Send request with CSRF token included. | ||
sendRequest() { | ||
const csrfToken = this.element.dataset.csrfToken; | ||
|
||
fetch("/login", { | ||
headers: { | ||
"X-CSRF-Token": csrfToken, | ||
} | ||
}) | ||
} | ||
|
||
} | ||
``` | ||
|
||
## Disable CSRF protection | ||
|
||
If you want to disable CSRF protection, you can do so globally by toggling the `csrf_protection` [configuration option](../configuration.md) to `false`, or on the controller level by implementing the `fn skip_csrf(&self)` method: | ||
|
||
```rust | ||
use rwf::prelude::*; | ||
|
||
#[derive(Default)] | ||
struct IndexController; | ||
|
||
impl Controller for IndexController { | ||
/// Disable CSRF protection for this controller. | ||
fn skip_csrf(&self) -> bool { | ||
true | ||
} | ||
|
||
/* ... */ | ||
} | ||
``` | ||
|
||
### REST | ||
|
||
If you're using JavaScript frameworks like React or Vue for your frontend, it's common to disable CSRF protection on your [REST](../controllers/REST/index.md) controllers. To do so, you can add the `#[skip_csrf]` attribute to your `ModelController`, for example: | ||
|
||
```rust | ||
#[derive(macros::ModelController)] | ||
#[skip_csrf] | ||
struct Users; | ||
``` | ||
|
||
You can always disable CSRF globally via [configuration](#disable-csrf-protection) and enable it only on the controllers that serve HTML forms. | ||
|
||
## Token validity | ||
|
||
The CSRF token is valid for the same duration as Rwf [sessions](../controllers/sessions.md). By default, this is set to 4 weeks. A new token is generated every time your users load a page which contains a token generated with the built-in template functions. | ||
|
||
## WSGI / Rack controllers | ||
|
||
Rwf CSRF protection is disabled for [Python](../migrating-from-python.md) and [Rails](../migrating-from-rails.md) applications. It's expected that Django/Flask/Rails applications will use their own CSRF protection middleware. | ||
|
||
[^1]: [https://en.wikipedia.org/wiki/Cross-site_request_forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery) |
78 changes: 39 additions & 39 deletions
78
docs/docs/encryption.md → docs/docs/security/encryption.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,39 @@ | ||
# Encryption | ||
|
||
Rwf uses [AES-128](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) for encrypting user [sessions](controllers/sessions.md) and private [cookies](controllers/cookies.md). The same functionality is available through the [`rwf::crypto`](https://docs.rs/rwf/latest/rwf/crypto/index.html) module to encrypt and decrypt arbitrary data. | ||
|
||
## Encrypt data | ||
|
||
To encrypt data using AES-128 and the application secret key, you can use the [`encrypt`](https://docs.rs/rwf/latest/rwf/crypto/fn.encrypt.html) function, for example: | ||
|
||
```rust | ||
use rwf::crypto::encrypt; | ||
|
||
let data = serde_json::json!({ | ||
"user": "test", | ||
"password": "hunter2" | ||
}); | ||
|
||
// JSON is converted into a byte array. | ||
let data = serde_json::to_vec(&data).unwrap(); | ||
|
||
// Data is encrypted with AES. | ||
let encrypted = encrypt(&data).unwrap(); | ||
``` | ||
|
||
Any kind of data can be encrypted, as long as it's serializable to an array of bytes. Serialization can typically be achieved by using [`serde`](https://docs.rs/serde/latest/serde/). | ||
|
||
Encryption produces a base64-encoded UTF-8 string. You can save this string in the database or send it via an insecure medium like email. | ||
|
||
## Decrypt data | ||
|
||
To decrypt the data, you can call the [`decrypt`](https://docs.rs/rwf/latest/rwf/crypto/fn.decrypt.html) function on the string produced by the `encrypt` function. The decryption algorithm will automatically convert the base64-encoded string to bytes and decrypt those bytes using the secret key, for example: | ||
|
||
```rust | ||
use rwf::crypto::decrypt; | ||
|
||
let decrypted = decrypt(&encrypted).unwrap(); | ||
let json = serde_json::from_slice(&decrypted).unwrap(); | ||
|
||
assert_eq!(json["user"], "test"); | ||
``` | ||
# Encryption | ||
|
||
Rwf uses [AES-128](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) for encrypting user [sessions](../controllers/sessions.md) and private [cookies](../controllers/cookies.md). The same functionality is available through the [`rwf::crypto`](https://docs.rs/rwf/latest/rwf/crypto/index.html) module to encrypt and decrypt arbitrary data. | ||
|
||
## Encrypt data | ||
|
||
To encrypt data using AES-128 and the application secret key, you can use the [`encrypt`](https://docs.rs/rwf/latest/rwf/crypto/fn.encrypt.html) function, for example: | ||
|
||
```rust | ||
use rwf::crypto::encrypt; | ||
|
||
let data = serde_json::json!({ | ||
"user": "test", | ||
"password": "hunter2" | ||
}); | ||
|
||
// JSON is converted into a byte array. | ||
let data = serde_json::to_vec(&data).unwrap(); | ||
|
||
// Data is encrypted with AES. | ||
let encrypted = encrypt(&data).unwrap(); | ||
``` | ||
|
||
Any kind of data can be encrypted, as long as it's serializable to an array of bytes. Serialization can typically be achieved by using [`serde`](https://docs.rs/serde/latest/serde/). | ||
|
||
Encryption produces a base64-encoded UTF-8 string. You can save this string in the database or send it via an insecure medium like email. | ||
|
||
## Decrypt data | ||
|
||
To decrypt the data, you can call the [`decrypt`](https://docs.rs/rwf/latest/rwf/crypto/fn.decrypt.html) function on the string produced by the `encrypt` function. The decryption algorithm will automatically convert the base64-encoded string to bytes and decrypt those bytes using the secret key, for example: | ||
|
||
```rust | ||
use rwf::crypto::decrypt; | ||
|
||
let decrypted = decrypt(&encrypted).unwrap(); | ||
let json = serde_json::from_slice(&decrypted).unwrap(); | ||
|
||
assert_eq!(json["user"], "test"); | ||
``` |