結論
- html_safeを使用するのであれば、xss対策としてhメソッドを使用する必要がある。
text = "テスト\nテスト"
<%= h(text).gsub(/\n/, "<br>").html_safe %>
経緯
- devise.ja.ymlで定義した文章を改行するタスクがあり、それを実現するために、以下の実装を行った。
- devise.ja.ymlに改行コード(\n)を埋め込む
- html_safeを使用して改行を実現
よし。これで改行を表現することができた。よろしくおねがいしまーす。タンッ!
その後、プルリクのコメントに「xss対策をしてください。」の文字。
・・・?
xss(クロスサイトスクリプティング)
- 脆弱性の一種
- 第三者がWebページにスクリプトを自由に組み込める状態のこと
- スクリプト上で意図していないスクリプトを埋め込まれ、情報を盗み取られる危険性がある
自分が実装した内容だとscriptタグを埋め込まれて情報を盗み取られる危険がある実装をしていたということになります。原因と対策を調べました。
原因
html_safeを使用したときに発生する脆弱性を把握していなかったこと
- 今回、改行を表現するためにhtml_safeを使用しました。これで、文字列に改行文字を埋め込むと、改行コードが反映されるようになります。
# index.html.erb
text = "テスト<br>テスト"
<% text %>
#=>
テスト テスト
<% text.html_safe %>
#=>
テスト
テスト
# 埋め込んだ<br>が適応され、改行されて表示される
しかし、このメソッドは、エスケープ処理を行わないようにするメソッドのため、エスケープ処理を自分で実施する必要がありました。それを怠っていたということになります。
今回は、エスケープ処理の動作と対策を確認すべく、scriptタグを文字列に埋め込み、alertが表示されないこと = 対策ができているとして調べました。
調査
- 今回の目的は、テキストを改行させることでした。しかし、それを実現するためにhtml_safeを使用すると、脆弱性が発生してしまう。なので、html_safeを使用して改行を実現しつつ、脆弱性対策も実施する方法を探りました。
hメソッド
- エスケープ処理を実装してくれるメソッドで、特別な意味を持つ記号を、別の文字列に変換してくれます。挙動は以下の通り。
> text
=> "テスト\nテスト<script>alert(`test`)</script>"
> h(text)
=> "テスト\nテスト<script>alert(`test`)</script>"
上記のようになります。これにより、文字として扱うことになるのでscriptタグを無効化することができます。
hメソッドを使用して、XSS対策を行った結果が以下の通りになります。
> rails c
> text
=> "テスト\nテスト<script>alert(`test`)</script>"
> text.gsub(/\n/, "<br>")
=> "テスト<br>テスト<script>alert(`test`)</script>"
> h(text).gsub(/\n/, "<br>")
=> "テスト<br>テスト<script>alert(`test`)</script>"
> h(text).gsub(/\n/, "<br>").html_safe
=> "テスト<br>テスト<script>alert(`test`)</script>"
これで、改行を実現しつつxss対策も行うことができました!
index.html.erb
<%# pタグに普通に埋め込んだ場合: alertする%>
<% text = "テスト\nテスト<script>alert(`test`)</script>" %>
<br>
<%# そのまま出力 = 改行✗ エスケープ○ %>
<h3>1.text</h3>
<%= text %>
<%# html_safe = 改行✗ エスケープ✗ %>
<h3>2.text + html_safe</h3>
<%= text.html_safe %>
<%# gsub = 改行✗ エスケープ○ %>
<h3>3.text + gsub</h3>
<%= text.gsub(/\n/, "<br>")%>
<%# gsub + html_safe = 改行○ エスケープ✗ %>
<h3>4.text + gsub + html_safe</h3>
<%= text.gsub(/\n/, "<br>").html_safe%>
<%# gsub + html_safe + hメソッド = 改行○ エスケープ○ %>
<h3>5.text + gsub + html_safe + hメソッド</h3>
<%= h(text).gsub(/\n/, "<br>").html_safe %>
erbに埋め込んで1つずつ確認すると、alertが表示されるパターン、されないパターンを確認することができました。
スクリーンショット
動作確認として、以下の2点をチェックしました。
- scriptタグが文字として表示されている = エスケープ処理がされている
- scriptタグが表示されていない = エスケープ処理がされていない
エスケープ処理がされていない場合、アラートが表示されてしまいます。
実際に動かしてみることで、エスケープ処理の重要性を確認することができました。
まとめ
- Railsは自動的にエスケープ処理を実装行ってくれている。
- html_safeを使用することで、エスケープ処理が行われなくなり、脆弱性が発生する
- html_safeを使用した場合は、hメソッドでエスケープ処理を実装することで対策をする必要がある。