11 changed files with 50 additions and 494 deletions
@ -1,128 +0,0 @@
@@ -1,128 +0,0 @@
|
||||
using Microsoft.AspNetCore.Builder; |
||||
using Microsoft.AspNetCore.Http; |
||||
using Microsoft.AspNetCore.Routing; |
||||
using Microsoft.Extensions.Caching.Memory; |
||||
using Newtonsoft.Json; |
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Collections.Specialized; |
||||
using System.IO; |
||||
using System.Linq; |
||||
using System.Threading.Tasks; |
||||
using Teknik.Configuration; |
||||
|
||||
namespace Teknik.Middleware |
||||
{ |
||||
public class BlacklistMiddleware |
||||
{ |
||||
private readonly RequestDelegate _next; |
||||
private readonly IMemoryCache _cache; |
||||
|
||||
public BlacklistMiddleware(RequestDelegate next, IMemoryCache cache) |
||||
{ |
||||
_next = next; |
||||
_cache = cache; |
||||
} |
||||
|
||||
public async Task Invoke(HttpContext context, Config config) |
||||
{ |
||||
// Beggining of Request
|
||||
bool blocked = false; |
||||
string blockReason = string.Empty; |
||||
|
||||
#region Detect Blacklisted IPs
|
||||
if (!blocked) |
||||
{ |
||||
string IPAddr = context.Request.HttpContext.Connection.RemoteIpAddress.ToString(); |
||||
if (!string.IsNullOrEmpty(IPAddr)) |
||||
{ |
||||
StringDictionary badIPs = GetFileData(context, "BlockedIPs", config.IPBlacklistFile); |
||||
|
||||
blocked |= (badIPs != null && badIPs.ContainsKey(IPAddr)); |
||||
blockReason = $"This IP address ({IPAddr}) has been blacklisted. If you feel this is in error, please contact support@teknik.io for assistance."; |
||||
} |
||||
} |
||||
#endregion
|
||||
|
||||
#region Detect Blacklisted Referrers
|
||||
if (!blocked) |
||||
{ |
||||
string referrer = context.Request.Headers["Referer"].ToString(); |
||||
string referrerHost = referrer; |
||||
try |
||||
{ |
||||
var referrerUri = new Uri(referrer); |
||||
referrerHost = referrerUri.Host; |
||||
} catch |
||||
{ } |
||||
if (!string.IsNullOrEmpty(referrer)) |
||||
{ |
||||
StringDictionary badReferrers = GetFileData(context, "BlockedReferrers", config.ReferrerBlacklistFile); |
||||
|
||||
if (badReferrers != null) |
||||
{ |
||||
blocked |= badReferrers.ContainsKey(referrer) || badReferrers.ContainsKey(referrerHost); |
||||
blockReason = $"This referrer ({referrer}) has been blacklisted. If you feel this is in error, please contact support@teknik.io for assistance."; |
||||
} |
||||
} |
||||
} |
||||
#endregion
|
||||
|
||||
if (blocked) |
||||
{ |
||||
// Clear the response
|
||||
context.Response.Clear(); |
||||
|
||||
string jsonResult = JsonConvert.SerializeObject(new { error = new { type = "Blacklist", message = blockReason } }); |
||||
await context.Response.WriteAsync(jsonResult); |
||||
return; |
||||
} |
||||
|
||||
await _next.Invoke(context); |
||||
|
||||
// End of request
|
||||
} |
||||
|
||||
public StringDictionary GetFileData(HttpContext context, string key, string filePath) |
||||
{ |
||||
StringDictionary data; |
||||
if (!_cache.TryGetValue(key, out data)) |
||||
{ |
||||
data = GetFileLines(filePath); |
||||
_cache.Set(key, data); |
||||
} |
||||
|
||||
return data; |
||||
} |
||||
|
||||
public StringDictionary GetFileLines(string configPath) |
||||
{ |
||||
StringDictionary retval = new StringDictionary(); |
||||
if (File.Exists(configPath)) |
||||
{ |
||||
using (StreamReader sr = new StreamReader(configPath)) |
||||
{ |
||||
String line; |
||||
while ((line = sr.ReadLine()) != null) |
||||
{ |
||||
line = line.Trim(); |
||||
if (line.Length != 0) |
||||
{ |
||||
retval.Add(line, null); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
return retval; |
||||
} |
||||
} |
||||
|
||||
public static class BlacklistMiddlewareExtensions |
||||
{ |
||||
public static IApplicationBuilder UseBlacklist(this IApplicationBuilder builder) |
||||
{ |
||||
return builder.UseMiddleware<BlacklistMiddleware>(); |
||||
} |
||||
} |
||||
} |
@ -1,73 +0,0 @@
@@ -1,73 +0,0 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using System.Threading.Tasks; |
||||
using Microsoft.AspNetCore.Builder; |
||||
using Microsoft.AspNetCore.Http; |
||||
using Teknik.Configuration; |
||||
using Teknik.Utilities; |
||||
using Teknik.Utilities.Routing; |
||||
|
||||
namespace Teknik.Middleware |
||||
{ |
||||
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
|
||||
public class CORSMiddleware |
||||
{ |
||||
private readonly RequestDelegate _next; |
||||
|
||||
public CORSMiddleware(RequestDelegate next) |
||||
{ |
||||
_next = next; |
||||
} |
||||
|
||||
public Task InvokeAsync(HttpContext httpContext, Config config) |
||||
{ |
||||
// Allow this domain, or everything if local
|
||||
string origin = (httpContext.Request.IsLocal()) ? "*" : httpContext.Request.Headers["Origin"].ToString(); |
||||
|
||||
// Is the referrer set to the CDN and we are using a CDN?
|
||||
if (config.UseCdn && !string.IsNullOrEmpty(config.CdnHost)) |
||||
{ |
||||
try |
||||
{ |
||||
string host = httpContext.Request.Headers["Host"]; |
||||
Uri uri = new Uri(config.CdnHost); |
||||
if (host == uri.Host) |
||||
origin = host; |
||||
} |
||||
catch { } |
||||
} |
||||
|
||||
string domain = (string.IsNullOrEmpty(origin)) ? string.Empty : origin.GetDomain(); |
||||
|
||||
if (string.IsNullOrEmpty(origin)) |
||||
{ |
||||
string host = httpContext.Request.Headers["Host"]; |
||||
string sub = host.GetSubdomain(); |
||||
origin = (string.IsNullOrEmpty(sub)) ? config.Host : sub + "." + config.Host; |
||||
} |
||||
else |
||||
{ |
||||
if (domain != config.Host) |
||||
{ |
||||
string sub = origin.GetSubdomain(); |
||||
origin = (string.IsNullOrEmpty(sub)) ? config.Host : sub + "." + config.Host; |
||||
} |
||||
} |
||||
|
||||
httpContext.Response.Headers.Append("Access-Control-Allow-Origin", origin); |
||||
httpContext.Response.Headers.Append("Vary", "Origin"); |
||||
|
||||
return _next(httpContext); |
||||
} |
||||
} |
||||
|
||||
// Extension method used to add the middleware to the HTTP request pipeline.
|
||||
public static class CORSMiddlewareExtensions |
||||
{ |
||||
public static IApplicationBuilder UseCORS(this IApplicationBuilder builder) |
||||
{ |
||||
return builder.UseMiddleware<CORSMiddleware>(); |
||||
} |
||||
} |
||||
} |
@ -1,93 +0,0 @@
@@ -1,93 +0,0 @@
|
||||
using Microsoft.AspNetCore.Builder; |
||||
using Microsoft.AspNetCore.Diagnostics; |
||||
using Microsoft.AspNetCore.Http; |
||||
using Microsoft.AspNetCore.Mvc; |
||||
using Microsoft.AspNetCore.Routing; |
||||
using Microsoft.Extensions.DependencyInjection; |
||||
using Microsoft.Extensions.Logging; |
||||
using Microsoft.Extensions.Options; |
||||
using Newtonsoft.Json; |
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Globalization; |
||||
using System.Linq; |
||||
using System.Text; |
||||
using System.Threading.Tasks; |
||||
using Teknik.Areas.Error.Controllers; |
||||
using Teknik.Configuration; |
||||
using Teknik.Data; |
||||
using Teknik.Logging; |
||||
using Teknik.Utilities; |
||||
|
||||
namespace Teknik.Middleware |
||||
{ |
||||
public class ErrorHandlerMiddleware |
||||
{ |
||||
private readonly RequestDelegate _next; |
||||
|
||||
public ErrorHandlerMiddleware(RequestDelegate next) |
||||
{ |
||||
_next = next; |
||||
} |
||||
|
||||
public async Task Invoke(HttpContext httpContext, ILogger<Logger> logger, Config config, TeknikEntities dbContext, ErrorController errorController) |
||||
{ |
||||
var statusCodeFeature = new StatusCodePagesFeature(); |
||||
httpContext.Features.Set<IStatusCodePagesFeature>(statusCodeFeature); |
||||
|
||||
Exception exception = null; |
||||
try |
||||
{ |
||||
await _next(httpContext); |
||||
} |
||||
catch (Exception ex) |
||||
{ |
||||
exception = ex; |
||||
} |
||||
|
||||
if (!statusCodeFeature.Enabled) |
||||
{ |
||||
// Check if the feature is still available because other middleware (such as a web API written in MVC) could
|
||||
// have disabled the feature to prevent HTML status code responses from showing up to an API client.
|
||||
return; |
||||
} |
||||
|
||||
// Do nothing if a response body has already been provided or not 404 response
|
||||
if (httpContext.Response.HasStarted) |
||||
{ |
||||
return; |
||||
} |
||||
|
||||
// Detect if there is a response code or exception occured
|
||||
if ((httpContext.Response.StatusCode >= 400 && httpContext.Response.StatusCode <= 600) || exception != null) |
||||
{ |
||||
var routeData = httpContext.GetRouteData() ?? new RouteData(); |
||||
|
||||
var context = new ControllerContext(); |
||||
context.HttpContext = httpContext; |
||||
context.RouteData = routeData; |
||||
context.ActionDescriptor = new Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor(); |
||||
|
||||
errorController.ControllerContext = context; |
||||
|
||||
if (httpContext.Response.StatusCode == 500 || exception != null) |
||||
{ |
||||
await errorController.Http500(exception).ExecuteResultAsync(context); |
||||
} |
||||
else |
||||
{ |
||||
await errorController.HttpError(httpContext.Response.StatusCode).ExecuteResultAsync(context); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Extension method used to add the middleware to the HTTP request pipeline.
|
||||
public static class SetupErrorHandlerMiddlewareExtensions |
||||
{ |
||||
public static IApplicationBuilder UseErrorHandler(this IApplicationBuilder builder, Config config) |
||||
{ |
||||
return builder.UseMiddleware<ErrorHandlerMiddleware>(); |
||||
} |
||||
} |
||||
} |
@ -1,68 +0,0 @@
@@ -1,68 +0,0 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.Linq; |
||||
using System.Threading.Tasks; |
||||
using Microsoft.AspNetCore.Builder; |
||||
using Microsoft.AspNetCore.Http; |
||||
using Teknik.Configuration; |
||||
using Teknik.Utilities; |
||||
|
||||
namespace Teknik.Middleware |
||||
{ |
||||
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
|
||||
public class PerformanceMonitorMiddleware |
||||
{ |
||||
private readonly RequestDelegate _next; |
||||
|
||||
public PerformanceMonitorMiddleware(RequestDelegate next) |
||||
{ |
||||
_next = next; |
||||
} |
||||
|
||||
public async Task Invoke(HttpContext httpContext, Config config) |
||||
{ |
||||
Stopwatch timer = new Stopwatch(); |
||||
timer.Start(); |
||||
|
||||
httpContext.Response.OnStarting(state => |
||||
{ |
||||
var context = (HttpContext)state; |
||||
|
||||
timer.Stop(); |
||||
|
||||
double ms = (double)timer.ElapsedMilliseconds; |
||||
string result = string.Format("{0:F0}", ms); |
||||
|
||||
if (!httpContext.Response.Headers.IsReadOnly) |
||||
httpContext.Response.Headers.Add("GenerationTime", result); |
||||
|
||||
return Task.CompletedTask; |
||||
}, httpContext); |
||||
|
||||
await _next(httpContext); |
||||
|
||||
// Don't interfere with non-HTML responses
|
||||
if (httpContext.Response.ContentType != null && httpContext.Response.ContentType.StartsWith("text/html") && httpContext.Response.StatusCode == 200 && !httpContext.Request.IsAjaxRequest()) |
||||
{ |
||||
double ms = (double)timer.ElapsedMilliseconds; |
||||
string result = string.Format("{0:F0}", ms); |
||||
|
||||
await httpContext.Response.WriteAsync( |
||||
"<script nonce=\"" + httpContext.Items[Constants.NONCE_KEY] + "\">" + |
||||
"var pageGenerationTime = '" + result + "';" + |
||||
"pageloadStopTimer();" + |
||||
"</script >"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Extension method used to add the middleware to the HTTP request pipeline.
|
||||
public static class PerformanceMonitorMiddlewareExtensions |
||||
{ |
||||
public static IApplicationBuilder UsePerformanceMonitor(this IApplicationBuilder builder) |
||||
{ |
||||
return builder.UseMiddleware<PerformanceMonitorMiddleware>(); |
||||
} |
||||
} |
||||
} |
@ -1,53 +0,0 @@
@@ -1,53 +0,0 @@
|
||||
using Microsoft.AspNetCore.Builder; |
||||
using Microsoft.AspNetCore.Http; |
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using System.Threading.Tasks; |
||||
using Teknik.Configuration; |
||||
|
||||
namespace Teknik.Middleware |
||||
{ |
||||
public class SecurityHeadersMiddleware |
||||
{ |
||||
private readonly RequestDelegate _next; |
||||
|
||||
public SecurityHeadersMiddleware(RequestDelegate next) |
||||
{ |
||||
_next = next; |
||||
} |
||||
|
||||
public Task Invoke(HttpContext httpContext, Config config) |
||||
{ |
||||
IHeaderDictionary headers = httpContext.Response.Headers; |
||||
|
||||
// Access Control
|
||||
headers.Append("Access-Control-Allow-Credentials", "true"); |
||||
headers.Append("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS"); |
||||
headers.Append("Access-Control-Allow-Headers", "Authorization, Accept, Origin, Content-Type, X-Requested-With, Connection, Transfer-Encoding"); |
||||
|
||||
// HSTS
|
||||
headers.Append("strict-transport-security", "max-age=31536000; includeSubdomains; preload"); |
||||
|
||||
// XSS Protection
|
||||
headers.Append("X-XSS-Protection", "1; mode=block"); |
||||
|
||||
// Content Type Options
|
||||
headers.Append("X-Content-Type-Options", "nosniff"); |
||||
|
||||
// Referrer Policy
|
||||
headers.Append("Referrer-Policy", "no-referrer, strict-origin-when-cross-origin"); |
||||
|
||||
return _next(httpContext); |
||||
} |
||||
} |
||||
|
||||
// Extension method used to add the middleware to the HTTP request pipeline.
|
||||
public static class SecurityHeadersMiddlewareExtensions |
||||
{ |
||||
public static IApplicationBuilder UseSecurityHeaders(this IApplicationBuilder builder) |
||||
{ |
||||
return builder.UseMiddleware<SecurityHeadersMiddleware>(); |
||||
} |
||||
} |
||||
} |
@ -1,42 +0,0 @@
@@ -1,42 +0,0 @@
|
||||
using Microsoft.AspNetCore.Builder; |
||||
using Microsoft.AspNetCore.Http; |
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using System.Text; |
||||
using System.Threading.Tasks; |
||||
using Teknik.Configuration; |
||||
using Teknik.Utilities; |
||||
|
||||
namespace Teknik.Middleware |
||||
{ |
||||
public class SetupHttpContextMiddleware |
||||
{ |
||||
private readonly RequestDelegate _next; |
||||
|
||||
public SetupHttpContextMiddleware(RequestDelegate next) |
||||
{ |
||||
_next = next; |
||||
} |
||||
|
||||
public async Task Invoke(HttpContext httpContext, Config config) |
||||
{ |
||||
// Setup the HTTP Context for everything else
|
||||
|
||||
// Generate the NONCE used for this request
|
||||
string nonce = Convert.ToBase64String(Encoding.UTF8.GetBytes(StringHelper.RandomString(24))); |
||||
httpContext.Items[Constants.NONCE_KEY] = nonce; |
||||
|
||||
await _next(httpContext); |
||||
} |
||||
} |
||||
|
||||
// Extension method used to add the middleware to the HTTP request pipeline.
|
||||
public static class SetupHttpContextMiddlewareExtensions |
||||
{ |
||||
public static IApplicationBuilder UseHttpContextSetup(this IApplicationBuilder builder) |
||||
{ |
||||
return builder.UseMiddleware<SetupHttpContextMiddleware>(); |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue