MarkupString を使用して Blazor で生の HTML をレンダリングする
先日、私は CMS からの HTML をレンダリングする必要があるコンポーネントを構築していました。変数に HTML 文字列を入れて、それを @myHtml のようにテンプレートにドロップしました。そしてもちろん、Blazor はすべてをエスケープし、実際のタグをページ上のテキストとしてレンダリングしました。私が望んでいたものではありませんでした。
問題
既定では、Blazor はテンプレートでレンダリングする文字列をエンコードします。したがって、次のものがある場合:
@code {
private string content = "<strong>Hello</strong> <em>world</em>";
}
そして、次のようにします。
<div>@content</div>
ページには Hello world の代わりにリテラル テキスト <strong>Hello</strong> <em>world</em> が表示されます。 Blazor は XSS 攻撃を防ぐために意図的にこれを行いますが、これは正しい既定の動作です。
解決策: MarkupString
実際に生の HTML をレンダリングする必要がある場合は、文字列を MarkupString でラップします。
<div>@((MarkupString)content)</div>
それで終わりです!これで、Blazor は HTML を実際のマークアップとしてレンダリングします。変数に代入することもできます。
@code {
private string rawHtml = "<strong>Hello</strong> <em>world</em>";
private MarkupString HtmlContent => (MarkupString)rawHtml;
}
<div>@HtmlContent</div>
実際の例
API からブログ コンテンツを取得しており、それをプレビュー コンポーネントでレンダリングする必要がありました。コンテンツには、見出し、コード ブロック、リンク、画像など、あらゆる種類の HTML が含まれていました。おおよそ次のような感じです。
@inject HttpClient Http
@if (article is not null)
{
<article>
<h1>@article.Title</h1>
<div class="content">
@((MarkupString)article.HtmlBody)
</div>
</article>
}
@code {
[Parameter]
public int ArticleId { get; set; }
private ArticleDto? article;
protected override async Task OnInitializedAsync()
{
article = await Http.GetFromJsonAsync<ArticleDto>($"api/articles/{ArticleId}");
}
}
完璧に動作します。 API からの HTML は実際のマークアップとしてレンダリングされます。
信頼できないコンテンツには注意してください
これは重要です。MarkupString は HTML をサニタイズしません**。 <script> タグを含め、指定されたものはすべてレンダリングされます。したがって、コンテンツがユーザー入力または信頼できないソースからのものである場合は、最初にそれをサニタイズする必要があります。
Blazor には組み込みの HTML サニタイザーはありませんが、HtmlSanitizer のようなライブラリを使用できます。
using Ganss.Xss;
@code {
private HtmlSanitizer sanitizer = new();
private MarkupString SafeHtml(string html)
{
var clean = sanitizer.Sanitize(html);
return (MarkupString)clean;
}
}
<div>@SafeHtml(untrustedContent)</div>
これにより、<script>、onclick ハンドラーなどの危険な要素や、ユーザー提供のコンテンツからレンダリングしたくないその他の要素が削除されます。
#いつ使用するか
私は MarkupString を次の目的で使用します。
- サーバー側で HTML に変換された CMS コンテンツまたはマークダウン
- リッチテキストエディタの出力
- 電子メールテンプレートのプレビュー
- 信頼できるソースからの事前に構築された HTML
ユーザー入力から得られるものはすべて、常に最初にサニタイズしてください。後悔するよりは安全な方が良いです。
投稿が気に入っていただければ幸いです!ソーシャルメディアで**@emimontesdeoca**までお気軽にご連絡ください。