Web API w/ SSL allows non-secure GET requests - c#

The scenario:
I have a .NET Core 2.0 Web API app configure to use only secure connections. I'm using Postman to test the requests.
If I try to POST, PUT or DELETE using non-secure URL (HTTP), it returns status 403 (as it should be). However, it accepts GET requests via HTTP.
I'm really an amateur regarding SSL usage, so I don't know if it should be the common behavior (although, In my head, it doesn't make any sense).
The SSL configuration in Web API is done as the following:
certificate.json:
{
"certificateSettings": {
"fileName": "filename.pfx",
"password": "password"
}
}
Program.cs:
public static IWebHost BuildWebHost(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddEnvironmentVariables()
.AddJsonFile("certificate.json", optional: true, reloadOnChange: true)
.AddJsonFile($"certificate.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true, reloadOnChange: true)
.Build();
var certificateSettings = config.GetSection("certificateSettings");
string certificateFileName = certificateSettings.GetValue<string>("filename");
string certificatePassword = certificateSettings.GetValue<string>("password");
var certificate = new X509Certificate2(certificateFileName, certificatePassword);
return WebHost.CreateDefaultBuilder(args)
.UseKestrel(
options =>
{
options.AddServerHeader = false;
options.Listen(IPAddress.Loopback, 44312, listenOptions =>
{
listenOptions.UseHttps(certificate);
});
}
)
.UseConfiguration(config)
.UseStartup<Startup>()
.Build();
}
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new RequireHttpsAttribute());
});
services.AddAntiforgery(
options =>
{
options.Cookie.Name = "_af";
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.HeaderName = "X-XSRF-TOKEN";
}
);
The app is hosted on IIS 10. It's a sub-Application, inside a Website, and this Website has the certificate I'm using bound in port 443. There's a copy of this certificate in the root of the application (although I don't know if this is needed).
All HTTPS requests work like a charm.
I believe it's something silly, I just couldn't figure it out. My web searches came to nothing.
Thanks in advance.

Related

Dotnet core 6, Custom Config Provider doesn't work when hosted in Windows Service

I have written custom ConfigurationProvider so that the application ( .net Core 6 ) is able to read configuration from the database. This works as expected when running from Visual Studio or running the output .exe file from command line, however when I try to run it as a Windows Service the config from database is not added. See code below with the appropriate comments
private static IHost CreateHost(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureHostConfiguration(configBuilder =>
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appSettings.json", optional: true, reloadOnChange: true);
IConfigurationRoot configuration = builder.Build();
var dbConfig = configuration.GetSection("DatabaseConfig");
// Get connection string from appSettings.json and add SqlConfigurationProvider
configBuilder.AddSqlDatabase(config =>
{
config.ConnectionString = dbConfig["ConnectionString"];
config.RefreshInterval = TimeSpan.FromMinutes(1);
});
})
.ConfigureServices((host, services) =>
{
var myConfigSection = host.Configuration.GetSection("MyConfigSection");
var myConfigValue = myConfigSection["MyConfig"];
// When running from VS or CMD myConfigValue is properly taken from the database
// When running from Windows Service myConfigValue will be null suggesting SqlConfigurationProvider has not been added
if (string.IsNullOrEmpty(myConfigValue))
throw new ArgumentException("Config is missing");
services.AddHostedService<WorkerProcess>();
})
.UseWindowsService(options =>
{
options.ServiceName = "MyService";
})
.Build();
}
I suppose that when we call UseWindowsService the host configuration is ignored and that's the reason but that's just the assumption.
What is the proper way of registering Custom Configuration Provider when running the app as Windows Service ?
Edit
I have tried using ConfigureAppConfiguration instead of ConfigureHostConfiguration but the result is unfortunately the same

SignalR .net core is getting 401 for negotiate request when inside windows intranet

