## 2021年8月23日(月)現場Rails Chapter6-7 セキュリティを強化する
### Railsの代表的なセキュリティ機能
- StrongParameters
- CSRF対策
- インジェクション対策
## Strong Prametersとは
想定通りのパラメータかどうかをホワイトリスト方式(リストにないアプリケーションやプログラムは起動しないよう制限を設け、リストにあるアプリケーションやプログラムのみを実行します。 リストに入っていないものはブロックし、一切使用しないことで危険性を回避する)でチェックする機能。
<span style="color: red; ">**※ホワイトリストはブラックリストの逆のものというイメージを持つ。**</span>
### マスアサイメント機能とは
マスアサイメント機能とは、モデルからインスタンスを生成するときに、インスタンスに複数の属性を一括代入できる機能です。
ざっくり言うと、複数のキーワード引数に値を渡すこと。登録、更新を一括でできてしまう機能をマスアサイメント機能という。
つまり、登録更新処理またはインスタンス生成時(newする時)どちらでも属性を一括代入できる。
```ruby
Person.new(name: 'hoge', age: 24)
person.update(name: 'hoge', age: 24)
```
DBにないカラムをnewの引数で指定すると、エラーが起こる。
以下の例の場合、Taskオブジェクトを生成する際に、nameとdescriptionという2つの属性に値を一括で代入している。
```ruby=
task = Task.new(name: 'ラーメン食べたい', description: '今すぐ食べたい')
```

