
Sanitize는 허용 목록 기반의 HTML 및 CSS 새니타이저입니다. 문자열에서 사용자가 허용하도록 선택한 요소, 속성 및 프로퍼티를 제외한 모든 HTML 및/또는 CSS를 제거합니다.
간단한 구성 구문을 사용하여 Sanitize에 특정 HTML 요소, 해당 요소 내의 특정 속성, 그리고 URL을 포함하는 속성 내의 특정 URL 프로토콜을 허용하도록 지시할 수 있습니다. 또한 CSS를 포함하는 요소 또는 속성에서 특정 CSS 프로퍼티, @ 규칙 및 URL 프로토콜을 허용할 수 있습니다. 명시적으로 허용하지 않은 HTML 또는 CSS는 모두 제거됩니다.
Sanitize는 최신 브라우저와 동일한 방식으로 HTML을 파싱하는 Nokogiri HTML5 파서와 최신 브라우저와 동일한 방식으로 CSS를 파싱하는 Crass를 기반으로 합니다. 허용 목록 구성이 안전한 마크업과 CSS만 허용하는 한, 가장 잘못 구성되었거나 악의적인 입력이라도 안전한 출력으로 변환됩니다.
gem install sanitize
## 빠른 시작```ruby
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을 제거하고 안전한 텍스트만 남긴다는 의미입니다.```ruby
html = 'foo
'
Sanitize.fragment(html)
특정 요소를 유지하려면 해당 요소를 요소 허용 목록(allowlist)에 추가하십시오.```ruby
Sanitize.fragment(html, elements: ['b'])
# => "<b>foo</b>"
문서를 살균(sanitize)할 때 <html> 요소를 허용 목록에 추가해야 합니다. 또한 :allow_doctype을 true로 설정하면 올바른 형식의 문서 유형 정의를 허용할 수 있습니다.```ruby
html = %[
]
Sanitize.document(html, allow_doctype: true, elements: ['html'] )
### HTML의 CSS
HTML 조각 또는 문서에서 CSS를 정화하려면 먼저 `<style>` 요소 및/또는 `style` 속성을 허용 목록에 추가하세요. 그런 다음 허용하려는 CSS 속성, @ 규칙 및 URL 프로토콜을 허용 목록에 추가하세요. CSS 주석이나 브라우저 호환성 핵(hack)을 허용할지 여부도 선택할 수 있습니다.```ruby
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 주입 공격이 가능해질 수 있습니다.```ruby 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)
Sanitize::CSS.properties(%[ left: expression(alert('xss!')); text-decoration: underline; ], Sanitize::Config::RELAXED)
## Configuration
초고안전 기본 설정 외에도 Sanitize에는 즉시 사용하거나 필요에 맞게 조정할 수 있는 세 가지 기본 제공 구성이 포함되어 있습니다.
### Sanitize::Config::RESTRICTED
매우 간단한 인라인 마크업만 허용합니다. 링크, 이미지 또는 블록 요소는 허용되지 않습니다.```ruby
Sanitize.fragment(html, Sanitize::Config::RESTRICTED)
# => "<b>foo</b>"
서식 요소, 링크, 목록을 포함한 다양한 마크업을 허용합니다.
이미지와 표는 허용되지 않으며, 링크는 FTP, HTTP, HTTPS, mailto 프로토콜로 제한됩니다. 또한 SEO 스팸을 완화하기 위해 모든 링크에 rel="nofollow" 속성이 추가됩니다.```ruby
Sanitize.fragment(html, Sanitize::Config::BASIC)
### Sanitize::Config::RELAXED
이미지와 테이블은 물론 안전한 CSS를 포함하여 훨씬 더 다양한 마크업을 허용합니다. 링크는 여전히 FTP, HTTP, HTTPS 및 mailto 프로토콜로 제한되며, 이미지는 HTTP 및 HTTPS로 제한됩니다. 이 모드에서는 링크에 `rel="nofollow"`가 추가되지 않습니다.```ruby
Sanitize.fragment(html, Sanitize::Config::RELAXED)
# => '<b><a href="http://foo.com/">foo</a></b><img src="https://raw.githubusercontent.com/rgrove/sanitize/HEAD/bar.jpg">'
기본 제공 모드가 요구 사항을 충족하지 못하는 경우, 사용자 지정 구성을 쉽게 지정할 수 있습니다:```ruby Sanitize.fragment(html, elements: ['a', 'span'],
attributes: { 'a' => ['href', 'title'], 'span' => ['class'] },
protocols: { 'a' => {'href' => ['http', 'https', 'mailto']} } )
Sanitize의 내장 구성 중 하나로 시작한 다음 필요에 맞게 사용자 지정할 수도 있습니다.
내장 구성은 사용자가 (실수로든 악의적으로든) 수정하지 못하도록 깊게 동결되어 있습니다. 내장 구성을 사용자 지정하려면 `Sanitize::Config.merge()`를 사용하여 새 복사본을 만드세요. 예를 들어 다음과 같습니다:```ruby
# 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> 요소를 추가합니다. 대신 요소 배열을 자신만의 배열로 완전히 덮어쓰려면 + 연산을 생략할 수 있습니다:```ruby
Sanitize.fragment(html, Sanitize::Config.merge(Sanitize::Config::BASIC, elements: ['div', 'table'], remove_contents: true ))
### 구성 설정
#### :add_attributes (Hash)
특정 요소에 추가할 속성입니다. 속성이 이미 존재하는 경우 여기에 지정된 값으로 대체됩니다. 모든 요소 이름과 속성은 소문자로 지정하세요.```ruby
add_attributes: {
'a' => {'rel' => 'nofollow'}
}
HTML 주석을 허용할지 여부를 나타냅니다. 주석 허용은 강력히 권장되지 않습니다. IE는 조건부 주석 내에서 스크립트 실행을 허용하기 때문입니다. 기본값은 false입니다.
문서를 정화(sanitize)할 때 ""과 같은 올바른 형식의 HTML doctype 선언을 허용할지 여부를 나타냅니다. 이 설정은 프래그먼트를 정화할 때는 무시됩니다. 기본값은 false입니다.
특정 요소에서 허용할 속성. 모든 요소 이름과 속성은 소문자로 지정하세요.```ruby attributes: { 'a' => ['href', 'title'], 'blockquote' => ['cite'], 'img' => ['alt', 'src', 'title'] }
만약 모든 요소에 특정 속성을 허용하려면 요소 이름 대신 `:all` 기호를 사용하세요.```ruby
# Allow the class attribute on all elements.
attributes: {
:all => ['class'],
'a' => ['href', 'title']
}
임의의 HTML5 data-* 속성을 허용하려면 속성 이름 대신 :data 기호를 사용하십시오.```ruby
attributes: { 'div' => [:data] }
#### :css (Hash)
CSS를 sanitize할 때(독립형 또는 HTML에 포함된 경우 모두) 사용할 다음 CSS 구성 설정의 해시입니다.
##### :css => :allow_comments (boolean)
CSS 주석을 허용할지 여부입니다. 기본값은 `false`입니다.
##### :css => :allow_hacks (boolean)
IE `*` 및 `_` 해킹과 같은 브라우저 호환성 해킹을 허용할지 여부입니다. 이러한 해킹은 일반적으로 무해하지만, 기술적으로는 유효하지 않은 CSS가 됩니다. 기본값은 `false`입니다.
##### :css => :at_rules (Array or Set)
연결된 블록이 없을 수 있는 허용할 CSS [at-rules][at-rules]의 이름입니다. 예를 들어 `import` 또는 `charset`이 있습니다. 이름은 소문자로 지정해야 합니다.
[at-rules]:https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule
##### :css => :at_rules_with_properties (Array or Set)
CSS 속성을 포함하는 연관 블록을 가질 수 있는 허용할 CSS [at-rules][at-rules]의 이름입니다. `font-face` 및 `page`와 같은 at-rules가 이 범주에 속합니다. 이름은 소문자로 지정해야 합니다.
##### :css => :at_rules_with_styles (Array or Set)
스타일 규칙을 포함하는 연관 블록을 가질 수 있는 허용할 CSS [at-rules][at-rules]의 이름입니다. `media` 및 `keyframes`와 같은 at-rules가 이 범주에 속합니다. 이름은 소문자로 지정해야 합니다.
##### :css => :import_url_validator
이것은 `Proc`(또는 다른 호출 가능한 객체)이며, `@import` [at-rules][at-rules]에 지정된 URL을 전달받아 호출됩니다.
이를 사용하여 가져올 수 있는 항목을 제한할 수 있습니다. 예를 들어 `@import`를 Google Fonts URL로 제한하려면 다음과 같이 할 수 있습니다:```ruby
Proc.new { |url| url.start_with?("https://fonts.googleapis.com") }
허용할 CSS 속성 이름 목록입니다. 이름은 소문자로 지정해야 합니다.
CSS URL에서 허용할 URL 프로토콜입니다. 소문자로 지정해야 합니다.
프로토콜이 없는 상대 URL을 허용하려면 프로토콜 배열에 :relative 기호를 포함하세요.
허용할 HTML 요소 이름 배열입니다. 모든 이름을 소문자로 지정하세요. 이 배열에 없는 요소는 제거됩니다.```ruby elements: %w[ a abbr b blockquote br cite code dd dfn dl dt em i kbd li mark ol p pre q s samp small strike strong sub sup time u ul var ]
> [!WARNING]
>
> Sanitize는 `<math>` 또는 `<svg>` 요소의 내용을 완전히 정화할 수 없습니다. MathML 및 SVG 요소는 일반적인 HTML 파싱 규칙을 따르지 않는 [외부 요소](https://html.spec.whatwg.org/multipage/syntax.html#foreign-elements)입니다.
>
> 기본적으로 Sanitize는 모든 MathML 및 SVG 요소를 제거합니다. 사용자 지정 요소 허용 목록에 MathML 또는 SVG 요소를 추가하는 경우, 해당 요소 내부의 모든 콘텐츠가 허용된다고 가정해야 합니다. Sanitize가 다른 상황에서는 제거하거나 이스케이프했을 콘텐츠라도 말입니다. 이로 인해 애플리케이션에 보안 취약점이 발생할 수 있습니다.
> [!NOTE]
>
> Sanitize는 `noscript`가 허용 목록에 있더라도 항상 `<noscript>` 요소와 그 내용을 제거합니다.
>
> 이는 `<noscript>` 요소의 콘텐츠가 스크립팅 활성화 여부에 따라 브라우저에서 다르게 파싱되기 때문입니다. Nokogiri는 스크립팅을 지원하지 않으므로 항상 스크립팅이 비활성화된 것처럼 `<noscript>` 요소를 파싱합니다. 그 결과, Nokogiri가 스크립팅이 활성화된 브라우저의 파싱 동작을 완전히 재현할 수 없기 때문에 `<noscript>` 요소의 콘텐츠를 안정적으로 정화할 수 없는 엣지 케이스가 발생합니다.
#### :parser_options (Hash)
Nokogiri에 제공할 [파싱 옵션](https://nokogiri.org/tutorials/parsing_an_html5_document.html?h=parsing+options#parsing-options)입니다.```ruby
parser_options: {
max_errors: -1,
max_tree_depth: -1
}
특정 속성에서 허용할 URL 프로토콜입니다. 여기에 나열된 속성에 지정된 프로토콜 이외의 프로토콜이 포함된 경우(또는 프로토콜이 전혀 포함되지 않은 경우) 해당 속성은 제거됩니다.```ruby protocols: { 'a' => {'href' => ['ftp', 'http', 'https', 'mailto']}, 'img' => {'src' => ['http', 'https']} }
프로토콜이 없는 상대 URL의 사용을 허용하려면 protocol array에 `:relative` 기호를 포함하세요:```ruby
protocols: {
'a' => {'href' => ['http', 'https', :relative]}
}
이 값이 true이면 Sanitize는 허용 목록(allowlist)에 없는 요소 자체뿐만 아니라 해당 요소의 내용물도 제거합니다. 기본적으로 Sanitize는 요소가 제거될 때 요소 내용물 중 안전한 부분은 남겨 둡니다.
이 값이 요소 이름의 Array 또는 Set인 경우, 지정된 요소(필터링될 때)의 내용물만 제거되고, 그 외 필터링된 요소의 내용물은 남겨 둡니다.
기본값은 기본 구성에서 확인할 수 있습니다.
사용자 정의 HTML transformer 또는 transformer 배열입니다. 자세한 내용은 아래 Transformers 섹션을 참조하세요.
제거될 때 가독성을 유지하기 위해 내용물 주변에 공백을 넣어야 하는 요소 이름의 Hash입니다.
각 요소 이름은 키이며, 제거된 요소의 위치 :before와 :after에 삽입할 특정 공백을 제공하는 또 다른 Hash를 가리킵니다. :after 값은 제거된 요소에 자식이 있는 경우에만 삽입되며, 이 경우 해당 자식들 뒤에 삽입됩니다.```ruby
whitespace_elements: {
'br' => { before: "\n", after: "" },
'div' => { before: "\n", after: "\n" },
'p' => { before: "\n", after: "\n" }
}
The default elements with whitespace added before and after can be seen in [the default config](https://github.com/rgrove/sanitize/blob/HEAD/lib/sanitize/config/default.rb).
## Transformers
Transformer를 사용하면 Sanitize의 핵심 필터 위에(또는 대신하여) 자체 사용자 정의 논리로 HTML 노드를 필터링하고 수정할 수 있습니다. Transformer는 `call()`에 응답하는 모든 객체입니다(예: lambda 또는 proc).
하나 이상의 transformer를 사용하려면 `:transformers` 구성 설정에 전달하세요. 단일 transformer 또는 transformer 배열을 전달할 수 있습니다.```ruby
Sanitize.fragment(html, transformers: [
transformer_one,
transformer_two
])
각 transformer의 call() 메서드는 HTML의 각 노드(요소, 텍스트 노드, 주석 등 포함)에 대해 한 번씩 호출되며, 다음 항목을 포함하는 Hash를 인자로 받습니다:
:config - 현재 Sanitize 구성 Hash입니다.
:is_allowlisted - 현재 노드가 이전 transformer에 의해 허용 목록에 추가되었으면 true, 그렇지 않으면 false입니다. 이전 transformer가 허용 목록에 추가한 노드를 제거하는 것은 일반적으로 좋지 않은 방식입니다.
:node - HTML 노드를 나타내는 Nokogiri::XML::Node 객체입니다. 노드는 요소, 텍스트 노드, 주석, CDATA 노드 또는 문서 조각일 수 있습니다. Nokogiri의 검사 메서드(element?, text? 등)를 사용하여 관심 없는 노드 유형은 선택적으로 무시하십시오.
:node_allowlist - 이전 transformer에 의해 허용 목록에 추가된 현재 문서의 Nokogiri::XML::Node 객체 집합입니다(있는 경우). 이전 transformer가 허용 목록에 추가한 노드를 제거하는 것은 일반적으로 좋지 않은 방식입니다.
:node_name - 현재 HTML 노드의 이름이며, 항상 소문자입니다(예: "div" 또는 "span"). 요소가 아닌 노드의 경우 이름은 "text", "comment", "#cdata-section", "#document-fragment" 등과 같은 형식입니다.
transformer는 아무것도 반환하지 않아도 되지만, 선택적으로 Hash를 반환할 수 있으며, 다음 항목을 포함할 수 있습니다:
Nokogiri::XML::Node 객체의 배열 또는 집합입니다. 이 특정 노드와 해당 노드의 모든 속성은 허용 목록에 추가되지만, 해당 노드의 자식 노드는 허용 목록에 추가되지 않습니다.transformer가 Hash 이외의 값을 반환하면 반환 값은 무시됩니다.
각 transformer는 전달받은 Nokogiri::XML::Node와 노드의 document() 메서드를 통한 문서의 나머지 부분에 대한 전체 액세스 권한을 가집니다. 현재 노드나 문서에 대한 모든 변경 사항은 문서에 즉시 반영되며, 이후에 호출되는 transformer와 Sanitize 자체에도 전달됩니다. transformer는 필요에 따라 사용자 지정 삭제(sanitization)를 수행하기 위해 내부적으로 Sanitize를 호출할 수도 있습니다.
노드는 탐색되는 순서대로 transformer에 전달됩니다. Sanitize는 하향식(top-down) 탐색을 수행합니다. 즉, 노드는 HTML에서 읽는 순서와 동일하게 최상위 노드부터 시작하여 첫 번째 자식 노드 등으로 탐색됩니다.```ruby html = %[
foobar
]transformer = lambda do |env| puts env[:node_name] if env[:node].element? end
Sanitize.fragment(html, transformers: transformer)
트랜스포머는 엄청난 힘을 가지고 있으며, Sanitize의 내장 필터링을 완전히 우회할 수 있는 힘도 포함합니다. 주의하세요! 안전은 여러분의 손에 달려 있습니다.
### 예제: 도메인별 이미지 URL을 허용하는 트랜스포머
다음 예제는 상대 URL을 사용하거나 특정 도메인에서 호스팅되지 않는 한 이미지 요소를 제거하는 방법을 보여줍니다. `` 요소와 해당 `src` 속성이 이미 허용 목록에 등록되어 있다고 가정합니다.```ruby
require "uri"
image_allowlist_transformer = lambda do |env|
# Ignore everything except elements.
return unless env[:node_name] == "img"
node = env[:node]
image_uri = URI.parse(node["src"])
# Only allow relative URLs or URLs with the example.com domain. The
# image_uri.host.nil? check ensures that protocol-relative URLs like
# "//evil.com/foo.jpg" are not allowed.
unless image_uri.host == "example.com"
unless image_uri.host.nil? && image_uri.relative?
node.unlink # `Nokogiri::XML::Node#unlink` removes a node from the document
end
end
end
다음 예시는 모든 ` ].strip
Sanitize.fragment(html, transformers: youtube_transformer)