Skip to content

Commit

Permalink
Docs
Browse files Browse the repository at this point in the history
  • Loading branch information
levkk committed Nov 8, 2024
1 parent 59c7921 commit 0f3e337
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 43 deletions.
5 changes: 3 additions & 2 deletions docs/docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,17 @@ Rwf will automatically load configuration settings from that file, as they are n

The configuration file is using the [TOML language](https://toml.io/). If you're not familiar with TOML, it's pretty simple and expressive language commonly used in the world of Rust programming.

Rwf configuration file is split into multiple sections. The `[general]` section controls various options such as logging settings, and which secret key to use for [encryption](encryption.md). The `[database]`
Rwf configuration file is split into multiple sections. The `[general]` section controls various options such as logging settings, and which secret key to use for [encryption](security/encryption.md). The `[database]`
section configures database connection settings, like the database URL, connection pool size, and others.

### `[general]`

| Setting | Description | Default |
|---------|-------------|---------|
| `log_queries` | Toggles logging of all SQL queries executed by the [ORM](models/index.md). | `false` |
| `secret_key` | Secret key, encoded using base64, used for [encryption](encryption.md). | Randomly generated |
| `secret_key` | Secret key, encoded using base64, used for [encryption](security/encryption.md). | Randomly generated |
| `cache_templates` | Toggle caching of [dynamic templates](views/templates/index.md). | `false` in debug, `true` in release |
| `csrf_protection` | Validate the [CSRF](security/CSRF.md) token is present on requests that mutate your application (POST, PUT, PATCH). | `true` |

#### Secret key

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/controllers/cookies.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ response
.add_private(cookie)?;
```

Cookies are [encrypted](../encryption.md) with AES-128, using the security key set in the [configuration](../configuration.md).
Cookies are [encrypted](../security/encryption.md) with AES-128, using the security key set in the [configuration](../configuration.md).


### Read private cookies
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/controllers/sessions.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Sessions

A session is an [encrypted](../encryption.md) [cookie](cookies.md) managed by Rwf. It contains a unique identifier for each browser using your web app. All standard-compliant browsers connecting to Rwf-powered apps will have a Rwf session set automatically, and should send it back on each request.
A session is an [encrypted](../security/encryption.md) [cookie](cookies.md) managed by Rwf. It contains a unique identifier for each browser using your web app. All standard-compliant browsers connecting to Rwf-powered apps will have a Rwf session set automatically, and should send it back on each request.

## Session types

Expand Down
95 changes: 95 additions & 0 deletions docs/docs/security/CSRF.md
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 docs/docs/encryption.md → docs/docs/security/encryption.md
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");
```

0 comments on commit 0f3e337

Please sign in to comment.