I'm trying to get signalR to work with a .net core 2.2 application. I need to connect to it from an angular front end. I have another app that is seemingly configured the same way that already has signalR working. When I connect to the other app from the angular it works but if I try to connect to the original app I get a 401 error. the code involving signalR looks identical as far as I can tell.
We use windows intranet authentication. I don't have access to any of the devs who set this up originally but it's all in our windows intranet site and we login via an alert popup that I put my intranet info into.
Here's relevant code in startup.cs configure services
services.AddCors();
services.AddSignalR();
Here's in sertup.cs configure
var origins = Configuration.GetValue<string>("apiAllowedOrigins").Split(',');
app.UseCors(builder =>
builder.WithOrigins(origins)
.AllowAnyMethod()
.AllowCredentials()
.AllowAnyHeader()
);
app.UseAuthentication();
app.UseSignalR(routes =>
{
routes.MapHub<IbjaHub>("/ibjahub");
});
Here's ibjahub
using Microsoft.AspNetCore.SignalR;
namespace CapitalJobs.SignalR
{
public class IbjaHub : Hub
{
}
}
Here's the angular code connecting to the hub
this._ibjaService
.getSignalRHub()
.pipe(
tap(hd => {
const hubConnection = new signalR.HubConnectionBuilder()
.withUrl(hd.hubURL)
.build();
hubConnection
.start()
.then(() => {
console.log('SignalR Connection started');
})
.catch(err => {
this._toaster.pop({
type: 'error',
title: 'Connection Lost',
body: `Connection to server lost, and attempts to reconnect failed. Please refresh the page.`,
timeout: 0,
bodyOutputType: BodyOutputType.TrustedHtml
});
console.error(err);
});
hubConnection.on(
'todo: replace this with what its going to say',
data => {
if (data.username !== this._config.appMenu.userName) {
return;
}
//todo parse the data about e1
}
);
hubConnection.onclose((error?: Error) => {
if (error) {
console.error(error.message);
window.setTimeout(() => {
hubConnection
.start()
.then(() => {})
.catch(err => {
this._toaster.pop({
type: 'error',
title: 'Connection Lost',
body: `Connection to server lost, and attempts to reconnect failed. Please refresh the page.`,
timeout: 0,
bodyOutputType: BodyOutputType.TrustedHtml
});
console.error(err);
})
.then(() => {
this._toaster.pop({
type: 'warning',
title: 'Connection Lost',
body: `Connection to server was lost, but was successfully reconnected. Some messages may have been lost. Please refresh the page if issues continue.`,
timeout: 0,
showCloseButton: true,
bodyOutputType: BodyOutputType.TrustedHtml
});
});
}, 2000);
}
});
})
)
.subscribe();
}
I used the same code to connect to the working api vs the one that doesn't work. Here's a side by side screen shot of the results of the options request.
options request from calls to both apis
I've gone through many solutions on stack overflow as well as microsoft forums.
I tried adding a kestrel server line to program.cs.
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((builderContext, config) =>
{
})
.UseStartup<Startup>()
.ConfigureKestrel((context, options) =>{}) // this line
.UseNLog()
.Build();
}
I've added forwarding the tokens in start up both shown below.
services.AddAuthentication(IISDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "<issuer removed>",
ValidAudience = "<audience removed>",
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(Configuration["CapitalJobs:JWTSigningKey"]))
};

ASP.NET Core 3.1 Web API : can it be self-hosted as Windows service with https and use some certificate like IIS?

