Fix a bunch of open redirect (#4575)

This commit is contained in:
Nicolas Dorier 2023-02-02 09:42:58 +09:00 committed by GitHub
parent e4f256d5cd
commit f2ced20c42
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 37 additions and 24 deletions

View file

@ -203,7 +203,6 @@ namespace BTCPayServer.Tests
{ {
var isImport = !string.IsNullOrEmpty(seed); var isImport = !string.IsNullOrEmpty(seed);
GoToWalletSettings(cryptoCode); GoToWalletSettings(cryptoCode);
// Replace previous wallet case // Replace previous wallet case
if (Driver.PageSource.Contains("id=\"ChangeWalletLink\"")) if (Driver.PageSource.Contains("id=\"ChangeWalletLink\""))
{ {

View file

@ -1,4 +1,5 @@
using System;
using BTCPayServer; using BTCPayServer;
using BTCPayServer.Client.Models; using BTCPayServer.Client.Models;
using BTCPayServer.Controllers; using BTCPayServer.Controllers;
@ -10,6 +11,18 @@ namespace Microsoft.AspNetCore.Mvc
{ {
public static class UrlHelperExtensions public static class UrlHelperExtensions
{ {
#nullable enable
public static string? EnsureLocal(this IUrlHelper helper, string? url, HttpRequest? httpRequest = null)
{
if (url is null || helper.IsLocalUrl(url))
return url;
if (httpRequest is null)
return null;
if (Uri.TryCreate(url, UriKind.Absolute, out var r) && r.Host.Equals(httpRequest.Host.Host))
return url;
return null;
}
#nullable restore
public static string EmailConfirmationLink(this LinkGenerator urlHelper, string userId, string code, string scheme, HostString host, string pathbase) public static string EmailConfirmationLink(this LinkGenerator urlHelper, string userId, string code, string scheme, HostString host, string pathbase)
{ {
return urlHelper.GetUriByAction(nameof(UIAccountController.ConfirmEmail), "UIAccount", return urlHelper.GetUriByAction(nameof(UIAccountController.ConfirmEmail), "UIAccount",

View file

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -16,7 +17,7 @@ namespace BTCPayServer.Models.StoreViewModels
public string[] Words public string[] Words
{ {
get => Mnemonic.Split((char[])null, System.StringSplitOptions.RemoveEmptyEntries); get => Mnemonic?.Split((char[])null, System.StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
} }
} }
} }

View file

@ -32,7 +32,7 @@
@if (!string.IsNullOrEmpty(Model.Action)) @if (!string.IsNullOrEmpty(Model.Action))
{ {
<form id="ConfirmForm" method="post" action="@actionUrl" rel="noreferrer noopener"> <form id="ConfirmForm" method="post" action="@Url.EnsureLocal(actionUrl)" rel="noreferrer noopener">
<div class="modal-body pt-0" id="ConfirmText" hidden> <div class="modal-body pt-0" id="ConfirmText" hidden>
<label for="ConfirmInput" class="form-label">Confirm the action by typing <strong id="ConfirmInputText"></strong>:</label> <label for="ConfirmInput" class="form-label">Confirm the action by typing <strong id="ConfirmInputText"></strong>:</label>
<input id="ConfirmInput" class="form-control"/> <input id="ConfirmInput" class="form-control"/>

View file

@ -38,7 +38,7 @@
} }
else else
{ {
<form method="post" id="postform" action="@Model.FormUrl" rel="noreferrer noopener"> <form method="post" id="postform" action="@Url.EnsureLocal(Model.FormUrl, this.Context.Request)" rel="noreferrer noopener">
@Html.AntiForgeryToken() @Html.AntiForgeryToken()
@foreach (var o in Model.FormParameters) @foreach (var o in Model.FormParameters)
{ {

View file

@ -73,7 +73,7 @@
</div> </div>
@if (Model.RequireConfirm) @if (Model.RequireConfirm)
{ {
<form id="RecoveryConfirmation" action="@Model.ReturnUrl" class="position-relative d-flex align-items-start justify-content-center" style="margin-top:4rem;padding-bottom: 80px" rel="noreferrer noopener"> <form id="RecoveryConfirmation" action="@Url.EnsureLocal(Model.ReturnUrl)" class="position-relative d-flex align-items-start justify-content-center" style="margin-top:4rem;padding-bottom: 80px" rel="noreferrer noopener">
<label class="form-check-label lead order-2" for="confirm">I have written down my recovery phrase and stored it in a secure location</label> <label class="form-check-label lead order-2" for="confirm">I have written down my recovery phrase and stored it in a secure location</label>
<input type="checkbox" class="me-3 order-1 form-check-input" id="confirm" style="margin-top:.35rem;flex-shrink:0"> <input type="checkbox" class="me-3 order-1 form-check-input" id="confirm" style="margin-top:.35rem;flex-shrink:0">
<button type="submit" class="btn btn-primary btn-lg px-5 order-3" id="submit">Done</button> <button type="submit" class="btn btn-primary btn-lg px-5 order-3" id="submit">Done</button>
@ -82,7 +82,7 @@
} }
else else
{ {
<a href="@Model.ReturnUrl" class="btn btn-primary btn-lg mt-3 px-5 order-3" id="proceed" rel="noreferrer noopener">Done</a> <a href="@Url.EnsureLocal(Model.ReturnUrl)" class="btn btn-primary btn-lg mt-3 px-5 order-3" id="proceed" rel="noreferrer noopener">Done</a>
} }
</div> </div>
</div> </div>

View file

@ -52,7 +52,7 @@
data-bs-toggle="modal" data-bs-toggle="modal"
data-bs-target="#ConfirmModal" data-bs-target="#ConfirmModal"
data-title="Replace @Model.CryptoCode wallet" data-title="Replace @Model.CryptoCode wallet"
data-description="@Html.Encode(ViewData["ReplaceDescription"])" data-description="@ViewData["ReplaceDescription"]"
data-confirm="Setup new wallet" data-confirm="Setup new wallet"
data-confirm-input="REPLACE"> data-confirm-input="REPLACE">
Replace wallet Replace wallet
@ -64,7 +64,7 @@
data-bs-toggle="modal" data-bs-toggle="modal"
data-bs-target="#ConfirmModal" data-bs-target="#ConfirmModal"
data-title="Remove @Model.CryptoCode wallet" data-title="Remove @Model.CryptoCode wallet"
data-description="@Html.Encode(ViewData["RemoveDescription"])" data-description="@ViewData["RemoveDescription"]"
data-confirm="Remove" data-confirm="Remove"
data-confirm-input="REMOVE">Remove wallet</button> data-confirm-input="REMOVE">Remove wallet</button>
</form> </form>

View file

@ -11,11 +11,11 @@
@section Navbar { @section Navbar {
@if (backUrl != null) @if (backUrl != null)
{ {
<a href="@backUrl" id="GoBack"> <a href="@Url.EnsureLocal(backUrl)" id="GoBack">
<vc:icon symbol="back" /> <vc:icon symbol="back" />
</a> </a>
} }
<a href="@cancelUrl" id="CancelWizard" class="cancel"> <a href="@Url.EnsureLocal(cancelUrl)" id="CancelWizard" class="cancel">
<vc:icon symbol="close" /> <vc:icon symbol="close" />
</a> </a>
} }

View file

@ -11,11 +11,11 @@
@section Navbar { @section Navbar {
@if (backUrl != null) @if (backUrl != null)
{ {
<a href="@backUrl" id="GoBack"> <a href="@Url.EnsureLocal(backUrl)" id="GoBack">
<vc:icon symbol="back" /> <vc:icon symbol="back" />
</a> </a>
} }
<a href="@cancelUrl" id="CancelWizard" class="cancel"> <a href="@Url.EnsureLocal(cancelUrl)" id="CancelWizard" class="cancel">
<vc:icon symbol="close" /> <vc:icon symbol="close" />
</a> </a>
} }

View file

@ -11,11 +11,11 @@
@section Navbar { @section Navbar {
@if (backUrl != null) @if (backUrl != null)
{ {
<a href="@backUrl" id="GoBack"> <a href="@Url.EnsureLocal(backUrl)" id="GoBack">
<vc:icon symbol="back" /> <vc:icon symbol="back" />
</a> </a>
} }
<a href="@cancelUrl" id="CancelWizard" class="cancel"> <a href="@Url.EnsureLocal(cancelUrl)" id="CancelWizard" class="cancel">
<vc:icon symbol="close" /> <vc:icon symbol="close" />
</a> </a>
} }

View file

@ -75,11 +75,11 @@
@section Navbar { @section Navbar {
@if (backUrl != null) @if (backUrl != null)
{ {
<a href="@backUrl" id="GoBack"> <a href="@Url.EnsureLocal(backUrl)" id="GoBack">
<vc:icon symbol="back" /> <vc:icon symbol="back" />
</a> </a>
} }
<a href="@cancelUrl" id="CancelWizard" class="cancel"> <a href="@Url.EnsureLocal(cancelUrl)" id="CancelWizard" class="cancel">
<vc:icon symbol="close" /> <vc:icon symbol="close" />
</a> </a>
} }

View file

@ -1,4 +1,4 @@
@inject BTCPayServer.Services.BTCPayServerEnvironment env @inject BTCPayServer.Services.BTCPayServerEnvironment env
@using BTCPayServer.Controllers @using BTCPayServer.Controllers
@using BTCPayServer.Components.QRCode @using BTCPayServer.Components.QRCode
@model BTCPayServer.Controllers.WalletReceiveViewModel @model BTCPayServer.Controllers.WalletReceiveViewModel
@ -15,7 +15,7 @@
} }
@section Navbar { @section Navbar {
<a href="@returnUrl" id="CancelWizard" class="cancel"> <a href="@Url.EnsureLocal(returnUrl)" id="CancelWizard" class="cancel">
<vc:icon symbol="close" /> <vc:icon symbol="close" />
</a> </a>
} }

View file

@ -14,11 +14,11 @@
@section Navbar { @section Navbar {
@if (backUrl != null) @if (backUrl != null)
{ {
<a href="@backUrl" id="GoBack"> <a href="@Url.EnsureLocal(backUrl)" id="GoBack">
<vc:icon symbol="back" /> <vc:icon symbol="back" />
</a> </a>
} }
<a href="@cancelUrl" id="CancelWizard" class="cancel"> <a href="@Url.EnsureLocal(cancelUrl)" id="CancelWizard" class="cancel">
<vc:icon symbol="close" /> <vc:icon symbol="close" />
</a> </a>
} }

View file

@ -11,11 +11,11 @@
@section Navbar { @section Navbar {
@if (backUrl != null) @if (backUrl != null)
{ {
<a href="@backUrl" id="GoBack"> <a href="@Url.EnsureLocal(backUrl)" id="GoBack">
<vc:icon symbol="back" /> <vc:icon symbol="back" />
</a> </a>
} }
<a href="@cancelUrl" id="CancelWizard" class="cancel"> <a href="@Url.EnsureLocal(cancelUrl)" id="CancelWizard" class="cancel">
<vc:icon symbol="close" /> <vc:icon symbol="close" />
</a> </a>
} }

View file

@ -12,11 +12,11 @@
@section Navbar { @section Navbar {
@if (backUrl != null) @if (backUrl != null)
{ {
<a href="@backUrl" id="GoBack"> <a href="@Url.EnsureLocal(backUrl)" id="GoBack">
<vc:icon symbol="back" /> <vc:icon symbol="back" />
</a> </a>
} }
<a href="@cancelUrl" id="CancelWizard" class="cancel"> <a href="@Url.EnsureLocal(cancelUrl)" id="CancelWizard" class="cancel">
<vc:icon symbol="close" /> <vc:icon symbol="close" />
</a> </a>
} }