Do not allow login or register over an insecure connection

This commit is contained in:
Kukks
2019-11-14 19:01:26 +01:00
parent 2bfea50014
commit f1cef81d76
3 changed files with 139 additions and 60 deletions

View File

@@ -74,21 +74,29 @@ namespace BTCPayServer.Controllers
[AllowAnonymous] [AllowAnonymous]
public async Task<IActionResult> Login(string returnUrl = null) public async Task<IActionResult> Login(string returnUrl = null)
{ {
if (User.Identity.IsAuthenticated && string.IsNullOrEmpty(returnUrl)) if (User.Identity.IsAuthenticated && string.IsNullOrEmpty(returnUrl))
return RedirectToLocal(); return RedirectToLocal();
// Clear the existing external cookie to ensure a clean login process // Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
CanLoginOrRegister();
ViewData["ReturnUrl"] = returnUrl; ViewData["ReturnUrl"] = returnUrl;
return View(); return View();
} }
[HttpPost] [HttpPost]
[AllowAnonymous] [AllowAnonymous]
[ValidateAntiForgeryToken] [ValidateAntiForgeryToken]
[RateLimitsFilter(ZoneLimits.Login, Scope = RateLimitsScope.RemoteAddress)] [RateLimitsFilter(ZoneLimits.Login, Scope = RateLimitsScope.RemoteAddress)]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null) public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{ {
if (!CanLoginOrRegister())
{
return View(model);
}
ViewData["ReturnUrl"] = returnUrl; ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid) if (ModelState.IsValid)
{ {
@@ -199,6 +207,11 @@ namespace BTCPayServer.Controllers
[ValidateAntiForgeryToken] [ValidateAntiForgeryToken]
public async Task<IActionResult> LoginWithU2F(LoginWithU2FViewModel viewModel, string returnUrl = null) public async Task<IActionResult> LoginWithU2F(LoginWithU2FViewModel viewModel, string returnUrl = null)
{ {
if (!CanLoginOrRegister())
{
return RedirectToAction("Login");
}
ViewData["ReturnUrl"] = returnUrl; ViewData["ReturnUrl"] = returnUrl;
var user = await _userManager.FindByIdAsync(viewModel.UserId); var user = await _userManager.FindByIdAsync(viewModel.UserId);
@@ -242,6 +255,11 @@ namespace BTCPayServer.Controllers
[AllowAnonymous] [AllowAnonymous]
public async Task<IActionResult> LoginWith2fa(bool rememberMe, string returnUrl = null) public async Task<IActionResult> LoginWith2fa(bool rememberMe, string returnUrl = null)
{ {
if (!CanLoginOrRegister())
{
return RedirectToAction("Login");
}
// Ensure the user has gone through the username & password screen first // Ensure the user has gone through the username & password screen first
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
@@ -264,6 +282,11 @@ namespace BTCPayServer.Controllers
[ValidateAntiForgeryToken] [ValidateAntiForgeryToken]
public async Task<IActionResult> LoginWith2fa(LoginWith2faViewModel model, bool rememberMe, string returnUrl = null) public async Task<IActionResult> LoginWith2fa(LoginWith2faViewModel model, bool rememberMe, string returnUrl = null)
{ {
if (!CanLoginOrRegister())
{
return RedirectToAction("Login");
}
if (!ModelState.IsValid) if (!ModelState.IsValid)
{ {
return View(model); return View(model);
@@ -305,6 +328,11 @@ namespace BTCPayServer.Controllers
[AllowAnonymous] [AllowAnonymous]
public async Task<IActionResult> LoginWithRecoveryCode(string returnUrl = null) public async Task<IActionResult> LoginWithRecoveryCode(string returnUrl = null)
{ {
if (!CanLoginOrRegister())
{
return RedirectToAction("Login");
}
// Ensure the user has gone through the username & password screen first // Ensure the user has gone through the username & password screen first
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null) if (user == null)
@@ -322,6 +350,11 @@ namespace BTCPayServer.Controllers
[ValidateAntiForgeryToken] [ValidateAntiForgeryToken]
public async Task<IActionResult> LoginWithRecoveryCode(LoginWithRecoveryCodeViewModel model, string returnUrl = null) public async Task<IActionResult> LoginWithRecoveryCode(LoginWithRecoveryCodeViewModel model, string returnUrl = null)
{ {
if (!CanLoginOrRegister())
{
return RedirectToAction("Login");
}
if (!ModelState.IsValid) if (!ModelState.IsValid)
{ {
return View(model); return View(model);
@@ -366,7 +399,8 @@ namespace BTCPayServer.Controllers
[AllowAnonymous] [AllowAnonymous]
public async Task<IActionResult> Register(string returnUrl = null, bool logon = true, bool useBasicLayout = false) public async Task<IActionResult> Register(string returnUrl = null, bool logon = true, bool useBasicLayout = false)
{ {
var policies = await _SettingsRepository.GetSettingAsync<PoliciesSettings>() ?? new PoliciesSettings(); CanLoginOrRegister();
var policies = await _SettingsRepository.GetSettingAsync<PoliciesSettings>() ?? new PoliciesSettings();
if (policies.LockSubscription && !User.IsInRole(Roles.ServerAdmin)) if (policies.LockSubscription && !User.IsInRole(Roles.ServerAdmin))
return RedirectToAction(nameof(HomeController.Index), "Home"); return RedirectToAction(nameof(HomeController.Index), "Home");
ViewData["ReturnUrl"] = returnUrl; ViewData["ReturnUrl"] = returnUrl;
@@ -381,6 +415,11 @@ namespace BTCPayServer.Controllers
[ValidateAntiForgeryToken] [ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null, bool logon = true) public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null, bool logon = true)
{ {
if (!CanLoginOrRegister())
{
return RedirectToAction("Register");
}
ViewData["ReturnUrl"] = returnUrl; ViewData["ReturnUrl"] = returnUrl;
ViewData["Logon"] = logon.ToString(CultureInfo.InvariantCulture).ToLowerInvariant(); ViewData["Logon"] = logon.ToString(CultureInfo.InvariantCulture).ToLowerInvariant();
ViewData["AllowIsAdmin"] = _Options.AllowAdminRegistration; ViewData["AllowIsAdmin"] = _Options.AllowAdminRegistration;
@@ -580,6 +619,21 @@ namespace BTCPayServer.Controllers
return RedirectToAction(nameof(HomeController.Index), "Home"); return RedirectToAction(nameof(HomeController.Index), "Home");
} }
} }
private bool CanLoginOrRegister()
{
if (_btcPayServerEnvironment.IsDevelopping || _btcPayServerEnvironment.IsSecure) return true;
TempData.SetStatusMessageModel(new StatusMessageModel()
{
Severity = StatusMessageModel.StatusSeverity.Error,
Message = "You cannot login over an insecure connection. Please use HTTPS or Tor."
});
ViewData["disabled"] = true;
return false;
}
#endregion #endregion
} }

View File

@@ -4,7 +4,14 @@
ViewData["Title"] = "Log in"; ViewData["Title"] = "Log in";
Layout = "_WelcomeLayout.cshtml"; Layout = "_WelcomeLayout.cshtml";
} }
@if (TempData.HasStatusMessage())
{
<div class="row">
<div class="col-lg-12 text-center">
<partial name="_StatusMessage"/>
</div>
</div>
}
<div class="modal-dialog modal-login"> <div class="modal-dialog modal-login">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
@@ -12,30 +19,32 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form asp-route-returnurl="@ViewData["ReturnUrl"]" method="post"> <form asp-route-returnurl="@ViewData["ReturnUrl"]" method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div> <fieldset disabled="@(ViewData.ContainsKey("disabled") ? "disabled" : null)" >
<div class="form-group"> <div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="input-group"> <div class="form-group">
<div class="input-group-prepend"> <div class="input-group">
<label for="Email" class="input-group-text"><span class="input-group-addon fa fa-user"></span></label> <div class="input-group-prepend">
</div> <label for="Email" class="input-group-text"><span class="input-group-addon fa fa-user"></span></label>
</div>
<input asp-for="Email" class="form-control" placeholder="Email" required="required" /> <input asp-for="Email" class="form-control" placeholder="Email" required="required" />
</div>
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<label for="Password" class="input-group-text"><span class="input-group-addon fa fa-lock"></span></label>
</div> </div>
<input asp-for="Password" class="form-control" placeholder="Password" required="required" /> <span asp-validation-for="Email" class="text-danger"></span>
</div> </div>
<span asp-validation-for="Password" class="text-danger"></span> <div class="form-group">
</div> <div class="input-group">
<div class="form-group"> <div class="input-group-prepend">
<button type="submit" class="btn btn-primary btn-block btn-lg" id="LoginButton">Sign in</button> <label for="Password" class="input-group-text"><span class="input-group-addon fa fa-lock"></span></label>
</div> </div>
<p class="hint-text"><a asp-action="ForgotPassword">Forgot your password?</a></p> <input asp-for="Password" class="form-control" placeholder="Password" required="required" />
</div>
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-block btn-lg" id="LoginButton">Sign in</button>
</div>
<p class="hint-text"><a asp-action="ForgotPassword">Forgot your password?</a></p>
</fieldset>
</form> </form>
</div> </div>
@if (themeManager.ShowRegister) @if (themeManager.ShowRegister)

View File

@@ -4,6 +4,14 @@
var useBasicLayout = ViewData["UseBasicLayout"] is true; var useBasicLayout = ViewData["UseBasicLayout"] is true;
Layout = useBasicLayout ? "../Shared/_Layout.cshtml" : "_WelcomeLayout.cshtml"; Layout = useBasicLayout ? "../Shared/_Layout.cshtml" : "_WelcomeLayout.cshtml";
} }
@if (TempData.HasStatusMessage())
{
<div class="row">
<div class="col-lg-12 text-center">
<partial name="_StatusMessage"/>
</div>
</div>
}
<!-- We want to center the dialog box in case we are not using the Welcome layout --> <!-- We want to center the dialog box in case we are not using the Welcome layout -->
<div class="modal-dialog @(useBasicLayout ? "modal-dialog-centered" : "")"> <div class="modal-dialog @(useBasicLayout ? "modal-dialog-centered" : "")">
@@ -13,45 +21,53 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form asp-route-returnUrl="@ViewData["ReturnUrl"]" asp-route-logon="@ViewData["Logon"]" method="post"> <form asp-route-returnUrl="@ViewData["ReturnUrl"]" asp-route-logon="@ViewData["Logon"]" method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div> <fieldset disabled="@(ViewData.ContainsKey("disabled") ? "disabled" : null)" >
<div class="form-group"> <div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="input-group">
<div class="input-group-prepend">
<label for="Email" class="input-group-text"><span class="input-group-addon fa fa-user"></span></label>
</div>
<input asp-for="Email" class="form-control" placeholder="Email" required="required" />
</div>
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<label for="Password" class="input-group-text"><span class="input-group-addon fa fa-lock"></span></label>
</div>
<input asp-for="Password" class="form-control" placeholder="Password" required="required" />
</div>
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<label for="ConfirmPassword" class="input-group-text"><span class="input-group-addon fa fa-lock"></span></label>
</div>
<input asp-for="ConfirmPassword" class="form-control" placeholder="Repeat password" required="required" />
</div>
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
@if (ViewData["AllowIsAdmin"] is true)
{
<div class="form-group"> <div class="form-group">
<label asp-for="IsAdmin"></label> <div class="input-group">
<input asp-for="IsAdmin" type="checkbox" class="form-check-inline" /> <div class="input-group-prepend">
<span asp-validation-for="IsAdmin" class="text-danger"></span> <label for="Email" class="input-group-text">
<span class="input-group-addon fa fa-user"></span>
</label>
</div>
<input asp-for="Email" class="form-control" placeholder="Email" required="required"/>
</div>
<span asp-validation-for="Email" class="text-danger"></span>
</div> </div>
} <div class="form-group">
<div class="form-group"> <div class="input-group">
<button type="submit" class="btn btn-primary btn-block btn-lg" id="RegisterButton">Create account</button> <div class="input-group-prepend">
</div> <label for="Password" class="input-group-text">
<span class="input-group-addon fa fa-lock"></span>
</label>
</div>
<input asp-for="Password" class="form-control" placeholder="Password" required="required"/>
</div>
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<label for="ConfirmPassword" class="input-group-text">
<span class="input-group-addon fa fa-lock"></span>
</label>
</div>
<input asp-for="ConfirmPassword" class="form-control" placeholder="Repeat password" required="required"/>
</div>
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
@if (ViewData["AllowIsAdmin"] is true)
{
<div class="form-group">
<label asp-for="IsAdmin"></label>
<input asp-for="IsAdmin" type="checkbox" class="form-check-inline"/>
<span asp-validation-for="IsAdmin" class="text-danger"></span>
</div>
}
<div class="form-group">
<button type="submit" class="btn btn-primary btn-block btn-lg" id="RegisterButton">Create account</button>
</div>
</fieldset>
</form> </form>
</div> </div>
</div> </div>