### 許可される型
[Railsガイド Action Controllerの概要 4.5.1 許可済みスカラー値より](https://railsguides.jp/action_controller_overview.html#%E8%A8%B1%E5%8F%AF%E6%B8%88%E3%81%BF%E3%82%B9%E3%82%AB%E3%83%A9%E3%83%BC%E5%80%A4)
`params.permit(:id)`
- String
- Symbol
- NilClass
- Numeric
- TrueClass
- FalseClass
- Date
- Time
- DateTime
- StringIO
- IOActionDispatch::Http::UploadedFile
- Rack::Test::UploadedFile
「paramsの値には許可されたスカラー値の配列を使わなければならない」=>以下のようにキーに空の配列を対応付けられる
`params.permit(id: [])`
### Strong parametersの基本構文
``` ruby
#controller内定義
private
def user_params
params.require(:キー(モデル名)).permit(:カラム名1,:カラム名2,・・・).merge(カラム名: 入力データ)
end
```
### requireメソッド
params内の特定のキーに紐付く値だけを抽出する事ができます。そのため、引数には取り出したい値のキーを指定する必要があります。requireに設定した値が見つからない場合は、例外を発生させます。
```ruby=
#キー値userに対するデータを抽出したい場合
params.require(:user).permit(・・・略・・・)
```
### permitメソッド
許可された値のみを取得することができます。
そのため、permitメソッドの引数には登録を許可する全てのカラム名を指定しておく必要があります。もし、許可されいないカラムがparams内に存在した場合、そのデータは取得されず無視されます。
```ruby=
#Userモデルに存在するnameおよび、emailカラムのみ入力を受け付けたい場合(他のカラム(admin)は受け付けないたくない)
params.require(:user).permit(:name,:email)
```
## mergeメソッド
mergeメソッドを使用することでハッシュ同士を結合することができます。
例えば、paramsに含まれない値をストロングメソッドに加えたい場合などに、ストロングパラメータの後に記述することができます。
```ruby=
#paramsのkeyとvalueに含まれていないものを追加したい場合、mergeを使う
params.require(:user).permit(:name,:email).merge(user_id: current_user.id)
```
↓チェリー本 P163 mergeの例文
```ruby=
h = { us: 'dollar', india: 'rupee'}
{ japan: 'yen' }.merge(h) #=> {:japan=>"yen", :us=>"dollar", :india=>"rupee"}
```
### 一次元ハッシュと2次元ハッシュ
```ruby=
#1次元ハッシュ
{key1: value1, key2: value2}
#2次元ハッシュ
{key1: value1, key2: {key3: value3}}
#一次元ハッシュの場合=>URL
params.permit(:キー名)
# 二次元ハッシュの場合=>モデル
params.require(:モデル名).permit(:キー名)
```
requireとpermitメソッドとmergeメソッドのめっちゃわかりやすい例↓
```ruby=
#keyであるboardのvalueをハッシュの形で指定している。mergeで追加されるのは、boardのvalueの部分。
params.require(:board).permit(:body).merge(user_id: current_user.id)
{"body"=>"abc", "user_id"=>5}
#=>{board:{body: value1, user_id: value2}}
```
## privateメソッド
privateメソッド配下に記述したメソッドは、クラス外からのアクセスができません。
基本的にストロングパラメータは、クラス外からのアクセスをさせないようにprivateメソッド配下に書く。
## CSRF
- CSRFとは、ユーザーがログイン済みのアプリケーションに対して、悪意のあるリクエストを飛ばすこと。
- RailsにおけるCSRF対策はHTTPメソッドのGET以外を使用することで、自動的にパラメーターにセキュリティートークンを持たせることで実現している。
### CSRF攻撃例
攻撃例:
Amazon会員の太郎さん。ブラウザ操作でログアウトしていない状況で、悪意あるサイトにアクセスしました。
そこには「こちらをクリックすると懸賞にワンクリックで応募できます」の文字と、ボタンがあり、太郎さんはそのボタンをクリックしました。
しかし、そのボタンはAmazon会員を解約する`href="...../delete"`みたいなURLが仕込まれており、太郎さんは意図せずAmazon会員を解約してしまいました。
### Ajax
JavaScriptの値を明示的にRails側に渡したい場合は、jQueryでajaxメソッドを使用するのが良い。(jQuery.ajax())ただし、jQueryをrails-ujsの前に読み込んでおく必要がある。
「Rails.ajax()」関数を利用することでトークン付きのAjaxリクエストを行うことが出来る。
#### **Ajax**とは?
JavaScriptでサーバー側との通信を「**非同期**」で行い、通信結果によって「動的にページの一部だけ書き換える手法のこと」です。JavaScript以外にもXMLやDOMなど「既存の機能」を組み合わせて実現します。
例)twitterのいいね機能
ページ全体の更新を行うのではなく、読み込みたい部分だけの通信を行うことで、いいね部分だけ更新することができる。通信の無駄がない!対して、**同期通信**は更新をしなくて良い部分も更新してしまうので無駄が多い!
### POSTリクエスト
Railsのルーティングの文脈での「DELETE」「PATCH」「PUT」なども、ここでいうPOSTリクエストに該当します。
## 参照
Railsガイド StrobngParameters
https://railsguides.jp/action_controller_overview.html#strong-parameters
【Rails】requireとpermitメソッド
https://qiita.com/morikuma709/items/2dc20d922409ae7ce216
【Ruby on Rails】ストロングパラメータって何なの?
https://qiita.com/ozackiee/items/f100fd51f4839b3fdca8
チェリー本p163
pikawakaさんブログ
https://pikawaka.com/rails/permit
Railsのセッション管理には何が最適か
https://qiita.com/shota_matsukawa_ga/items/a21c5cf49a1de6c9561a
初心者目線でAjaxの説明
https://qiita.com/hisamura333/items/e3ea6ae549eb09b7efb9
Railsガイド 「Rails セキュリティガイド -クロスサイトリクエストフォージェリ (CSRF)-」
https://railsguides.jp/security.html#%E3%82%AF%E3%83%AD%E3%82%B9%E3%82%B5%E3%82%A4%E3%83%88%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E3%83%95%E3%82%A9%E3%83%BC%E3%82%B8%E3%82%A7%E3%83%AA-csrf
Railsのセッション管理には何が最適か
https://qiita.com/shota_matsukawa_ga/items/a21c5cf49a1de6c9561a
## 2021年8月24日 現場Rails Chapter6-7-3 インジェクションに注意する
## インジェクション
Webアプリケーションに悪意のあるスクリプトやパラメータを入力し、それが評価される時の権限で、実行させる攻撃。ユーザーがデータを入力するところは注意が必要!
- XXS(クロスサイトスクリプティング)
- SOLインジェクション
- rubyコードインジェクション
- コマンドラインインジェクション
### XXS
ユーザーに表示するコンテンツに悪意のあるスクリプト(主にJavaScript)を仕掛け、悪意のある操作を行う攻撃方法
[ブラウザ画面参考例: 実際にサンプルページで確認してみた!クロスサイトスクリプティング(XSS)の危険性!](https://eit.systems/danger-of-cross-site-scripting/)
[情報漏洩の参考図: クロスサイトスクリプティングとは?仕組みと事例から考える対策](https://cybersecurity-jp.com/security-measures/18427)
例:`&`,`"`,`<`,`>` → `&`,`"`,`<`,`>`
**Railsのviewファイルはデフォルトでエスケープされる。**
### エスケープとは
HTML文の中では「<」「>」「&」「"」といった文字は特別な意味を持ちます。その為、HTMLタグを含む文字列をそのまま出力してしまうと違う意味になってしまいます。その為、特別の意味を持つ文字が含まれる文字列を文字列として出力されたい場合には、特別な意味を持つ文字をエスケープした上で出力する必要があります。
<span style="color: red; ">**※Railsのビューではユーザーの入力した文字列を出力する際に自動的にHTMLをエスケープする。
※エスケープした場合が文字列で表示される。**</span>
エスケープする。
↓
htmlタグなどを特殊文字に置き換える。特殊文字に置き換えることによって、画面上にhtmlタグを文字列として出力できる(画面上にはhtmlタグが見えてるけど、そのhtmlタグは文字列として認識されている。そのhtmlに意味はない)。
エスケープしない。
↓
htmlをダイレクトに受け取る。htmlとして受け取るので、画面上でhtmlが実行される。
### SQLインジェクション
アプリケーションが想定しないSQL文を実行させることにより、データベースシステムを不正に操作する攻撃方法。
Railsではクエリメソッドに対して、ハッシュ形式で条件指定をすることで安全に処理をしてくれる。
```ruby
users = User.where(name: params[:name])
```
実行したい内容がハッシュ形式で記述できない場合(クエリメソッドにSQL文を直接記述したい場合)は文字列を直接埋め込むのではなくて、**プレースホルダー**を利用する。
```ruby
users = User.where(`name = ?`, params[:name])
```
params[:name]が`?`に代入されるイメージ。その際にエスケープ処理を行ってくれる。
### SQL: WHERE 1 の意味
```ruby=
#SELECT * FROM table_name;と同じ意味になる
#つまり、以下のWhere以降はTrueになる条件式になる。
SELECT * FROM table_name WHERE 1
SELECT * FROM table_name WHERE 1 = 1
```
これは、
- WHERE 1 →MySQLでは整数の1はTRUEと等価である。
- WHERE 1 = 1 →(1 = 1) を評価すると当然TRUEなので WHERE 1とWHERE 1 = 1は全く同じ結果となる。
つまり、WHEREで指定する条件が必ず真であるならば、すべての行が選択されるので、WHERE を指定しない、SELECT * FROM table_name;と結果は同じになる。
### エスケープ処理を行わず(スキップして)出力する方法
Railsでは、HTML特殊文字は自動的にエスケープされるようになりました。自動でエスケープ処理が行われのは便利なのですが、エスケープ処理を行いたくない場合もあります。<span style="color: red; ">文字列ではなく、HTMLやJSタグとしてつまりコードとして認識させたい場合に**エスケープをスキップする**処理を行う。</span>
例)エスケープされない→ htmlをダイレクトに受け取る。htmlとして受け取るので、htmlが実行される。
#### ①Stringクラスで用意されているhtml_safeメソッドを用いる方法
ただし、nil には html_safe メソッドが存在しないため、例外が起きてしまう。
```ruby=
文字列.html_safe
```
#### ②ActionView::Helpers::OutputSafetyHelperクラスで用意されているrawメソッドを用いる方法
この方法では、nilの例外が起きない。また、raw ヘルパーを使うことで、エスケープ処理の回避が明確になるのも利点の一つ。
```ruby=
#rawメソッドを使うことで引数に指定された文字列はエスケープ処理がされずに出力される
raw(文字列)
```
#### ③Slimの==を用いる方法
この方法だと、nil の例外は起きない。また、エスケープ処理の回避が明確になる。
```ruby=
#=(単一等号)はescape_htmlメソッドによるHTMLエスケープが行われます
div = "<a href='https://google.com'>Google</a>"
#==(二重等号)を用いて、HTMLエスケープを行いたくない場合
div == "<a href='https://google.com'>Google</a>"
```
#### まとめ
Slimの==を用いる方法が一番良いとされる方法である。
### Sanitize
ある程度タグをそのまま出したいものの危険なタグは出力しないようにしたい場合に用いる。ホワイトリスト形式で制限する。ホワイトリストフィルタでは特定の値のみが許可され、それ以外の値はすべて拒否されます。
**sanitize(サニタイズ)とは**
危険なコードやデータを変換または除去して無力化する処理です。
```ruby=
sanitize(文字列 [, オプション])
```
#### オプション
:tags - 許可するHTMLタグ名
:attributes - 許可するHTML属性名
:scrubber - スクラブ指定
scrubberはより自由に許可するタグや属性をカスタマイズできる。
## 参照
HTML特殊文字のエスケープ
https://www.javadrive.jp/rails/template/index7.html
【Ruby】array.map(&:method)を理解する
https://qiita.com/k-penguin-sato/items/420d7487b28b5d58cac4
SQLデータ分析入門#3『WHERE句を理解する』
https://qiita.com/piyoSakai/items/44bc21178349103e2d45
エラーの向こうへ SQL:WHERE 1 の意味
https://tech.mktime.com/entry/32
大ちゃんの駆け出し技術ブログ
https://sakitadaiki.hatenablog.com/entry/2021/02/04/082402
Pikawaka 【Rails】 whereメソッドを使って欲しいデータの取得をしよう!
https://pikawaka.com/rails/where
クロスサイトスクリプティング
https://www.tohoho-web.com/ex/xss.html
rawヘルパー
https://railsdoc.com/page/raw
ビューについて -Rails ドキュメント-の結果をエスケープしないで出力
https://railsdoc.com/view
RailsのHTMLテンプレートエンジン、Slimの基本的な記法
https://qiita.com/mom0tomo/items/999f806d083569529f81
サニタイズ
https://railsdoc.com/page/sanitize
html_safe、raw、「<%==」の比較
https://qiita.com/iwamot/items/74c2bd9ebd3ac6458837
おもしろwebサービス開発日記 -h以外のサニタイズ系メソッド-
https://blog.willnet.in/entry/20080826/1219760222
Sanitize
https://api.rubyonrails.org/classes/ActionView/Helpers/SanitizeHelper.html
## 2021年8月25日 現場Rails Chapter6-7-3 インジェクションに注意する(Rubyコードインジェクションから)
### sendとevalってなに??
**send**メソッドは、引数で渡された文字列のメソッドを呼び出して実行します。
しかし、渡される引数に危険な値が渡される可能性があるため、条件分岐させてホワイトリスト形式で特定の文字列のみ許可してあげる方が安全です。
(ホワイトリスト方式は原則許可せず、許可するリストをこちらで指定するため、ブラックリスト方式よりも原則漏れが起きづらいという特徴があります。)
sendメソッドは動的なメソッド呼び出しができて便利だが、セキュリティ面を考慮し、**外部の入力に依存する値は渡さない**(特に、ユーザーからの入力をそのままsendに引き渡すと、意図せぬ範囲のデータやプライベートメソッドにまで影響が及ぶ可能性があるため)ようにします。
```ruby=
#sendメソッド
#第二引数で呼び出したメソッドに与える引数を渡しています。
send("メソッド名", "引数")
```
**eval**メソッドも、第一引数で文字列を渡すことができますが、渡された文字列のコードをそのまま実行することができます。しかし、sendメソッド同様に悪意のあるコードを渡されて、プログラムを実行される恐れがあります。
```ruby=
#evalメソッド
eval('rubyコード')
```
### sendの使用例 チェリー本P420
``` ruby=
str = 'a,b,c'
# str.upcaseを呼ぶのと同じ
str.send('upcase') #=> "A,B,C"
# str.split(',')を呼ぶのと同じ
str.send('split',',') #=> ["a","b","c"]
```
### evalの使用例 チェリー本P420
``` ruby=
# 文字列としてRubyのコードを記述する
code = '[1,2,3].map { |in| n * 10 }'
#evalメソッドに渡すと、文字列がRubyのコードとして実行される
eval(code) #=> [10,20,30]
```
eval攻撃例(予想)
`eval(params[:hoge])`
上記のparams[:hoge]に、「データベースのUsersテーブルにあるメールアドレスを1行ずつfuga変数に配列として代入し、その後fuga変数を外部(悪意を持った者のメールアドレスetc)に送信する」みたいな記述を渡してしまうと、それが実行されてしまう危険性がある。
```ruby=
# ユーザー全件削除
params[:hoge] = User.destroy_all
eval(params[:hoge])
```
大事なことは、**ユーザーから渡された生の値をそのまま使わない**ことです。
sendメソッドやevalメソッドを使う場合、ストロングパラメータのような、**ユーザーから渡された値を評価する仕組み**を作る必要があると思われます。
### コマンドラインインジェクション
コントローラーにコマンドライン(例: mkdir hogefuga)などを組み込んだ際に注意する必要があります。
`@group = mkdir #{params[:input]}` (←使用イメージ)
(params[:input]で送られた値のファイルを作成及びそれを@groupに代入)
バッククォートで囲まれた文字列はコマンドとして出力されます。
```ruby=
`date`
%x{ date }
```
> バッククォートで囲まれた文字列は、ダブルクォートで囲まれた文字列と同様にバックスラッシュ記法 の解釈と式展開 が行なわれた後、コマンドとして実行され、その標準出力が文字列として与えられます。コマンドは評価されるたびに実行されます。コマンドの終了ステータスを得るには、$? を参照します。
%記法 による別形式のコマンド出力もあります。

**考え方はストロングパラメータの時と同じで良いと思います。生の値をそのまま使うのはご法度です。**
### Content Security Policy(CSP)
HTTPリクエスト⇔HTTPレスポンスのやり取りの中で、約束事をホワイトリスト形式でブラウザに伝えるイメージです。
> クロスサイトスクリプティング (XSS) やデータインジェクション攻撃などのような、特定の種類の攻撃を検知し、影響を軽減するために追加できるセキュリティレイヤーです。CSP を有効にするには、ウェブサーバーから Content-Security-Policy HTTP ヘッダーを返すように設定する必要があります。
CSPの適用前に通信のコネクション自体確立していて、データ送受信(HTTPリクエストを送信する)段階で、ヘッダーにContent-Security-Policyを組み込むことにより攻撃の軽減(想定しないユーザーのパケット傍受などを防ぐ)を目指しています。
#### CSPを適用すると、、、
> CSP の第一の目的は XSS 攻撃の軽減と報告です。 XSS 攻撃とは、サーバーから取得したコンテンツをブラウザーが信頼する性質を悪用した攻撃です。サーバー管理者が CSP を利用する場合、実行を許可するスクリプトの正しいドメインをブラウザーに向けて指定することにより、 XSS の発生する箇所を削減・根絶することができます。 CSP をサポートするブラウザーは、サーバーから指定された許可リストに載っているドメインのスクリプトのみ実行し、他のスクリプトはすべて無視します。
#### CSP適用方法
メッセージヘッダーにcontent-security-policyが追加されるイメージ

```ruby=
#CSPの適用
#config/initializers/content_security_policy.rbに記述
Rails.application.config.content_security_policy do |policy|
policy.default_src :self, :https # サイト自身のドメインとHTTPSを使用した全ドメインを許可する
policy.style_src :self, :https, :unsafe_inline # インラインの<style>要素を許可(危険)
...
end
```
```ruby=
# コントローラで設定をカスタマイズするには、content_security_policyメソッドを使用
class ArticlesController < ApplicationController
content_security_policy do |policy|
policy.upgrade_insecure_requests true # httpをhttpsへリダイレクト(対応ブラウザの場合)
end
end
```
**※ドメイン** = インターネット上に存在するコンピュータやネットワークを識別するためのコンピュータの名前のことを言います。ネットワーク上の住所を表すイメージです。ドメイン名は重複することない一意な名前になるように管理されています。
### ディレクティブとは
ディレクティブとは、和訳すると**指示文**です。
コンピュータプログラムのソースコードに記述される要素の一つで、そのコードを解釈・変換するソフトウェア(コンパイラやプリプロセッサなど)への指示や指定などを与えるためのものです。
```ruby=
#ディレクティブの構文例
#このように<%@ %> で囲い、ページの先頭に記述します。
<%@ ディレクティブ名 属性名1="値1" 属性名2="値2" ... %>
```
## 参照
【Ruby on Rails】sendメソッドのいろんな書き方
https://qiita.com/ngron/items/05d3a9624c2c3ec5dbb6
Kernel.#eval公式
https://docs.ruby-lang.org/ja/latest/method/Kernel/m/eval.html
exitメソッド
https://techacademy.jp/magazine/20564
rubyで使うリテラル
https://docs.ruby-lang.org/ja/latest/doc/spec=2fliteral.html
Rubyのsendメソッドを使用するメリットと注意点
https://qiita.com/hogucc/items/79da134520ee731fe298
Linuxコマンドを連続して実行する
https://webkaru.net/linux/execute-combine-multiple-commands/
git logのオプション
https://atmarkit.itmedia.co.jp/ait/articles/2004/09/news018.html
パケットとは
https://www.otsuka-shokai.co.jp/words/packet.html
Ruby on Rails 5.2の新機能(Active Storage, Content Security Policyなど)
https://qiita.com/ryohashimoto/items/75f48fa5a3846c58f735
コンテンツセキュリティポリシー (CSP)
https://developer.mozilla.org/ja/docs/Web/HTTP/CSP
ディレクティブとは
https://www.javadrive.jp/servlet/jsp_directive/index1.html
## 2021年8月26日 現場Rails Chapter6-8 アセットパイプライン(その①)
**アセットパイプライン**とは、gem `sprockets-rails`を活用して、アセット(CSS, JavaScript, 画像etc)を効率的にまとめた上で、軽量化・高速化・変更反映化を実現してくれます。流れとしては以下の順で行います。
1. 高級言語(CoffeeScript,ERB,Slimなど)のコンパイル
2. アセットの連結(複数のjsファイルをapplication.jsにまとめる)
3. アセットの最小化(スペース、改行などを削除してファイルを最小化する)
4. ダイジェストの付与(コードの内容からハッシュ値をファイル名の末尾に付与)
*ダイジェスト:`application-xxxxx.js`の「xxxxx」部分。
アセットに変更があると、ダイジェスト値も変化→ブラウザに残っているダイジェスト値と比較→再読込を実行するかを判定します。
## 環境により挙動が変わる
development環境とproduction環境で、それぞれの目的に応じた挙動をします。
### development環境
- 高級言語のコンパイルとダイジェストの付与は自動で行われます。
- <span style="color: red; ">**アセットの連結と最小化はしません**</span>。デバッグのしやすさを考慮したためです。
development環境下で**デバッグモードをオフ**にする際は、以下の設定をする必要があります。
```ruby=
config.assets.debug = false
```
### production環境
- 高級言語のコンパイル・アセットの連結・アセットの最小化・ダイジェストの付与**全て行われます**。
### マニフェストファイル(/application.拡張子)
マニフェストファイルには、個別に分類した.CSSや.JSファイルをどのように連結するのかを**ディレクティブ**(指示文)として記述しています。CSSやJSなどアセットごとに**マニュフェストファイル**が作られます。また、**アセットパイプライン実行後の連結結果**もマニュフェストファイルに記述されます。
```ruby=
app/assets/javascript/application.js
app/assets/stylesheets/application.css
```
### マニフェストファイルの書き方
application.jsファイル上では、`//=`で始まる行をディレクティブ(指示文)として扱います。ディレクティブに指定したファイルは**マニフェストファイル**の位置からは見当たらないが、Sprocketsのアセットファイルにアクセスするためのパスを管理する(**アセットパス**)機能を使って、パスを検索しています。
```ruby=
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require_tree .
#「require_tree.」は指定されたディレクトリ配下のの全ファイルの内容を結合します。
#「.」を指定していると、application.jsが配置されているディレクトリ配下が対象となります。
```
appplication.**scss**ファイル上では、`@import`を使ってファイルを読み込ませます。
```ruby=
@import "bootstrap";
@import "tasks";
```
## 参照
Railsガイド アセットパイプライン
https://railsguides.jp/asset_pipeline.html
Railsガイド アセットパイプライン -デバッグをオフにする-
https://railsguides.jp/asset_pipeline.html#%E3%83%87%E3%83%90%E3%83%83%E3%82%B0%E3%82%92%E3%82%AA%E3%83%95%E3%81%AB%E3%81%99%E3%82%8B
## 2021年8月27日 現場Rails Chapter6-8 アセットパイプライン(その②)
### 探索パスについて
マニフェストファイルがファイルを探しに行くためのパスを**探索パス**と言います。
マニフェストファイルは**デフォルト**で以下のパスを探しに行きます。
- app/assets/*
- lib/assets/*
- vendor/assets/*
上記以外のパスを参照したい場合、**config/initializers/assets.rb**に以下の1文を記述します。この1文を記述することで、`node_modules`ディレクトリを探索パスとして設定できます。
```ruby=
Rails.application.ocnfig.assets.paths << Rails.root.join('node_modules')
```
コンソール上で探索パスを確認するには、
```ruby=
puts Rails.application.ocnfig.assets.paths
```
で確認することができます。
### アセットの設定
**config/initializers/assets.rb**
=全ての環境に共通する設定を記述するファイルです。
```ruby=
#config/initializers/assets.rb内に記述
#defaultで設定されているもの
#アセットのバージョンを設定する※
Rails.application.config.assets.version = '1.0'
#検索パスにnode_moduleを追加
Rails.application.config.assets.paths << Rails.root.join('node_modules')
#プリコンパイルするファイルを設定
Rails.application.config.assets.precompile += %w( admin.js admin.css )
マニフェストファイル以外でプリコンパイルするファイルを配列で設定
```
**config/environments/***
=環境別の設定をconfig/environments以下のファイルに記述します。**development**環境と**production**環境の違いもここに記述します。
```ruby=
#config/environments/以下に記述
#デバックモードで動作する(アセットの連結や前処理を行わない)
#defaultでdevelopment環境でのみ有効
config.assets.debug = true
#アセットへのリクエストに関するログを減らす。(処理を早くするため)
#defalutではdevelopment環境で有効
config.assets.quiet = true
#JSとCSSの圧縮方法
#defaultではproduction環境でのみ設定されている
config.aseets.js_compressor = :uglifier
config.assets.css_compressor = :sass
#プリコンパイル済みのアセットが存在しないときに、自動でコンパイルを行わない
config.assets.compile = false
#アセットファイルを配信するサーバーを設定
config.action_controller.aseet_host = 'サーバーURL'
```
※プリコンパイル=前処理(preprocess)を行うプログラムのことで、コンパイラの前処理を行います。
#### **アセットのバージョンを指定するとは?**
コンパイル時のダイジェストの生成をする(ファイル名の末尾にハッシュを付与する)フェーズで特定の文字列の情報を持たせることができます。そうすることで、バージョン管理の概念を持たせることができます。以下のように**config/initializers/assets.rb**の中に記載することができます。
```ruby=
Rails.application.config.assets.version = '1.0'
```
バージョンを変更すると、変更前のアセットは使用できなくなることに注意します。ただ、あくまで、ハッシュ情報に文字列を持たせているだけなので、文字列が一致すれば使用することはできます。
## 参照
Rails Guide Configuring Rails Applications
https://guides.rubyonrails.org/configuring.html
What does config.assets.version do in Rails? - stack overflow
https://stackoverflow.com/questions/13873176/what-does-config-assets-version-do-in-rails
アセットのバージョンを設定する
https://railsguides.jp/asset_pipeline.html#%E5%8F%A4%E3%81%84%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E3%81%AErails%E3%81%8B%E3%82%89%E3%82%A2%E3%83%83%E3%83%97%E3%82%B0%E3%83%AC%E3%83%BC%E3%83%89%E3%81%99%E3%82%8B
アセットパイプライン動的コンパイル
https://railsguides.jp/asset_pipeline.html#%E5%8B%95%E7%9A%84%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB
## 2021年8月28日(土)現場Rails Chapter6-9 production環境でアプリケーションを立ち上げる
### プリコンパイルとは
Railsにおける**プリコンパイル**とは、あらかじめ**アセットパイプライン**を実行して**静的ファイル**を生成しておくことを指します。
プリコンパイルされたファイルは**public/assets**ディレクトリの配下に配置されます。
上記における、「静的ファイル」と「プリコンパイルされたファイル」は同じで、**application-asdkljahsoiueraqwrufga8923479q37f89qw4.js**のようなファイル名になります。
```ruby
#プリコンパイルする
$ bin/rails assets:precompile
#プリコンパイル後に、public/aseets/配下のフォルダにプリコンパイルされたファイルが生成される。フォルダ内のファイルを確認。
$ ls public/assets/application-asdkljahsoiueraqwrufga8923479q37f89qw4.js
```
例)
100m走を行う場合、スタートの合図と同時に走り出せるように、着替えて靴をはいてスタートする姿勢までの準備をしておくのがプリコンパイルによるpublic配下にファイルが移動した状態です。しかし、これをしていないとスタートの合図がなってから着替えて靴を履くという準備が始まってしまう為に初動が遅れる事態になるから、本番環境ではプリコンパイルを行う必要があると考えられます。
開発環境ではデバッグのしやすさを考慮してプリコンパイルは行われません。
### 開発環境と本番環境におけるプリコンパイルの挙動の違い
開発環境ではコンパイル時にデフォルトでアセットの連結と最小化を行いません。これをサッカーの試合を例えると、ユニフォームを着ることで観客が視認出来るように配慮しますが、練習(開発環境)では背番号をつける必要は特にありません。汚れたら洗濯(デバッグ)する必要もでてきますし、着替える手間もあります。
プリコンパイルの実行は試合(本番環境)に備えてユニフォームを用意してコートに立つ準備を行うことを指します。
```ruby=
# trueにするとAssetPipeLineが常に実行されるので、リクエストのたびに毎回毎回静的ファイルを生成してしまう。
# 開発時には便利だがメモリを余分に消費したり、パフォーマンスが低下してしまう。
config.assets.compile = false
```
## 参照
「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
https://wa3.i-3-i.info/word16767.html
A visual guide to Webpacker
https://rossta.net/blog/visual-guide-to-webpacker.html
アセットのプリコンパイル
https://railsguides.jp/asset_pipeline.html#%E3%82%A2%E3%82%BB%E3%83%83%E3%83%88%E3%82%92%E3%83%97%E3%83%AA%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB%E3%81%99%E3%82%8B
## 2021年8月30日(月)現場Rails Chapter6-9-2 静的ファイルのサーバーを設定する、秘密情報の管理
### 動的なファイルと静的なファイル
**assets**配下のファイルは**動的ファイル**として扱われ、アセットパイプラインを通して静的なファイルへとコンパイルされ、publicディレクトリの配下に生成されます。
404や500のエラーページは、もともと**静的ファイル**として**public**配下に置かれています。アセットパイプラインでは、**動画コンテンツ**を読み込まないので、動画は**public**配下に設置します。
Railsのコントローラーのアクションを通じて、viewを表示する場合、全て、**動的ページ**になります。コントローラーのアクションで処理されて作られるコンテンツが動的ページです。
例)見た目上では、同じ"Hello World"と表示されているページでも毎回コントローラーのアクションが同じ動的ページを毎回作って配信しているイメージ。
対して、**アクション**を経由せず、直接**webサーバー**から受け取るコンテンツは**静的ページ**になります。そのため、public配下に静的ページを設置しておく必要があります。
publicに配置されているものは、**静的ページ**と捉えて良いです。

静的とは、ボールを投げたら壁に当たって跳ね返ってくる様子で、動的のイメージはキャッチボールのイメージです。
## 秘密情報の管理
秘密情報はRails5.2から**Credentials**という仕組みで管理されています。
管理したい秘密情報は**yaml形式**で記述し**credentials.yml.enc**の形で管理されます。それにアクセスするための鍵となるのが**master.key**です。
これらは**rails new**したタイミングで自動生成されます。
production環境用の秘密情報は**credentials.yml.enc**に常に暗号化された状態で保存されます。
**master.keyファイル**か**RAILS_MASTER_KEY 環境変数**からキー情報を取り出して秘密情報を復号して利用します。
暗号化された内容はコマンドを実行して閲覧したり、編集することが出来ます。
**master.key**はrailsアプリ作成時に「**.gitignore**」ファイルに記述済みとなっています。「.gitignore」ファイルに、Githubにアップロードしたくないファイルの名前を書くことで、Githubにアップロードされません。「ignore」は英単語で「無視する」という意味なので、「.gitignore」ファイルがこのような機能になります。
```ruby=
#Credentialファイルの中身をCatコマンドで見てみる
$ cat config/credentials.yml.enc
Q+GBLmbxagoirjyHWkJDdaMdg0cJR...#=>暗号化された状態で、秘密情報が保存されている
#ただ、中身を見るだけだと、暗号化された文字列の羅列が見えるだけで何が書いてあるかわからないので、
#credentialファイルの中身を復号して閲覧する
$ bin/rails credentials:show
#Credentialsファイルの編集
#credentials.yml.encの内容は暗号化されてるため、エディタなどで直接ファイルを開いて編集することはできません。
#コマンドでエディターを指定して、編集する
#エディターにviを利用して、credentials.yml.encを編集する場合
$ EDITOR="vi" bin/rails credentials:edit
#編集後のファイルを保存すると、再び暗号化して、config/credentials.yml.encとして保存される
#=>New credentials encrypted and saved!というメッセージが表示される
#credentials.yml.encファイルに設定された秘密情報の読み取り方法
#credentials.yml.encファイルのawsのaccess_key_idをキーとする値を取得
irb(main):001:0>Rails.application.credentials.aws[:access_key_id]
=> 123
```
## 参考
Rails5.2から追加された credentials.yml.enc のキホン
https://qiita.com/NaokiIshimura/items/2a179f2ab910992c4d39
## 2021年8月31日(火)現場Rails Chapter7
## 7-1-1
## f.hidden_field
f.hidden_fieldとは、画面に表示されないinputタグを生成するメソッドです。このinputタグには値が保持されています。既に決まった値をユーザーから見えない形で送信する際に使います。f.hidden_fieldの代表的な使用例は、確認画面のフォームです。
↓Rails: How do I use hidden_field in a form_for?の引用
- f.hidden_field
```ruby=
<%= f.hidden_field :some_column %>
```
- 上のf.hidden_fieldで生成されるHTML
```ruby=
<input type="hidden" id="request_some_column" name="request[some_column]" value="#{@request.some_column}" />
```
### valid?
>バリデーションが実行された結果
```ruby=
@task.valid? # =>false
@task.errors # =>バリデーションにはじかれた内容が入る
@task.errors.full_messages # =>エラーメッセージを文章化し、配列で表現
```
valid?メソッドで、検証が通るか通らないかを確かめています。
```ruby=
true→検証が通る
false→検証が通らない
```
#### ↓現場で使えるRuby on Rails P287引用
```ruby=
Rails.application.routes.draw do
root to: 'tasks#index'
resources :tasks do
post :confirm, action: :comfirm_new, on: :new
end
end
```
基本的にonオプションはmemberやcollectionなどをresourecesの配下などでネストさせた場合にブロック(do ~ end)の省略をするときに使うものですが、今回におけるonオプションはnewアクションに続くURLとして"tasks/new/confirm"のような形のURLの形式にしたいために、postメソッドの引数で:confirmを明示的に指定します。
## 参照
<INPUT type="hidden">
http://www.htmq.com/html/input_hidden.shtml
Rails: How do I use hidden_field in a form_for?
https://stackoverflow.com/questions/3131982/rails-how-do-i-use-hidden-field-in-a-form-for