How do I force https redirect on static files in ASP.NET Core 2.1? - asp.net-core

I have a ASP.NET Core app that has HTTPS redirection enabled. The problem is that HTTPS redirection doesn´t work on static files, so the front-end is in a different port than the back-end when I enter the site with http and this causes CORS to block all requests made to the back-end. I tried allowing CORS to all requests (for testing, of course) but no matter what I did, the requests failed. Only visiting the front end with https worked.How do I force https redirect on static files with ASP.NET Core?

Based on my expeirence, you must add this code to the startup.cs file
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpsRedirection(options =>
{
options.HttpsPort = 443;
});
You have to love Microsoft documentation. 6 paragraphs below the incomplete example they tell you about this requirement:
If no port is set:
Requests aren't redirected.
You can set port using the following techniques:
The port can be configured by setting the:
ASPNETCORE_HTTPS_PORT environment variable.
http_port host configuration key (for example, via hostsettings.json or a command
line argument).
HttpsRedirectionOptions.HttpsPort. See the preceding

This worked for me.
app.UseStaticFiles(new StaticFileOptions()
{
OnPrepareResponse = (context) =>
{
var request = context.Context.Request;
var response = context.Context.Response;
UrlRewriteUtils.RedirectIfHttp(request, response);
}
});
And here is the Utility method.
public class UrlRewriteUtils
{
public static void RedirectIfHttp(HttpRequest request, HttpResponse response)
{
string reqProtocol;
if (request.Headers.ContainsKey("X-Forwarded-Proto"))
reqProtocol = request.Headers["X-Forwarded-Proto"][0];
else if (request.IsLocal())
reqProtocol = "https";
else
reqProtocol = request.IsHttps ? "https" : "http";
if (reqProtocol.ToLower() != "https")
{
var newUrl = new StringBuilder()
.Append("https://").Append(request.Host)
.Append(request.PathBase).Append(request.Path)
.Append(request.QueryString);
response.Redirect(newUrl.ToString(), true);
}
}
}

Related

What is the difference between public and private HTTPS connections?

I have a Net 5 Blazor server app published on a productive server and used a valid certificate for https requests, which works fine. Newly I added a HubConnection class to support SignalR notifications between web pages. But if I call the web page through an public URL like https://crm.example.com/call, I get the following error, although the same page works fine if I call it through an internal URL like https://10.12.0.151/call:
Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host..
I don't know why it happens, what is the difference between a public and private HTTPS connection? For public connection it uses a valid public certificate and actually works fine if I deactivate the SignalR notification.
Because of the application working just fine if I call the page with an internal URL, seems that all prerequisite for using SignalR are included such as installing WebSocket-Protocol feature on the server and so on.
The following snipet shows the part of code:
try
{
string sHubUrl = NavManager.BaseUri;
sHubUrl = sHubUrl.TrimEnd('/') + "/call";
LogBuilder.LogInfo($"URL in Call.NotificationInit: " + sHubUrl);
hubConnection = new HubConnectionBuilder()
.WithUrl(sHubUrl, options => {
options.UseDefaultCredentials = true;
options.HttpMessageHandlerFactory = (msg) =>
{
if (msg is HttpClientHandler clientHandler)
{
System.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls12;
// bypass SSL certificate
clientHandler.ServerCertificateCustomValidationCallback +=
(sender, certificate, chain, sslPolicyErrors) => { return true; };
}
return msg;
};
})
.WithAutomaticReconnect()
.Build();
hubConnection.On<string, string>("NewMessage", ReceivedNotification);
await hubConnection.StartAsync();
}
catch (Exception ex)
{
LogBuilder.LogExecption("Exception at Call.NotificationInit");
}
What else should I do? Can anyone help me to solve this problem?

Querystring issue with .Net Core url rewrite

