Sanitizeは、許可リストベースのHTMLおよびCSSサニタイザーです。許可することを選択した要素、属性、プロパティを除き、文字列からすべてのHTMLおよび/またはCSSを削除します。
シンプルな設定構文を使用して、Sanitizeに特定のHTML要素、それらの要素内の特定の属性、さらにはURLを含む属性内の特定のURLプロトコルを許可するよう指示できます。また、CSSを含む要素や属性内で特定のCSSプロパティ、@ルール、URLプロトコルを許可することもできます。明示的に許可していないHTMLやCSSはすべて削除されます。
Sanitizeは、現代のブラウザと同じ方法でHTMLを解析するNokogiri HTML5パーサーと、現代のブラウザと同じ方法でCSSを解析するCrassに基づいています。許可リストの設定が安全なマークアップとCSSのみを許可している限り、最も不正な形式や悪意のある入力であっても安全な出力に変換されます。
[!WARNING]
Sanitizeは
<math>または<svg>要素の内容を完全にサニタイズすることはできません。MathMLおよびSVG要素は、通常のHTML解析ルールに従わない外部要素です。デフォルトでは、SanitizeはすべてのMathMLおよびSVG要素を削除します。カスタム要素の許可リストにMathMLまたはSVG要素を追加すると、アプリケーションにセキュリティ脆弱性を作成する可能性があります。
gem install sanitize
require 'sanitize'
# Clean up an HTML fragment using Sanitize's permissive but safe Relaxed config.
# This also sanitizes any CSS in `<style>` elements or `style` attributes.
Sanitize.fragment(html, Sanitize::Config::RELAXED)
# Clean up an HTML document using the Relaxed config.
Sanitize.document(html, Sanitize::Config::RELAXED)
# Clean up a standalone CSS stylesheet using the Relaxed config.
Sanitize::CSS.stylesheet(css, Sanitize::Config::RELAXED)
# Clean up some CSS properties using the Relaxed config.
Sanitize::CSS.properties(css, Sanitize::Config::RELAXED)
Sanitize は以下の種類の入力をサニタイズできます:
<style> 要素内の CSS スタイルシートstyle 属性内の CSS プロパティ[!WARNING]
Sanitize は
<math>や<svg>要素の内容を完全にサニタイズすることはできません。MathML および SVG 要素は、通常の HTML パースルールに従わない外部要素です。デフォルトでは、Sanitize はすべての MathML および SVG 要素を削除します。カスタム要素の許可リストに MathML または SVG 要素を追加すると、アプリケーションにセキュリティ脆弱性を生み出す可能性があります。
フラグメントとは、ルートレベルの <html> 要素を含まない HTML のスニペットです。
設定オプションを何も指定しない場合、Sanitize はデフォルトで最も厳格な設定を使用します。つまり、すべての HTML を除去し、安全なテキストのみを残します。
html = '<b><a href="http://foo.com/">foo</a></b><img src="https://raw.githubusercontent.com/rgrove/sanitize/main/bar.jpg">'
Sanitize.fragment(html)
# => "foo"
特定の要素を保持するには、その要素を要素許可リストに追加します。
Sanitize.fragment(html, elements: ['b'])
# => "<b>foo</b>"
ドキュメントをサニタイズする際は、<html> 要素を許可リストに含める必要があります。また、:allow_doctype を true に設定すると、整形式の文書型定義を許可できます。
html = %[
<!DOCTYPE html>
<html>
<b><a href="http://foo.com/">foo</a></b><img src="https://raw.githubusercontent.com/rgrove/sanitize/main/bar.jpg">
</html>
]
Sanitize.document(html,
allow_doctype: true,
elements: ['html']
)
# => "<!DOCTYPE html><html>foo\n \n</html>"
HTML フラグメントまたはドキュメント内の CSS をサニタイズするには、まず <style> 要素および/または style 属性を許可リストに追加します。次に、許可したい CSS プロパティ、@ ルール、URL プロトコルを許可リストに追加します。また、CSS コメントやブラウザ互換ハックを許可するかどうかも選択できます。
html = %[
<style>
div { color: green; width: 1024px; }
</style>
<div style="height: 100px; width: 100px;"></div>
<p>hello!</p>
]
Sanitize.fragment(html,
elements: ['div', 'style'],
attributes: {'div' => ['style']},
css: {
properties: ['width']
}
)
#=> %[
# <style>
# div { width: 1024px; }
# </style>
#
# <div style=" width: 100px;"></div>
# hello!
# ]
Sanitizeは、HTMLパーサーを呼び出すことなく、スタンドアロンのCSSスタイルシートやプロパティ文字列を問題なくクリーンアップします。
[!WARNING] これは、スタンドアロンのCSSファイルで使用するCSSをサニタイズする場合にのみ使用してください。
<style>要素やstyle属性など、HTMLコンテキストで使用するCSSを安全にサニタイズするには、前のセクションで説明したように、適切な許可リストを指定してSanitize.document()またはSanitize.fragment()を使用する必要があります。そうしないと、HTMLインジェクション攻撃が可能になる場合があります。
css = %[
@import url(evil.css);
a { text-decoration: none; }
a:hover {
left: expression(alert('xss!'));
text-decoration: underline;
}
]
Sanitize::CSS.stylesheet(css, Sanitize::Config::RELAXED)
# => %[
#
#
# a { text-decoration: none; }
#
# a:hover {
#
# text-decoration: underline;
# }
# ]
Sanitize::CSS.properties(%[
left: expression(alert('xss!'));
text-decoration: underline;
], Sanitize::Config::RELAXED)
# => %[
#
# text-decoration: underline;
# ]
超安全なデフォルト設定に加えて、Sanitize にはそのまま使用できる、あるいはニーズに合わせて調整できる 3 つの組み込み設定が用意されています。
非常にシンプルなインラインマークアップのみを許可します。リンク、画像、ブロック要素は許可されません。
Sanitize.fragment(html, Sanitize::Config::RESTRICTED)
# => "<b>foo</b>"
書式設定要素、リンク、リストなど、さまざまなマークアップを許可します。
画像とテーブルは許可されず、リンクはFTP、HTTP、HTTPS、mailtoプロトコルに限定され、SEOスパムを軽減するためにすべてのリンクにrel="nofollow"属性が追加されます。
Sanitize.fragment(html, Sanitize::Config::BASIC)
# => '<b><a href="http://foo.com/" rel="nofollow">foo</a></b>'
画像やテーブル、安全な CSS を含む、さらに幅広い種類のマークアップを許可します。リンクは引き続き FTP、HTTP、HTTPS、mailto プロトコルに制限され、画像は HTTP と HTTPS に制限されます。このモードでは、リンクに rel="nofollow" は追加されません。
Sanitize.fragment(html, Sanitize::Config::RELAXED)
# => '<b><a href="http://foo.com/">foo</a></b><img src="https://raw.githubusercontent.com/rgrove/sanitize/main/bar.jpg">'
組み込みモードがニーズに合わない場合は、カスタム設定を簡単に指定できます:
Sanitize.fragment(html,
elements: ['a', 'span'],
attributes: {
'a' => ['href', 'title'],
'span' => ['class']
},
protocols: {
'a' => {'href' => ['http', 'https', 'mailto']}
}
)
Sanitizeの組み込み設定のいずれかから始めて、必要に応じてカスタマイズすることもできます。
組み込み設定は、ユーザーが(誤ってまたは悪意を持って)変更することを防ぐために深く凍結されています。組み込み設定をカスタマイズするには、次のように Sanitize::Config.merge() を使って新しいコピーを作成します:
# Create a customized copy of the Basic config, adding <div> and <table> to the
# existing allowlisted elements.
Sanitize.fragment(html, Sanitize::Config.merge(Sanitize::Config::BASIC,
elements: Sanitize::Config::BASIC[:elements] + ['div', 'table'],
remove_contents: true
))
上記の例では、Sanitize::Config::BASIC 内の既存の要素リストのコピーに <div> 要素と <table> 要素を追加しています。代わりに要素配列を独自のもので完全に上書きしたい場合は、+ 演算を省略できます:
# Overwrite :elements instead of creating a copy with new entries.
Sanitize.fragment(html, Sanitize::Config.merge(Sanitize::Config::BASIC,
elements: ['div', 'table'],
remove_contents: true
))
特定の要素に追加する属性。属性がすでに存在する場合、ここで指定した値に置き換えられます。すべての要素名と属性は小文字で指定してください。
add_attributes: {
'a' => {'rel' => 'nofollow'}
}
HTMLコメントを許可するかどうか。コメントを許可することは強く推奨されません。IEでは条件付きコメント内でのスクリプト実行が許可されるためです。デフォルト値は false です。
ドキュメントのサニタイズ時に、"<!DOCTYPE html>" のような整形式のHTML doctype宣言を許可するかどうか。この設定はフラグメントのサニタイズ時には無視されます。デフォルト値は false です。
特定の要素で許可する属性。すべての要素名と属性は小文字で指定してください。
attributes: {
'a' => ['href', 'title'],
'blockquote' => ['cite'],
'img' => ['alt', 'src', 'title']
}
すべての要素で特定の属性を許可したい場合は、要素名の代わりに記号 :all を使用してください。
# Allow the class attribute on all elements.
attributes: {
:all => ['class'],
'a' => ['href', 'title']
}
任意の HTML5 data-* 属性を許可するには、属性名の代わりに記号 :data を使用します。
# Allow arbitrary HTML5 data-* attributes on <div> elements.
attributes: {
'div' => [:data]
}
CSS をサニタイズする際(単体または HTML に埋め込まれた状態のいずれでも)に使用する、以下の CSS 設定のハッシュ。
CSS コメントを許可するかどうか。デフォルト値は false です。
IE の * や _ ハックなど、ブラウザ互換性のためのハックを許可するかどうか。これらは一般に無害ですが、技術的には無効な CSS になります。デフォルトは false です。