bluemonday は Go で実装された HTML サニタイザーです。高速で、高度に設定可能です。
bluemonday は、信頼できないユーザー生成コンテンツを入力として受け取り、承認された HTML 要素と属性の許可リストに基づいてサニタイズされた HTML を返すため、そのコンテンツを Web ページに安全に含めることができます。
ユーザー生成コンテンツを受け入れており、サーバーが Go を使用しているなら、bluemonday が必要です。
ユーザー生成コンテンツのデフォルトポリシー(bluemonday.UGCPolicy().Sanitize())は、これを:
Hello <STYLE>.XSS{background-image:url("javascript:alert('XSS')");}</STYLE><A CLASS=XSS></A>World
この無害なものに変換します:
Hello World
また、これは:
<a href="javascript:alert('XSS1')" onmouseover="alert('XSS2')">XSS<a>
次のように変換されます:
XSS
一方、これは引き続き許可されます:
<a href="http://www.google.com/">
<img src="https://ssl.gstatic.com/accounts/ui/logo_2x.png"/>
</a>
ほとんど変更されずに通過します(ユーザー生成コンテンツにとって良いことである rel="nofollow" が追加されます):
<a href="http://www.google.com/" rel="nofollow">
<img src="https://ssl.gstatic.com/accounts/ui/logo_2x.png"/>
</a>
これにより、サイトを XSS 攻撃から保護します。XSS 攻撃のベクター は多数存在し、リスクを軽減する最善の方法は、既知の安全な HTML 要素と属性のリストに対してユーザー入力をサニタイズすることです。
bluemonday は、他の処理の後に常に実行してください。
blackfriday や Pandoc を使用する場合、これらのステップの後に bluemonday を実行する必要があります。これにより、プロセスの後半で安全でない HTML が挿入されることを防ぎます。
bluemonday は、OWASP Java HTML Sanitizer と HTML Purifier の両方から大きな影響を受けています。
許可リストベースであり、許可する HTML 要素と属性(および属性の regexp パターン)を記述したポリシーを構築するか、適切なデフォルトを表す提供済みポリシーのいずれかを使用する必要があります。
許可リストを含むポリシーは、Go コアチームによって実装された Go net/html ライブラリ の高速な非検証・前方向のみ・トークンベースのパーサーを使用して適用されます。
適切にフォーマットされた HTML(該当するすべての開始要素に対する終了要素、正しいネスト)が提供されることを想定しているため、不正なネストや不完全な HTML の修復には焦点を当てていません。既存の要素がポリシーの許可リストに記載されていること、および属性とリンクが Web ページで安全に使用できることを確認することにのみ焦点を当てています。GIGO が適用され、不正な HTML を入力した場合、bluemonday はそれを再び適切なものにする方法を考える役割は負いません。
はい
広く使用され、十分に現場テストされた OWASP Java HTML Sanitizer から移行し、本番環境で bluemonday を使用しています。
広範なテストスイート(AntiSamy テストや、提起された問題に対するテストを含む)をすべて通過しています。未解決の問題 を確認して、あなたにとってのブロッカーとなるものがないかどうかを確認してください。
ユーザー生成コンテンツを介したさまざまな攻撃に対して包括的な保護を提供できるよう、プルリクエストと issue をお待ちしています。
go get github.com/microcosm-cc/bluemonday でインストールします
次に、次のように呼び出します:
package main
import (
"fmt"
"github.com/microcosm-cc/bluemonday"
)
func main() {
// Do this once for each unique policy, and use the policy for the life of the program
// Policy creation/editing is not safe to use in multiple goroutines
p := bluemonday.UGCPolicy()
// The policy can then be used to sanitize lots of input and it is safe to use the policy in multiple goroutines
html := p.Sanitize(
`<a onblur="alert(secret)" href="http://www.google.com">Google</a>`,
)
// Output:
// <a href="http://www.google.com" rel="nofollow">Google</a>
fmt.Println(html)
}
Sanitize を呼び出す方法は 3 つあります:
p.Sanitize(string) string
p.SanitizeBytes([]byte) []byte
p.SanitizeReader(io.Reader) bytes.Buffer
パフォーマンスにこだわる場合、p.SanitizeReader(r).Bytes() は入力または出力の不要なキャストを行うことなく []byte を返します。ただし、その差はごくわずかなので、気にする必要はありません。
独自のポリシーを構築できます:
package main
import (
"fmt"
"github.com/microcosm-cc/bluemonday"
)
func main() {
p := bluemonday.NewPolicy()
// Require URLs to be parseable by net/url.Parse and either:
// mailto: http:// or https://
p.AllowStandardURLs()
// We only allow <p> and <a href="">
p.AllowAttrs("href").OnElements("a")
p.AllowElements("p")
html := p.Sanitize(
`<a onblur="alert(secret)" href="http://www.google.com">Google</a>`,
)
// Output:
// <a href="http://www.google.com">Google</a>
fmt.Println(html)
}
2 つのデフォルトポリシーを提供しています:
bluemonday.StrictPolicy() は、許可リストに何もないため、すべての HTML 要素とその属性を除去することと同等と考えられます。使用例としては、HTML タグがまったく期待されず、もし存在する場合は要素と要素のコンテンツの両方を除去すべきブログ投稿のタイトルが挙げられます。これは非常に厳格なポリシーです。bluemonday.UGCPolicy() は、ユーザー生成コンテンツに対して安全な幅広い HTML 要素と属性を許可します。このポリシーは iframe、object、embed、styles、script などを許可しないことに注意してください。使用例としては、さまざまなフォーマットに加えて TABLE や IMG の可能性が期待されるブログ投稿の本文が挙げられます。ポリシー構築の本質は、あなたのシナリオにとってどの HTML 要素と属性が安全であると見なされるかを決定することです。OWASP はリスクの説明に役立つ XSS 防止チートシート を提供していますが、基本的には次のとおりです:
script、style、iframe、object、embed、base 要素を避ける基本的に、あなたのシナリオに適した HTML を説明できる必要があります。ポリシーを説明できる自信がない場合は、bluemonday.UGCPolicy() などの提供済みポリシーのいずれかの使用を検討してください。
新しいポリシーを作成するには:
p := bluemonday.NewPolicy()
ポリシーに要素を追加するには、要素だけを追加するか:
p.AllowElements("b", "strong")
または正規表現を使用します:
注:上記のように名前で要素が追加された場合、一致する正規表現は無視されます
また、実行順序は保証されておらず、一部のルールが見逃される可能性があるため、複数のパターンが重複しないようにすることも推奨されます。
p.AllowElementsMatching(regex.MustCompile(`^my-element-`))
あるいは、属性を追加することで要素を追加します:
// Note the recommended pattern, see the recommendation on using .Matching() below
p.AllowAttrs("nowrap").OnElements("td", "th")
同様に、これも正規表現パターンマッチの代替をサポートしています:
p.AllowAttrs("nowrap").OnElementsMatching(regex.MustCompile(`^my-element-`))
属性はすべての要素に追加することもできます:
p.AllowAttrs("dir").Matching(regexp.MustCompile("(?i)rtl|ltr")).Globally()
または、属性を特定の要素に追加することもできます:
// Not the recommended pattern, see the recommendation on using .Matching() below
p.AllowAttrs("value").OnElements("li")
属性は常にパターンにマッチさせることが推奨されます。そうしないと、HTML 属性内の XSS が非常に容易になります:
// \p{L} matches unicode letters, \p{N} matches unicode numbers
p.AllowAttrs("title").Matching(regexp.MustCompile(`[\p{L}\p{N}\s\-_',:\[\]!\./\\\(\)&]*`)).Globally()
いつでも停止して .Sanitize() を呼び出すことができます:
// string htmlIn passed in from a HTTP POST
htmlOut := p.Sanitize(htmlIn)
また、既存のポリシーを取得して拡張できます:
p := bluemonday.UGCPolicy()
p.AllowElements("fieldset", "select", "option")
Matching ルールを備えた AllowAttrs を使用してインライン CSS を処理することは可能ですが、許可したいすべてのインライン CSS を安全に処理する単一のモノリシックな正規表現を作成することは簡単な作業ではありません。その代わりに、任意の要素で style 属性を許可し、スタイルポリシーを使用してインラインスタイルを制御およびサニタイズできます。
各スタイルがニーズに合っていることを確認するために、Matching(適切な正規表現を使用)、MatchingEnum、または MatchingHandler を使用することを強くお勧めします。ただし、最も広く使用されているスタイルにはデフォルトのハンドラーが提供されています。
属性と同様に、特定の CSS プロパティがインラインで設定されることを許可できます:
p.AllowAttrs("style").OnElements("span", "p")
// Allow the 'color' property with valid RGB(A) hex values only (on any element allowed a 'style' attribute)
p.AllowStyles("color").Matching(regexp.MustCompile("(?i)^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$")).Globally()
さらに、CSS プロパティが許可された値にのみ設定されることを許可できます:
p.AllowAttrs("style").OnElements("span", "p")
// Allow the 'text-decoration' property to be set to 'underline', 'line-through' or 'none'
// on 'span' elements only
p.AllowStyles("text-decoration").MatchingEnum("underline", "line-through", "none").OnElements("span")
または、regex パターンマッチに基づいて要素を指定できます:
p.AllowAttrs("style").OnElementsMatching(regex.MustCompile(`^my-element-`))
// Allow the 'text-decoration' property to be set to 'underline', 'line-through' or 'none'
// on 'span' elements only
p.AllowStyles("text-decoration").MatchingEnum("underline", "line-through", "none").OnElementsMatching(regex.MustCompile(`^my-element-`))