I am using default ASP.NET Core 3.1 Web API app where I have configured it for https and using app.UseHttpsRedirection(); as well.
Now I am hosting this as a Windows service using this nuget package: Microsoft.Extensions.Hosting.WindowsServices.
Hosting is done but I am getting the API result using http, but it's causing an error while trying to use https like https://localhost:5001/weatherforecast
Can I create some self signed certificate like IIS and assign it and can run as https?
#Frank Nielsen answer is correct, but i wanted to add one more method if someone else wanted different approach.
Part with creating certificate is the same as on the blog that #Franck Nielsen posted link to it. Just in this approach all configuration is done automatically through appsettings.json.
ASP.NET Core 5.0 docs says:
CreateDefaultBuilder calls Configure(context.Configuration.GetSection("Kestrel")) by default to load Kestrel configuration.
So you should add "Kestrel" section to appsetting.json
{
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://localhost:5000"
},
"Https": {
"Url": "https://localhost:5001",
"Certificate": {
"Path": "<path to .pfx file>",
"Password": "<certificate password>"
}
}
}
}
}
Program.cs could look like this with no additional configuring of Kestrel.
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseWindowsService();
And viola, rest is done by framework...
I found this method more cleaner and there is no hustle with things like getting the root directory of application .exe in windows service.
Link to ASP.NET Core 5.0 docs: Configure endpoints for the ASP.NET Core Kestrel web server
Yes, you can - but with some browser restrictions.
Create a certificate, and then either register it in certificate store, or load it in manually into Kestrel like:
certificate.json
{
"certificateSettings": {
"fileName": "localhost.pfx",
"password": "YourSecurePassword"
}
}
and use it something like this:
public class Program
{
public static void Main(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddEnvironmentVariables()
.AddJsonFile("certificate.json", optional: true, reloadOnChange: true)
.AddJsonFile($"certificate.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true, reloadOnChange: true)
.Build();
var certificateSettings = config.GetSection("certificateSettings");
string certificateFileName = certificateSettings.GetValue<string>("filename");
string certificatePassword = certificateSettings.GetValue<string>("password");
var certificate = new X509Certificate2(certificateFileName, certificatePassword);
var host = new WebHostBuilder()
.UseKestrel(
options =>
{
options.AddServerHeader = false;
options.Listen(IPAddress.Loopback, 443, listenOptions =>
{
listenOptions.UseHttps(certificate);
});
}
)
.UseConfiguration(config)
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseUrls("https://localhost:443")
.Build();
host.Run();
}
}
Snippets taken from this good blog post: https://www.humankode.com/asp-net-core/develop-locally-with-https-self-signed-certificates-and-asp-net-core

How to fix 'One or more errors occurred. (The SSL connection could not be established, see inner exception.)'?

I have created web api, when trying to add ssl get this error, when the code try to reach an endpoint in the web api i get this error, this is a self-signed certificates.
This is development environment, using visual studio 2019 to debug the code but no luck after trying to re-create the ssl certificates, checked guides about implement https in .net core apps, yet no luck.
Program.cs:
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseUrls("http://*:5000")
.UseSetting("https_port", "5001")
.UseKestrel(options =>
{
options.Listen(System.Net.IPAddress.Any, 5000);
options.Listen(System.Net.IPAddress.Any, 5001,
listenOptions => { listenOptions.UseHttps("localhost.pfx", "A2345_678b"); });
})
.UseStartup<Startup>();
}
ConfigureServices in Startup.cs:
services.AddSignalR();
services.AddMvc(options => options.EnableEndpointRouting = false).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
services.AddDefaultIdentity<IdentityUser>().AddRoles<IdentityRole>()
.AddEntityFrameworkStores<SmartContext>();
services.AddMvc(
options =>
{
options.SslPort = 5001;
options.Filters.Add(new RequireHttpsAttribute());
}
);
services.AddHttpsRedirection(options =>
{
options.RedirectStatusCode = StatusCodes.Status307TemporaryRedirect;
options.HttpsPort = 5001;
});
services.AddAntiforgery(
options =>
{
options.Cookie.Name = "_af";
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.HeaderName = "X-XSRF-TOKEN";
}
);
services.Configure<IdentityOptions>(options =>
{
// Password settings
options.Password.RequireDigit = false;
options.Password.RequiredLength = 6;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequireLowercase = false;
});
services.AddDbContext<SmartContext>(options =>
options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
All you should need to do is run the following in your project root:
dotnet dev-certs https --trust
This should pop a dialog asking you if you want to add the cert to your trusted store, which you obviously should accept. You'll need to do this for each project. For example, it's not enough to trust your web app, if that web app is connecting to an API app. You need to do the same thing for the API app so that both certs are trusted.

IIS Express messing with HTTP Status on exception

I have an ASP.NET Core 2.1 REST API app. I want to be able to run it as a windows service as well as on IIS so I have created a startup project for each and they use a shared Core library for the "guts" of the app. I am using CORS which I think may be coming into play.
The Service project runs normally under dotnet.exe from visual studio and the IIS project runs using IIS Express from visual studio. Their startup code differs very little and is at the end of this post.
Like most rest API's, I want to rely on Http status codes to convey information. On one endpoint, I want to use the RequestSizeLimitAttribute to return a 413 status if the request exceeds a certain limit.
The windows service project behaves correctly, returning the correct status codes to the client. But when I run as IIS Express, the client first sends an OPTIONS request and gets a 204 back (as expected), but then the response after that is always a 502-Bad Gateway.
Does IIS mess with things if an exception happens because of the RequestSizeLimitAttribute?
//IIS Express Project Startup Code
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IConfiguration config => new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile("appsettings.Development.json", optional: true)
.Build();
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseConfiguration(config)
.UseStartup<Startup>()
.Build();
AND
//Windows Service startup code
public static IConfiguration config => new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile("appsettings.Development.json", optional: true)
.Build();
public static void Main(string[] args)
{
RunWebHost(args, logger);
}
public static void RunWebHost(string[] args)
{
var isService = true;
if (Debugger.IsAttached || args.Contains("--console"))
{
isService = false;
}
var pathToContentRoot = Directory.GetCurrentDirectory();
if (isService)
{
var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
pathToContentRoot = Path.GetDirectoryName(pathToExe);
}
var webHostArgs = args.Where(arg => arg != "--console").ToArray();
var host = WebHost.CreateDefaultBuilder(webHostArgs)
.UseContentRoot(pathToContentRoot)
.UseConfiguration(config)
.UseStartup<Startup>()
.Build();
if (isService)
{
host.RunAsService();
}
else
{
host.Run();
}
}

Categories