Implementierung von HtmlSanitizer und UrlEncoder zur Absicherung von Benutzereingaben gegen XSS und URL-Manipulationsanfälligkeiten.
This commit is contained in:
parent
b19cccdc34
commit
d8617093ce
@ -1,10 +1,9 @@
|
|||||||
|
using EnvelopeGenerator.Application.Services;
|
||||||
using EnvelopeGenerator.Application.Contracts;
|
|
||||||
using EnvelopeGenerator.Application.Services;
|
|
||||||
using EnvelopeGenerator.Common;
|
using EnvelopeGenerator.Common;
|
||||||
using EnvelopeGenerator.Web.Services;
|
using EnvelopeGenerator.Web.Services;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Web.Controllers
|
namespace EnvelopeGenerator.Web.Controllers
|
||||||
{
|
{
|
||||||
@ -13,13 +12,13 @@ namespace EnvelopeGenerator.Web.Controllers
|
|||||||
{
|
{
|
||||||
private readonly EnvelopeOldService envelopeService;
|
private readonly EnvelopeOldService envelopeService;
|
||||||
private readonly ActionService? actionService;
|
private readonly ActionService? actionService;
|
||||||
private readonly IEnvelopeService _envelopeService;
|
private readonly UrlEncoder _urlEncoder;
|
||||||
|
|
||||||
public EnvelopeController(DatabaseService database, EnvelopeOldService envelope, ILogger<EnvelopeController> logger, IEnvelopeService envService) : base(database, logger)
|
public EnvelopeController(DatabaseService database, EnvelopeOldService envelope, ILogger<EnvelopeController> logger, UrlEncoder urlEncoder) : base(database, logger)
|
||||||
{
|
{
|
||||||
envelopeService = envelope;
|
envelopeService = envelope;
|
||||||
actionService = database?.Services?.actionService;
|
actionService = database?.Services?.actionService;
|
||||||
_envelopeService = envService;
|
_urlEncoder = urlEncoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
[NonAction]
|
[NonAction]
|
||||||
@ -28,6 +27,8 @@ namespace EnvelopeGenerator.Web.Controllers
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
envelopeKey = _urlEncoder.Encode(envelopeKey);
|
||||||
|
|
||||||
// Validate Envelope Key and load envelope
|
// Validate Envelope Key and load envelope
|
||||||
envelopeService.EnsureValidEnvelopeKey(envelopeKey);
|
envelopeService.EnsureValidEnvelopeKey(envelopeKey);
|
||||||
|
|
||||||
@ -52,6 +53,8 @@ namespace EnvelopeGenerator.Web.Controllers
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
envelopeKey = _urlEncoder.Encode(envelopeKey);
|
||||||
|
|
||||||
var authSignature = this.GetAuthenticatedReceiverSignature();
|
var authSignature = this.GetAuthenticatedReceiverSignature();
|
||||||
|
|
||||||
if (authSignature != envelopeKey.GetReceiverSignature())
|
if (authSignature != envelopeKey.GetReceiverSignature())
|
||||||
|
|||||||
@ -16,6 +16,9 @@ using EnvelopeGenerator.Application.DTOs;
|
|||||||
using Microsoft.AspNetCore.Localization;
|
using Microsoft.AspNetCore.Localization;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Ganss.Xss;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Web.Controllers
|
namespace EnvelopeGenerator.Web.Controllers
|
||||||
{
|
{
|
||||||
@ -26,22 +29,26 @@ namespace EnvelopeGenerator.Web.Controllers
|
|||||||
private readonly IEnvelopeHistoryService _historyService;
|
private readonly IEnvelopeHistoryService _historyService;
|
||||||
private readonly IStringLocalizer<Resource> _localizer;
|
private readonly IStringLocalizer<Resource> _localizer;
|
||||||
private readonly IConfiguration _configuration;
|
private readonly IConfiguration _configuration;
|
||||||
|
private readonly UrlEncoder _urlEncoder;
|
||||||
|
|
||||||
public HomeController(DatabaseService databaseService, EnvelopeOldService envelopeOldService, ILogger<HomeController> logger, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeHistoryService historyService, IStringLocalizer<Resource> localizer, IConfiguration configuration) : base(databaseService, logger)
|
public HomeController(DatabaseService databaseService, EnvelopeOldService envelopeOldService, ILogger<HomeController> logger, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeHistoryService historyService, IStringLocalizer<Resource> localizer, IConfiguration configuration, UrlEncoder urlEncoder) : base(databaseService, logger)
|
||||||
{
|
{
|
||||||
this.envelopeOldService = envelopeOldService;
|
this.envelopeOldService = envelopeOldService;
|
||||||
_envRcvService = envelopeReceiverService;
|
_envRcvService = envelopeReceiverService;
|
||||||
_historyService = historyService;
|
_historyService = historyService;
|
||||||
_localizer = localizer;
|
_localizer = localizer;
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
|
_urlEncoder = urlEncoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("/EnvelopeKey/{envelopeReceiverId}")]
|
[HttpGet("/EnvelopeKey/{envelopeReceiverId}")]
|
||||||
public async Task<IActionResult> SendAccessCode([FromRoute] string envelopeReceiverId)
|
public async Task<IActionResult> SendAccessCode([FromRoute] string envelopeReceiverId)
|
||||||
{
|
{
|
||||||
ViewData["EnvelopeKey"] = envelopeReceiverId;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
envelopeReceiverId = _urlEncoder.Encode(envelopeReceiverId);
|
||||||
|
ViewData["EnvelopeKey"] = envelopeReceiverId;
|
||||||
|
|
||||||
return await _envRcvService.ReadByEnvelopeReceiverIdAsync(envelopeReceiverId: envelopeReceiverId).ThenAsync<EnvelopeReceiverDto, IActionResult>(
|
return await _envRcvService.ReadByEnvelopeReceiverIdAsync(envelopeReceiverId: envelopeReceiverId).ThenAsync<EnvelopeReceiverDto, IActionResult>(
|
||||||
SuccessAsync: async er =>
|
SuccessAsync: async er =>
|
||||||
{
|
{
|
||||||
@ -77,6 +84,7 @@ namespace EnvelopeGenerator.Web.Controllers
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
envelopeReceiverId = _urlEncoder.Encode(envelopeReceiverId);
|
||||||
ViewData["Languages"] = _configuration.GetSection("Languages").Get<string[]>()!;
|
ViewData["Languages"] = _configuration.GetSection("Languages").Get<string[]>()!;
|
||||||
ViewData["UserLanguage"] = UserLanguage;
|
ViewData["UserLanguage"] = UserLanguage;
|
||||||
|
|
||||||
@ -100,6 +108,7 @@ namespace EnvelopeGenerator.Web.Controllers
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
envelopeReceiverId = _urlEncoder.Encode(envelopeReceiverId);
|
||||||
(string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId();
|
(string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId();
|
||||||
|
|
||||||
if(uuid is null || signature is null)
|
if(uuid is null || signature is null)
|
||||||
@ -187,6 +196,7 @@ namespace EnvelopeGenerator.Web.Controllers
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
envelopeReceiverId = _urlEncoder.Encode(envelopeReceiverId);
|
||||||
return await _envRcvService.IsExisting(envelopeReceiverId: envelopeReceiverId).ThenAsync(
|
return await _envRcvService.IsExisting(envelopeReceiverId: envelopeReceiverId).ThenAsync(
|
||||||
SuccessAsync: async isExisting =>
|
SuccessAsync: async isExisting =>
|
||||||
{
|
{
|
||||||
@ -231,6 +241,7 @@ namespace EnvelopeGenerator.Web.Controllers
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
language = _urlEncoder.Encode(language);
|
||||||
var cookieOptions = new CookieOptions()
|
var cookieOptions = new CookieOptions()
|
||||||
{
|
{
|
||||||
Expires = DateTimeOffset.UtcNow.AddYears(1),
|
Expires = DateTimeOffset.UtcNow.AddYears(1),
|
||||||
|
|||||||
@ -0,0 +1,37 @@
|
|||||||
|
using Ganss.Xss;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Web.Controllers.Test
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/test/[controller]")]
|
||||||
|
public class TestSanitizeController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly HtmlEncoder _htmlEncoder;
|
||||||
|
private readonly HtmlSanitizer _sanitizer;
|
||||||
|
|
||||||
|
public TestSanitizeController(HtmlEncoder htmlEncoder, HtmlSanitizer sanitizer)
|
||||||
|
{
|
||||||
|
_htmlEncoder = htmlEncoder;
|
||||||
|
_sanitizer = sanitizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("sanitize")]
|
||||||
|
public IActionResult Sanitize([FromQuery] string? input = null) => Ok(new
|
||||||
|
{
|
||||||
|
input,
|
||||||
|
Sanitized = _sanitizer.Sanitize(input),
|
||||||
|
SanitizedDocument = _sanitizer.SanitizeDocument(input),
|
||||||
|
SanitizedDom = _sanitizer.SanitizeDom(input)
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
[HttpGet("encode")]
|
||||||
|
public IActionResult Encoder([FromQuery] string? input = null) => Ok(new
|
||||||
|
{
|
||||||
|
input,
|
||||||
|
Encoded = _htmlEncoder.Encode(input)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||||
|
<PackageReference Include="HtmlSanitizer" Version="8.0.865" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.4" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.16" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.16" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.15">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.15">
|
||||||
|
|||||||
@ -14,6 +14,8 @@ using Microsoft.AspNetCore.Authentication.Cookies;
|
|||||||
using DigitalData.UserManager.Application.MappingProfiles;
|
using DigitalData.UserManager.Application.MappingProfiles;
|
||||||
using EnvelopeGenerator.Web.Models;
|
using EnvelopeGenerator.Web.Models;
|
||||||
using DigitalData.Core.DTO;
|
using DigitalData.Core.DTO;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
using Ganss.Xss;
|
||||||
|
|
||||||
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
|
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
|
||||||
logger.Info("Logging initialized!");
|
logger.Info("Logging initialized!");
|
||||||
@ -151,6 +153,15 @@ try
|
|||||||
|
|
||||||
builder.Services.AddCookieBasedLocalizer();
|
builder.Services.AddCookieBasedLocalizer();
|
||||||
|
|
||||||
|
builder.Services.AddSingleton(HtmlEncoder.Default);
|
||||||
|
builder.Services.AddSingleton(UrlEncoder.Default);
|
||||||
|
builder.Services.AddSingleton(_ =>
|
||||||
|
{
|
||||||
|
var sanitizer = new HtmlSanitizer();
|
||||||
|
//configure sanitzer
|
||||||
|
return sanitizer;
|
||||||
|
});
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
@ -166,6 +177,15 @@ try
|
|||||||
|
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
|
|
||||||
|
var csp = builder.Configuration["Content-Security-Policy"];
|
||||||
|
if(csp is not null)
|
||||||
|
app.Use(async (context, next) =>
|
||||||
|
{
|
||||||
|
context.Response.Headers.Add("Content-Security-Policy", csp);
|
||||||
|
await next();
|
||||||
|
});
|
||||||
|
|
||||||
app.UseCookiePolicy();
|
app.UseCookiePolicy();
|
||||||
|
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
|||||||
@ -1,25 +1,8 @@
|
|||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Dokument geschützt";
|
ViewData["Title"] = "Dokument geschützt";
|
||||||
string userLanguage = ViewData["UserLanguage"] as string;
|
var userLanguage = ViewData["UserLanguage"] as string;
|
||||||
string[] languages = ViewData["Languages"] as string[];
|
var languages = ViewData["Languages"] as string[];
|
||||||
}
|
}
|
||||||
@* @if(ViewData["UserLanguage"] == null){
|
|
||||||
<script>
|
|
||||||
fetch('/api/data', { // Assuming the API endpoint is '/api/data' on the same origin
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
key: 'value'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => console.log(data))
|
|
||||||
.catch(error => console.error('Error:', error));
|
|
||||||
</script>
|
|
||||||
} *@
|
|
||||||
|
|
||||||
<div class="page container p-5">
|
<div class="page container p-5">
|
||||||
<header class="text-center">
|
<header class="text-center">
|
||||||
<div class="icon locked">
|
<div class="icon locked">
|
||||||
@ -30,18 +13,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<h1>Dokument erfordert einen Zugriffscode</h1>
|
<h1>Dokument erfordert einen Zugriffscode</h1>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<section class="text-center">
|
<section class="text-center">
|
||||||
<p>Wir haben Ihnen gerade den Zugriffscode an die hinterlegte Email Adresse gesendet. Dies kann evtl. einige Minuten dauern.</p>
|
<p>Wir haben Ihnen gerade den Zugriffscode an die hinterlegte Email Adresse gesendet. Dies kann evtl. einige Minuten dauern.</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="d-flex">
|
<section class="d-flex">
|
||||||
<form id="form-access-code" class="form pl-0 ml-0" method="post">
|
<form id="form-access-code" class="form pl-0 ml-0" method="post">
|
||||||
<div class="input">
|
<div class="input">
|
||||||
<label class="visually-hidden" for="access_code">Zugriffscode</label>
|
<label class="visually-hidden" for="access_code">Zugriffscode</label>
|
||||||
<input type="password" id="access_code" class="form-control" name="access_code" placeholder="Zugriffscode" required="required">
|
<input type="password" id="access_code" class="form-control" name="access_code" placeholder="Zugriffscode" required="required">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="button">
|
<div class="button">
|
||||||
<button type="submit" class="btn btn-primary">Öffnen</button>
|
<button type="submit" class="btn btn-primary">Öffnen</button>
|
||||||
</div>
|
</div>
|
||||||
@ -49,15 +29,15 @@
|
|||||||
<form class="form pl-0 ml-0" method="post" action="/lang">
|
<form class="form pl-0 ml-0" method="post" action="/lang">
|
||||||
<div class="dropdown">
|
<div class="dropdown">
|
||||||
<select class="form-select" name="language" onchange="this.form.submit()">
|
<select class="form-select" name="language" onchange="this.form.submit()">
|
||||||
@foreach(var lang in languages)
|
@if(languages is not null)
|
||||||
{
|
foreach(var lang in languages)
|
||||||
<option data-icon="flag-icon flag-icon-us" value="@lang">@_localizer[@lang]</option>
|
{
|
||||||
}
|
<option data-icon="flag-icon flag-icon-us" value="@lang.TrySanitize(_sanitizer)">@_localizer[@lang].Value.TrySanitize(_sanitizer)</option>
|
||||||
|
}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="text-center">
|
<section class="text-center">
|
||||||
<details>
|
<details>
|
||||||
<summary>Sie haben keinen Zugriffscode erhalten?</summary>
|
<summary>Sie haben keinen Zugriffscode erhalten?</summary>
|
||||||
@ -65,5 +45,4 @@
|
|||||||
</details>
|
</details>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer class="container" id="page-footer">© SignFlow 2023-2024 <a href="https://digitaldata.works">Digital Data GmbH</a></footer>
|
<footer class="container" id="page-footer">© SignFlow 2023-2024 <a href="https://digitaldata.works">Digital Data GmbH</a></footer>
|
||||||
@ -18,7 +18,7 @@
|
|||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggleExternalContent" aria-controls="navbarToggleExternalContent" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggleExternalContent" aria-controls="navbarToggleExternalContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="navbar-brand me-auto ms-5 envelope-message">@($"Hallo {Model.Name}, {@envelope?.Message}")</div>
|
<div class="navbar-brand me-auto ms-5 envelope-message">@($"Hallo {Model.Name.TrySanitize(_sanitizer)}, {@envelope?.Message.TrySanitize(_sanitizer)}")</div>
|
||||||
<div class="col-1 p-0 m-0 me-3 d-flex">
|
<div class="col-1 p-0 m-0 me-3 d-flex">
|
||||||
<img src="~/img/digital_data.svg" alt="...">
|
<img src="~/img/digital_data.svg" alt="...">
|
||||||
</div>
|
</div>
|
||||||
@ -33,9 +33,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col p-0 m-0">
|
<div class="col p-0 m-0">
|
||||||
<div class="card-body p-0 m-0">
|
<div class="card-body p-0 m-0">
|
||||||
<h5 class="card-title p-0 m-0">@($"{envelope?.Title}")</h5>
|
<h5 class="card-title p-0 m-0">@($"{envelope?.Title.TrySanitize(_sanitizer)}")</h5>
|
||||||
<p class="card-text p-0 m-0">@($"Sie haben {(pages.Count())} Briefe zu unterschreiben. Bitte prüfen Sie die Seiten {stPageIndexes}.")</p>
|
<p class="card-text p-0 m-0">@($"Sie haben {(pages.Count())} Briefe zu unterschreiben. Bitte prüfen Sie die Seiten {stPageIndexes.TrySanitize(_sanitizer)}.")</p>
|
||||||
<p class="card-text p-0 m-0"><small class="text-body-secondary">Erstellt am @envelope?.AddedWhen von @sender?.Prename @sender?.Name. Sie können den Absender über <a href="mailto:@(sender?.Email)?subject=@(envelope?.Title)&body=Sehr%20geehrter%20@(sender?.Prename)%20@(sender?.Name),%0A%0A%0A">@sender?.Email</a> kontaktieren.</small></p>
|
<p class="card-text p-0 m-0"><small class="text-body-secondary">Erstellt am @envelope?.AddedWhen von @sender?.Prename.TrySanitize(_sanitizer) @sender?.Name.TrySanitize(_sanitizer). Sie können den Absender über <a href="mailto:@(sender?.Email.TryEncode(_encoder))?subject=@(envelope?.Title.TryEncode(_encoder))&body=Sehr%20geehrter%20@(sender?.Prename.TryEncode(_encoder))%20@(sender?.Name.TryEncode(_encoder)),%0A%0A%0A">@sender?.Email.TryEncode(_encoder)</a> kontaktieren.</small></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -66,8 +66,10 @@
|
|||||||
|
|
||||||
var documentBase64String = Convert.ToBase64String(documentBytes);
|
var documentBase64String = Convert.ToBase64String(documentBytes);
|
||||||
|
|
||||||
|
var envelopeKey = ViewData["EnvelopeKey"] as string;
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var base64String = "@Html.Raw(documentBase64String)";
|
var base64String = "@Html.Raw(documentBase64String.TrySanitize(_sanitizer))";
|
||||||
var byteCharacters = atob(base64String);
|
var byteCharacters = atob(base64String);
|
||||||
var byteNumbers = new Array(byteCharacters.length);
|
var byteNumbers = new Array(byteCharacters.length);
|
||||||
for (var i = 0; i < byteCharacters.length; i++) {
|
for (var i = 0; i < byteCharacters.length; i++) {
|
||||||
@ -76,9 +78,9 @@
|
|||||||
var byteArray = new Uint8Array(byteNumbers);
|
var byteArray = new Uint8Array(byteNumbers);
|
||||||
var documentArrayBuffer = byteArray.buffer;
|
var documentArrayBuffer = byteArray.buffer;
|
||||||
|
|
||||||
var envelopeResponse = @Html.Raw(envelopeResponseJson);
|
var envelopeResponse = @Html.Raw(envelopeResponseJson.TrySanitize(_sanitizer));
|
||||||
document.addEventListener("DOMContentLoaded", async () => {
|
document.addEventListener("DOMContentLoaded", async () => {
|
||||||
const app = new App("#app", "@ViewData["EnvelopeKey"]", envelopeResponse, documentArrayBuffer, "@ViewData["PSPDFKitLicenseKey"]");
|
const app = new App("#app", "@envelopeKey.TrySanitize(_sanitizer)", envelopeResponse, documentArrayBuffer, "@ViewData["PSPDFKitLicenseKey"]");
|
||||||
await app.init();
|
await app.init();
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -31,7 +31,7 @@
|
|||||||
<section>
|
<section>
|
||||||
<article class="envelope">
|
<article class="envelope">
|
||||||
<strong><a href="/EnvelopeKey/@encodeEnvelopeKey(envelope)">@envelope.Title</a></strong>
|
<strong><a href="/EnvelopeKey/@encodeEnvelopeKey(envelope)">@envelope.Title</a></strong>
|
||||||
<div><strong>Ersteller</strong> @envelope.User.Email</div>
|
<div><strong>Ersteller</strong> @envelope.User.Email.TrySanitize(_sanitizer)</div>
|
||||||
<div><strong>Datum</strong> @envelope.AddedWhen</div>
|
<div><strong>Datum</strong> @envelope.AddedWhen</div>
|
||||||
</article>
|
</article>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -3,4 +3,6 @@
|
|||||||
@using Microsoft.Extensions.Localization;
|
@using Microsoft.Extensions.Localization;
|
||||||
@using EnvelopeGenerator.Application.Resources;
|
@using EnvelopeGenerator.Application.Resources;
|
||||||
@inject IStringLocalizer<Resource> _localizer;
|
@inject IStringLocalizer<Resource> _localizer;
|
||||||
|
@inject System.Text.Encodings.Web.UrlEncoder _encoder
|
||||||
|
@inject Ganss.Xss.HtmlSanitizer _sanitizer
|
||||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
12
EnvelopeGenerator.Web/XSSExtensions.cs
Normal file
12
EnvelopeGenerator.Web/XSSExtensions.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using Ganss.Xss;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Web
|
||||||
|
{
|
||||||
|
public static class XSSExtensions
|
||||||
|
{
|
||||||
|
public static string? TryEncode(this string? value, UrlEncoder encoder) => value is null ? value : encoder.Encode(value);
|
||||||
|
|
||||||
|
public static string? TrySanitize(this string? html, HtmlSanitizer sanitizer) => html is null ? html : sanitizer.Sanitize(html);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,6 +9,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"PSPDFKitLicenseKey": null,
|
"PSPDFKitLicenseKey": null,
|
||||||
|
/* recommended Content-Security-Policy for production:
|
||||||
|
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self';" */
|
||||||
|
"Content-Security-Policy": null,
|
||||||
"AdminPassword": "dd",
|
"AdminPassword": "dd",
|
||||||
"AllowedOrigins": [ "https://localhost:7202" ],
|
"AllowedOrigins": [ "https://localhost:7202" ],
|
||||||
"NLog": {
|
"NLog": {
|
||||||
@ -57,10 +60,10 @@
|
|||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"AddTestControllers": false,
|
"AddTestControllers": true,
|
||||||
"Jwt": {
|
"Jwt": {
|
||||||
"Issuer": "https://localhost:7202",
|
"Issuer": null,
|
||||||
"Audience": "https://localhost:7202",
|
"Audience": null,
|
||||||
"Key": "8RGnd7x0G2TYLOIW4m_qlIls7MfbAIGNrpQJzMAUIvULHOLiG723znRa_MG-Z4yw3SErusOU4hTui2rVBMcCaQ"
|
"Key": "8RGnd7x0G2TYLOIW4m_qlIls7MfbAIGNrpQJzMAUIvULHOLiG723znRa_MG-Z4yw3SErusOU4hTui2rVBMcCaQ"
|
||||||
},
|
},
|
||||||
"AuthCookieConfig": {
|
"AuthCookieConfig": {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user