Merge pull request #47365 from p8/guides/document-request-js

Document Request.js in the guides [ci-skip]
This commit is contained in:
zzak 2023-04-09 16:57:50 +09:00 committed by GitHub
commit 4e41701b82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 89 additions and 14 deletions

@ -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