Working on an app in ASP.NET Core I am having a problem trying to rewrite a single url like:
https://localhost:44318/Details?content=My-url-to-rewrite&id=221
into
https://localhost:44318/mypage
The code I am using at the beginning of the Configure() method into Startup.cs is the following:
app.UseRewriter(new RewriteOptions()
.AddRewrite(#"^Details?content=My-url-to-rewrite&id=221", "/mypage", skipRemainingRules: true));
The strange thing is that if I try to rewrite the url without the querystring like the following, it works
app.UseRewriter(new RewriteOptions()
.AddRewrite(#"^Details", "/mypage", skipRemainingRules: true));
And even adding the question mark to append the querystring it works, like the following
app.UseRewriter(new RewriteOptions()
.AddRewrite(#"^Details?", "/mypage", skipRemainingRules: true));
But as soon as I add even a single character after the question mark the url is not rewritten and the page gets linked as usually without any error.
Any idea?
Thanks in advance.
You can implement your own ReWrite Middleware :
Configure :
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
....
var options = new RewriteOptions()
.Add(RedirectMyRequests);
app.UseRewriter(options);
...
}
static void RedirectMyRequests(RewriteContext context)
{
var request = context.HttpContext.Request;
// Because we're redirecting back to the same app, stop processing if the request has already been redirected
//If the modified path is similar with previous, add if statemetn to stop processing
//check the request path and request QueryString
if (request.Path.Value.StartsWith("/Details", StringComparison.OrdinalIgnoreCase) && request.QueryString.Value.StartsWith("?content", StringComparison.OrdinalIgnoreCase))
{
if (request.QueryString.Value.Split('&')[1].StartsWith("id", StringComparison.OrdinalIgnoreCase))
{
var response = context.HttpContext.Response;
response.StatusCode = StatusCodes.Status301MovedPermanently;
context.Result = RuleResult.EndResponse;
//change it with needed path, it is combined with request path and request QueryString
response.Headers[HeaderNames.Location] = "/Home/Privacy";
}
}
}

Redirect Non WWW to WWW [duplicate]

This question already has answers here:
Redirect Non WWW to WWW using Asp.Net Core Middleware
(10 answers)
Closed 4 years ago.
On an ASP.Net Core application startup I have:
RewriteOptions rewriteOptions = new RewriteOptions();
rewriteOptions.AddRedirectToHttps();
applicationBuilder.UseRewriter(rewriteOptions);
When in Production I need to redirect all Non WWW to WWW.
For example:
domain.com/contact > www.domain.com/contact
How can I do this using Rewrite Middleware?
You could create a custom rule by creating a class and implementing the IRule interface (see below). I think there must be a better way to construct the redirectUrl though.
public class CanonicalDomainRewriteRule : IRule
{
public void ApplyRule(RewriteContext context)
{
HttpRequest request = context.HttpContext.Request;
if (request.Host.Value.StartsWith("www.", StringComparison.OrdinalIgnoreCase))
{
return;
}
else
{
HttpResponse response = context.HttpContext.Response;
string redirectUrl = $"{request.Scheme}://www.{request.Host}{request.Path}{request.QueryString}";
response.Headers[HeaderNames.Location] = redirectUrl;
response.StatusCode = StatusCodes.Status301MovedPermanently;
context.Result = RuleResult.EndResponse;
}
}
}
You can add this rule as follows in your ConfigureServices method:
RewriteOptions rewriteOptions = new RewriteOptions()
.Add(new CanonicalDomainRewriteRule());
app.UseRewriter(rewriteOptions);
See Extensions and Options: IRule-based rule for further examples.

How might I get to the SSL URL defined in the properties of an MVC project?

I have SSL enabled and I am trying to write my own RequireHttpsAttribute to allow easy testing with IIS Express. When it redirects, it redirects to port 44301, but I don't want to hard code this, I want to read this from the existing configuration.
public sealed class RequireHttpsAlternativePortAttribute : RequireHttpsAttribute
{
protected override void HandleNonHttpsRequest(AuthorizationContext filterContext)
{
base.HandleNonHttpsRequest(filterContext);
//alter the port in the base classes result
var baseRedirect = filterContext.Result as RedirectResult;
if (baseRedirect == null)
{
throw new Exception("No redirect was suppied from the base class");
}
var builder = new UriBuilder(baseRedirect.Url);
var targetPort = 44301; //need this from settings
if (builder.Port == targetPort) return; //already correct port
//change the port
builder.Port = targetPort;
var redirectResult = new RedirectResult(builder.ToString());
filterContext.Result = redirectResult;
}
}
44301 is defined in the MVC project properties under SSL URL. My question is, is this accessible during runtime anywhere?

Using app from Apache in Tomcat application

Basically I have 2 RESTful services: one build in Java and using Tomcat server, and other build in PHP and using Apache Server.
Is there any way to configure such that the app from Tomcat becomes a consumer of the one from Apache?
The webservice from Tomcat is at address:
http://localhost:8080/myapp1
and the app from Apache is at address:
http://localhost:80/myapp2.
What I want is to use the responses of the RESTful service on Apache in the Tomcat one, something like this to use from the Java code:
HttpGet httpget = new HttpGet(http://localhost:80/myapp2/items);
Currently I am receiving 404-Not Found. Is there any way to do this? Or is there another way to make the services communicate?
Forgot to post the answer. I feel so dumb - I was having a mistake in my code. It works as expected. Here is a simple example of calling the Apache server from Tomcat:
final static String BASE_URL = "http://localhost:80/proiect/";
private String getResponse(String title) {
HttpClient httpclient = new DefaultHttpClient();
String url = (title != null && title.length() > 0) ? BASE_URL + "?title=" + title : BASE_URL;
HttpGet httpget = new HttpGet(url);
String response;
try {
ResponseHandler<String> responseHandler = new BasicResponseHandler();
response = httpclient.execute(httpget, responseHandler);
return response;
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}