From bea394cbaf43836a88664b44cc6645df64d035a2 Mon Sep 17 00:00:00 2001 From: Uncled1023 Date: Sat, 27 Jan 2018 20:23:56 -0800 Subject: [PATCH] Added IP and Referrer blacklists. --- Teknik/App_Data/ipBlacklist.txt | 0 Teknik/App_Data/referrerBlacklist.txt | 1 + Teknik/Modules/BlacklistModule.cs | 178 ++++++++++++++++++++++++++ Teknik/Modules/UserAuthModule.cs | 65 +++++----- Teknik/Teknik.csproj | 3 + Teknik/Web.config | 1 + Utilities/Configuration/Config.cs | 94 +++++++------- 7 files changed, 265 insertions(+), 77 deletions(-) create mode 100644 Teknik/App_Data/ipBlacklist.txt create mode 100644 Teknik/App_Data/referrerBlacklist.txt create mode 100644 Teknik/Modules/BlacklistModule.cs diff --git a/Teknik/App_Data/ipBlacklist.txt b/Teknik/App_Data/ipBlacklist.txt new file mode 100644 index 0000000..e69de29 diff --git a/Teknik/App_Data/referrerBlacklist.txt b/Teknik/App_Data/referrerBlacklist.txt new file mode 100644 index 0000000..25dafa8 --- /dev/null +++ b/Teknik/App_Data/referrerBlacklist.txt @@ -0,0 +1 @@ +xn--cckl0itdpc9763ahlyc.tv diff --git a/Teknik/Modules/BlacklistModule.cs b/Teknik/Modules/BlacklistModule.cs new file mode 100644 index 0000000..f53ed27 --- /dev/null +++ b/Teknik/Modules/BlacklistModule.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.IO; +using System.Linq; +using System.ServiceModel.Channels; +using System.Web; +using System.Web.Caching; +using System.Web.Helpers; +using System.Web.Mvc; +using System.Web.Routing; +using Teknik.Areas.Error.Controllers; +using Teknik.Configuration; + +namespace Teknik.Modules +{ + public class BlacklistModule : IHttpModule + { + + private EventHandler onBeginRequest; + + public BlacklistModule() + { + onBeginRequest = new EventHandler(this.HandleBeginRequest); + } + + void IHttpModule.Dispose() + { + } + + void IHttpModule.Init(HttpApplication context) + { + context.BeginRequest += onBeginRequest; + } + + #region Referrer Info + private const string BLOCKEDREFERRERKEY = "BlockedReferrer"; + private static string referrerFileName = null; + private static object referrerFileNameObj = new object(); + + public static string GetReferrerFilePath() + { + if (referrerFileName != null) + return referrerFileName; + lock (referrerFileNameObj) + { + if (referrerFileName == null) + { + Config config = Config.Load(); + referrerFileName = config.ReferrerBlacklistFile; + } + } + + return referrerFileName; + } + #endregion + + #region IP Info + private const string BLOCKEDIPKEY = "BlockedIP"; + private static string ipFileName = null; + private static object ipFileNameObj = new object(); + + public static string GetIPFilePath() + { + if (ipFileName != null) + return ipFileName; + lock (ipFileNameObj) + { + if (ipFileName == null) + { + Config config = Config.Load(); + ipFileName = config.IPBlacklistFile; + } + } + + return ipFileName; + } + #endregion + + public static StringDictionary GetFileData(HttpContext context, string key, Func fn) + { + StringDictionary data = (StringDictionary)context.Cache[key]; + if (data == null) + { + string filePath = fn(); + data = GetFileLines(filePath); + context.Cache.Insert(key, data, new CacheDependency(filePath)); + } + + return data; + } + + public static 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; + } + + private void HandleBeginRequest(object sender, EventArgs evargs) + { + HttpApplication app = sender as HttpApplication; + + if (app != null) + { + bool blocked = false; + string blockReason = string.Empty; + + #region Detect Blacklisted IPs + if (!blocked) + { + string IPAddr = app.Context.Request.ServerVariables["REMOTE_ADDR"]; + if (!string.IsNullOrEmpty(IPAddr)) + { + StringDictionary badIPs = GetFileData(app.Context, BLOCKEDIPKEY, GetIPFilePath); + + 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 = app.Context.Request.UrlReferrer?.Host; + if (!string.IsNullOrEmpty(referrer)) + { + StringDictionary badReferrers = GetFileData(app.Context, BLOCKEDREFERRERKEY, GetReferrerFilePath); + + blocked |= (badReferrers != null && badReferrers.ContainsKey(referrer)); + 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 + app.Context.Response.Clear(); + + RouteData routeData = new RouteData(); + routeData.DataTokens.Add("namespaces", new[] { typeof(ErrorController).Namespace }); + routeData.DataTokens.Add("area", "Error"); + routeData.Values.Add("controller", "Error"); + routeData.Values.Add("scheme", "https"); + routeData.Values.Add("action", "Http403"); + + // Clear the error on server. + app.Context.Server.ClearError(); + + // Avoid IIS7 getting in the middle + app.Context.Response.TrySkipIisCustomErrors = true; + + string jsonResult = Json.Encode(new { error = new { type = "Blacklist", message = blockReason } }); + app.Context.Response.Write(jsonResult); + app.Context.Response.End(); + return; + } + } + } + } +} diff --git a/Teknik/Modules/UserAuthModule.cs b/Teknik/Modules/UserAuthModule.cs index 3742ce4..9c6748c 100644 --- a/Teknik/Modules/UserAuthModule.cs +++ b/Teknik/Modules/UserAuthModule.cs @@ -29,46 +29,45 @@ namespace Teknik.Modules string username = string.Empty; bool hasAuthToken = false; - if (context.Request != null) + if (context.Request.Headers.HasKeys()) { - if (context.Request.Headers.HasKeys()) + string auth = context.Request.Headers["Authorization"]; + if (!string.IsNullOrEmpty(auth)) { - string auth = context.Request.Headers["Authorization"]; - if (!string.IsNullOrEmpty(auth)) + string[] parts = auth.Split(new char[] {' '}, StringSplitOptions.RemoveEmptyEntries); + string type = string.Empty; + string value = string.Empty; + if (parts.Length > 0) { - string[] parts = auth.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - string type = string.Empty; - string value = string.Empty; - if (parts.Length > 0) - { - type = parts[0].ToLower(); - } - if (parts.Length > 1) - { - value = parts[1]; - } + type = parts[0].ToLower(); + } - using (TeknikEntities entities = new TeknikEntities()) + if (parts.Length > 1) + { + value = parts[1]; + } + + using (TeknikEntities entities = new TeknikEntities()) + { + // Get the user information based on the auth type + switch (type) { - // Get the user information based on the auth type - switch (type) - { - case "basic": - KeyValuePair authCreds = StringHelper.ParseBasicAuthHeader(value); + case "basic": + KeyValuePair authCreds = StringHelper.ParseBasicAuthHeader(value); - bool tokenValid = UserHelper.UserTokenCorrect(entities, authCreds.Key, authCreds.Value); - if (tokenValid) - { - // it's valid, so let's update it's Last Used date - UserHelper.UpdateTokenLastUsed(entities, authCreds.Key, authCreds.Value, DateTime.Now); + bool tokenValid = UserHelper.UserTokenCorrect(entities, authCreds.Key, authCreds.Value); + if (tokenValid) + { + // it's valid, so let's update it's Last Used date + UserHelper.UpdateTokenLastUsed(entities, authCreds.Key, authCreds.Value, DateTime.Now); - // Set the username - username = authCreds.Key; - } - break; - default: - break; - } + // Set the username + username = authCreds.Key; + } + + break; + default: + break; } } } diff --git a/Teknik/Teknik.csproj b/Teknik/Teknik.csproj index 3e06417..7c2fc69 100644 --- a/Teknik/Teknik.csproj +++ b/Teknik/Teknik.csproj @@ -321,6 +321,7 @@ + @@ -395,6 +396,8 @@ + + Always diff --git a/Teknik/Web.config b/Teknik/Web.config index 9805ea8..7692517 100644 --- a/Teknik/Web.config +++ b/Teknik/Web.config @@ -56,6 +56,7 @@ + diff --git a/Utilities/Configuration/Config.cs b/Utilities/Configuration/Config.cs index 6aabf36..2a6c88a 100644 --- a/Utilities/Configuration/Config.cs +++ b/Utilities/Configuration/Config.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Threading; using Newtonsoft.Json; @@ -29,6 +29,8 @@ namespace Teknik.Configuration private string _Salt1; private string _Salt2; private string _CdnHost; + private string _IPBlacklistFile; + private string _ReferrerBlacklistFile; private UserConfig _UserConfig; private ContactConfig _ContactConfig; private EmailConfig _EmailConfig; @@ -46,21 +48,23 @@ namespace Teknik.Configuration private PiwikConfig _PiwikConfig; private IRCConfig _IRCConfig; - public bool DevEnvironment { get { return _DevEnvironment; } set { _DevEnvironment = value; } } - public bool Migrate { get { return _Migrate; } set { _Migrate = value; } } - public bool UseCdn { get { return _UseCdn; } set { _UseCdn = value; } } + public bool DevEnvironment { get { return _DevEnvironment; } set { _DevEnvironment = value; } } + public bool Migrate { get { return _Migrate; } set { _Migrate = value; } } + public bool UseCdn { get { return _UseCdn; } set { _UseCdn = value; } } // Site Information - public string Title { get { return _Title; } set { _Title = value; } } - public string Description { get { return _Description; } set { _Description = value; } } - public string Author { get { return _Author; } set { _Author = value; } } - public string Host { get { return _Host; } set { _Host = value; } } - public string SupportEmail { get { return _SupportEmail; } set { _SupportEmail = value; } } - public string NoReplyEmail { get { return _NoReplyEmail; } set { _NoReplyEmail = value; } } - public string BitcoinAddress { get { return _BitcoinAddress; } set { _BitcoinAddress = value; } } - public string Salt1 { get { return _Salt1; } set { _Salt1 = value; } } - public string Salt2 { get { return _Salt2; } set { _Salt2 = value; } } - public string CdnHost { get { return _CdnHost; } set { _CdnHost = value; } } + public string Title { get { return _Title; } set { _Title = value; } } + public string Description { get { return _Description; } set { _Description = value; } } + public string Author { get { return _Author; } set { _Author = value; } } + public string Host { get { return _Host; } set { _Host = value; } } + public string SupportEmail { get { return _SupportEmail; } set { _SupportEmail = value; } } + public string NoReplyEmail { get { return _NoReplyEmail; } set { _NoReplyEmail = value; } } + public string BitcoinAddress { get { return _BitcoinAddress; } set { _BitcoinAddress = value; } } + public string Salt1 { get { return _Salt1; } set { _Salt1 = value; } } + public string Salt2 { get { return _Salt2; } set { _Salt2 = value; } } + public string CdnHost { get { return _CdnHost; } set { _CdnHost = value; } } + public string IPBlacklistFile { get { return _IPBlacklistFile;} set { _IPBlacklistFile = value; }} + public string ReferrerBlacklistFile { get { return _ReferrerBlacklistFile;} set { _ReferrerBlacklistFile = value; }} // User Configuration public UserConfig UserConfig { get { return _UserConfig; } set { _UserConfig = value; } } @@ -122,35 +126,37 @@ namespace Teknik.Configuration public void SetDefaults() { - DevEnvironment = false; - Migrate = false; - UseCdn = false; - Title = string.Empty; - Description = string.Empty; - Author = string.Empty; - Host = string.Empty; - SupportEmail = string.Empty; - NoReplyEmail = string.Empty; - BitcoinAddress = string.Empty; - Salt1 = string.Empty; - Salt2 = string.Empty; - CdnHost = string.Empty; - UserConfig = new UserConfig(); - EmailConfig = new EmailConfig(); - ContactConfig = new ContactConfig(); - GitConfig = new GitConfig(); - BlogConfig = new BlogConfig(); - UploadConfig = new UploadConfig(); - PasteConfig = new PasteConfig(); - ApiConfig = new ApiConfig(); - PodcastConfig = new PodcastConfig(); - StreamConfig = new StreamConfig(); - ShortenerConfig = new ShortenerConfig(); - VaultConfig = new VaultConfig(); - StatusConfig = new StatusConfig(); - LoggingConfig = new LoggingConfig(); - PiwikConfig = new PiwikConfig(); - IRCConfig = new IRCConfig(); + DevEnvironment = false; + Migrate = false; + UseCdn = false; + Title = string.Empty; + Description = string.Empty; + Author = string.Empty; + Host = string.Empty; + SupportEmail = string.Empty; + NoReplyEmail = string.Empty; + BitcoinAddress = string.Empty; + Salt1 = string.Empty; + Salt2 = string.Empty; + CdnHost = string.Empty; + IPBlacklistFile = string.Empty; + ReferrerBlacklistFile = string.Empty; + UserConfig = new UserConfig(); + EmailConfig = new EmailConfig(); + ContactConfig = new ContactConfig(); + GitConfig = new GitConfig(); + BlogConfig = new BlogConfig(); + UploadConfig = new UploadConfig(); + PasteConfig = new PasteConfig(); + ApiConfig = new ApiConfig(); + PodcastConfig = new PodcastConfig(); + StreamConfig = new StreamConfig(); + ShortenerConfig = new ShortenerConfig(); + VaultConfig = new VaultConfig(); + StatusConfig = new StatusConfig(); + LoggingConfig = new LoggingConfig(); + PiwikConfig = new PiwikConfig(); + IRCConfig = new IRCConfig(); } public static Config Deserialize(string text) @@ -210,4 +216,4 @@ namespace Teknik.Configuration File.WriteAllText(path, configContents); } } -} \ No newline at end of file +}