Merge pull request #47365 from p8/guides/document-request-js
Document Request.js in the guides [ci-skip]
This commit is contained in:
commit
4e41701b82
@ -250,6 +250,8 @@ CSRF appears very rarely in CVE (Common Vulnerabilities and Exposures) - less th
|
||||
|
||||
NOTE: _First, as is required by the W3C, use GET and POST appropriately. Secondly, a security token in non-GET requests will protect your application from CSRF._
|
||||
|
||||
#### Use GET and POST Appropriately
|
||||
|
||||
The HTTP protocol basically provides two main types of requests - GET and POST (DELETE, PUT, and PATCH should be used like POST). The World Wide Web Consortium (W3C) provides a checklist for choosing HTTP GET or POST:
|
||||
|
||||
**Use GET if:**
|
||||
@ -287,21 +289,52 @@ There are many other possibilities, like using a `<script>` tag to make a cross-
|
||||
|
||||
NOTE: We can't distinguish a `<script>` tag's origin—whether it's a tag on your own site or on some other malicious site—so we must block all `<script>` across the board, even if it's actually a safe same-origin script served from your own site. In these cases, explicitly skip CSRF protection on actions that serve JavaScript meant for a `<script>` tag.
|
||||
|
||||
#### Required Security Token
|
||||
|
||||
To protect against all other forged requests, we introduce a _required security token_ that our site knows but other sites don't know. We include the security token in requests and verify it on the server. This is done automatically when [`config.action_controller.default_protect_from_forgery`][] is set to `true`, which is the default for newly created Rails applications. You can also do it manually by adding the following to your application controller:
|
||||
|
||||
```ruby
|
||||
protect_from_forgery with: :exception
|
||||
```
|
||||
|
||||
This will include a security token in all forms and Ajax requests generated by Rails. If the security token doesn't match what was expected, an exception will be thrown.
|
||||
This will include a security token in all forms generated by Rails. If the
|
||||
security token doesn't match what was expected, an exception will be thrown.
|
||||
|
||||
NOTE: By default, Rails includes an [unobtrusive scripting adapter](https://github.com/rails/rails/blob/main/actionview/app/assets/javascripts),
|
||||
which adds a header called `X-CSRF-Token` with the security token on every non-GET
|
||||
Ajax call. Without this header, non-GET Ajax requests won't be accepted by Rails.
|
||||
When using another library to make Ajax calls, it is necessary to add the security
|
||||
token as a default header for Ajax calls in your library. To get the token, have
|
||||
a look at `<meta name='csrf-token' content='THE-TOKEN'>` tag printed by
|
||||
`<%= csrf_meta_tags %>` in your application view.
|
||||
When submitting forms with [Turbo](https://turbo.hotwired.dev/) the security
|
||||
token is required as well. Turbo looks for the token in the `csrf` meta tags of
|
||||
your application layout and adds it to request in the `X-CSRF-Token` request
|
||||
header. These meta tags are created with the [`csrf_meta_tags`][] helper
|
||||
method:
|
||||
|
||||
```erb
|
||||
<head>
|
||||
<%= csrf_meta_tags %>
|
||||
</head>
|
||||
```
|
||||
|
||||
which results in:
|
||||
|
||||
```html
|
||||
<head>
|
||||
<meta name="csrf-param" content="authenticity_token" />
|
||||
<meta name="csrf-token" content="THE-TOKEN" />
|
||||
</head>
|
||||
```
|
||||
|
||||
When making your own non-GET requests from JavaScript the security token is
|
||||
required as well. [Rails Request.JS](https://github.com/rails/request.js) is a
|
||||
JavaScript library that encapsulates the logic of adding the required request
|
||||
headers.
|
||||
|
||||
When using another library to make Ajax calls, it is necessary to add the
|
||||
security token as a default header yourself. To get the token from the meta tag
|
||||
you could do something like:
|
||||
|
||||
```javascript
|
||||
document.head.querySelector("meta[name=csrf-token]")?.content
|
||||
```
|
||||
|
||||
#### Clearing Persistent Cookies
|
||||
|
||||
It is common to use persistent cookies to store user information, with `cookies.permanent` for example. In this case, the cookies will not be cleared and the out of the box CSRF protection will not be effective. If you are using a different cookie store than the session for this information, you must handle what to do with it yourself:
|
||||
|
||||
@ -316,6 +349,7 @@ The above method can be placed in the `ApplicationController` and will be called
|
||||
Note that _cross-site scripting (XSS) vulnerabilities bypass all CSRF protections_. XSS gives the attacker access to all elements on a page, so they can read the CSRF security token from a form or directly submit the form. Read [more about XSS](#cross-site-scripting-xss) later.
|
||||
|
||||
[`config.action_controller.default_protect_from_forgery`]: configuring.html#config-action-controller-default-protect-from-forgery
|
||||
[`csrf_meta_tags`]: https://api.rubyonrails.org/classes/ActionView/Helpers/CsrfHelper.html#method-i-csrf_meta_tags
|
||||
|
||||
Redirection and Files
|
||||
---------------------
|
||||
|
@ -238,17 +238,18 @@ With a WebSocket connection set up on the page that should receive the updates l
|
||||
Replacements for Rails/UJS Functionality
|
||||
----------------------------------------
|
||||
|
||||
Rails 6 shipped with a tool called UJS that allows developers to override the method of `<a>` tags
|
||||
to perform non-GET requests after a hyperlink click and to add confirmation dialogs before
|
||||
executing an action. This was the default before Rails 7, but it is now recommended to use Turbo
|
||||
instead.
|
||||
Rails 6 shipped with a tool called UJS (Unobtrusive JavaScript). UJS allows
|
||||
developers to override the HTTP request method of `<a>` tags, to add confirmation
|
||||
dialogs before executing an action, and more. UJS was the default before Rails
|
||||
7, but it is now recommended to use Turbo instead.
|
||||
|
||||
### Method
|
||||
|
||||
Clicking links always results in an HTTP GET request. If your application is
|
||||
[RESTful](https://en.wikipedia.org/wiki/Representational_State_Transfer), some links are in fact
|
||||
actions that change data on the server, and should be performed with non-GET requests. This
|
||||
attribute allows marking up such links with an explicit method such as "post", "put", or "delete".
|
||||
actions that change data on the server, and should be performed with non-GET
|
||||
requests. The `data-turbo-method` attribute allows marking up such links with
|
||||
an explicit method such as "post", "put", or "delete".
|
||||
|
||||
Turbo will scan `<a>` tags in your application for the `turbo-method` data attribute and use the
|
||||
specified method when present, overriding the default GET action.
|
||||
@ -297,3 +298,43 @@ added to the form that the `button_to` helper renders internally:
|
||||
```erb
|
||||
<%= button_to "Delete post", post, method: :delete, form: { data: { turbo_confirm: "Are you sure?" } } %>
|
||||
```
|
||||
|
||||
### Ajax Requests
|
||||
|
||||
When making non-GET requests from JavaScript the `X-CSRF-Token` header is required.
|
||||
Without this header requests won't be accepted by Rails.
|
||||
|
||||
[Rails Request.JS](https://github.com/rails/request.js) encapsulates the logic
|
||||
of adding the request headers that are required by Rails. Just
|
||||
import the `FetchRequest` class from the package and instantiate it
|
||||
passing the request method, url, options, then call `await request.perform()`
|
||||
and do what do you need with the response.
|
||||
|
||||
For example:
|
||||
|
||||
```javascript
|
||||
import { FetchRequest } from '@rails/request.js'
|
||||
|
||||
....
|
||||
|
||||
async myMethod () {
|
||||
const request = new FetchRequest('post', 'localhost:3000/posts', {
|
||||
body: JSON.stringify({ name: 'Request.JS' })
|
||||
})
|
||||
const response = await request.perform()
|
||||
if (response.ok) {
|
||||
const body = await response.text
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When using another library to make Ajax calls, it is necessary to add the
|
||||
security token as a default header yourself. To get the token, have a look at
|
||||
`<meta name='csrf-token' content='THE-TOKEN'>` tag printed by
|
||||
[`csrf_meta_tags`][] in your application view. You could do something like:
|
||||
|
||||
```javascript
|
||||
document.head.querySelector("meta[name=csrf-token]")?.content
|
||||
```
|
||||
|
||||
[`csrf_meta_tags`]: https://api.rubyonrails.org/classes/ActionView/Helpers/CsrfHelper.html#method-i-csrf_meta_tags
|
||||
|
Loading…
Reference in New Issue
Block a user