Exemplo n.º 1
0
        /// <summary>
        /// Process and track REST request tracking.
        /// </summary>
        /// <example>
        /// One line of code will enforce rate limiting and blacklist a repeated bad request offender,
        /// returning a class object that contains the HttpStatusCode and messaging.
        /// <code>
        /// var jsonData = RestSecurity.RateLimit("169.128.25.23", "2018-12-01T23:59:00Z", "member/signin", restApiSettings);
        /// if (jsonData.StatusCode == HttpStatusCode.OK)
        ///	{
        ///	    // Do stuff and set the status code and messaging properties in jsonData.
        ///     // After all processing, execute ProcessRestSecurityTracking() so subsequent
        ///     // calls to RateLimit() are aware of the ultimate result of this request.
        ///	    RestSecurity.ProcessRestSecurityTracking(jsonData, "member/signin", restApiSettings);
        ///	}
        /// </code>
        /// </example>
        /// <param name="result">REST request result to process and track</param>
        /// <param name="restPath">REST path to use as an identifier so that each path has its own rate limiting, etc. (e.g. "/member/signin")</param>
        /// <param name="config">REST security configuration</param>
        public static void ProcessRestSecurityTracking(RestSecurityResult result, string restPath, RestSecurityConfig config)
        {
            // Failure, track bad requests for potential blacklisting
            if (result.StatusCode != HttpStatusCode.OK)
            {
                int failures = 0;
                var _x       = HttpContext.Current.Cache;

                if (CacheHelpers.CacheExists("FAILURES" + GetWanIpCacheName(result.WanIp, restPath)))
                {
                    failures = CacheHelpers.Cache <int>("FAILURES" + GetWanIpCacheName(result.WanIp, restPath));
                }

                failures++;

                if (failures > config.RestApiBlacklistMaxFailureCount)
                {
                    CacheHelpers.CacheDelete("FAILURES" + GetWanIpCacheName(result.WanIp, restPath));
                    CacheHelpers.CacheAdd("BLACKLISTED" + GetWanIpCacheName(result.WanIp, restPath), 0, expirationSeconds: config.RestApiBlacklistSeconds);
                }

                else
                {
                    CacheHelpers.CacheAdd("FAILURES" + GetWanIpCacheName(result.WanIp, restPath), failures, expirationSeconds: config.RestApiBlacklistMaxFailureSeconds);
                }
            }
        }
Exemplo n.º 2
0
        /// <summary><![CDATA[
        /// Task Interval methods help manage scheduled activities triggered by web page views.
        /// These methods only track status and time. They do not execute anything.
        ///
        /// Check to see if a task is currently running (as indicated by running TimeIntervalStart()).
        /// ]]></summary>
        /// <param name="activityName">Name of the activity, like "TwitterImport".</param>
        /// <param name="context">Manually set the context if running the method from a thread.</param>
        public static bool TaskIsRunning(string activityName, HttpContext context = null)
        {
            bool result = false;

            if (context == null)
            {
                context = HttpContext.Current;
            }

            if (CacheHelpers.CacheExists(activityName + "_Running", context) == true)
            {
                result = CacheHelpers.Cache <bool>(activityName + "_Running", context);
            }

            Debug.WriteLine("Carbide.TemporalHelpers.TaskIsRunning (" + activityName + ") - " + (result == true ? "YES" : "NO"));

            return(result);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Simple way to rate limit and blacklist abusers for anonymous REST requests.
        /// Uses WAN IP address to identify requesters and track their bad requests in order to blacklist them.
        /// </summary>
        /// <example>
        /// One line of code will enforce rate limiting and blacklist a repeated bad request offender,
        /// returning a class object that contains the HttpStatusCode and messaging.
        /// <code>
        /// var jsonData = RestSecurity.RateLimit("169.128.25.23", "2018-12-01T23:59:00Z", "member/signin", restApiSettings);
        /// if (jsonData.StatusCode == HttpStatusCode.OK)
        ///	{
        ///	    // Do stuff and set the status code and messaging properties in jsonData.
        ///     // After all processing, execute ProcessRestSecurityTracking() so subsequent
        ///     // calls to RateLimit() are aware of the ultimate result of this request.
        ///	    RestSecurity.ProcessRestSecurityTracking(jsonData, "member/signin", restApiSettings);
        ///	}
        /// </code>
        /// </example>
        /// <param name="wanIpAddress">Optional WAN IP address of the caller (provided by the caller) to compare against the actual address used to make the request</param>
        /// <param name="timestamp">Optional UTC timestamp of the REST call (provided by the caller) in the format 2018-12-01T23:59:00Z</param>
        /// <param name="restPath">REST path to use as an identifier so that each path has its own rate limiting, etc. (e.g. "/member/signin")</param>
        /// <param name="config">RestSecurityConfig object</param>
        /// <param name="context">Optional HttpContext object (uses Current if not provided)</param>
        /// <returns>RestSecurityResult object with response information</returns>
        public static RestSecurityResult RateLimit(string wanIpAddress, string timestamp, string restPath, RestSecurityConfig config, HttpContext context = null)
        {
            var wanip  = ContextHelpers.EnsureAppContext(context).Request.UserHostAddress;
            var result = new RestSecurityResult {
                WanIp = wanip, StatusCode = HttpStatusCode.BadGateway, StatusText = "Bad Gateway", Message = "Invalid request"
            };

            try
            {
                if (CacheHelpers.CacheExists("BLACKLISTED" + GetWanIpCacheName(wanip, restPath)))
                {
                    result.StatusCode = HttpStatusCode.Forbidden;
                    result.StatusText = "Forbidden";
                    result.Message    = "You appear to be up to no good.";
                }

                else
                {
                    if (CacheHelpers.CacheExists(GetWanIpCacheName(wanip, restPath)) == false)
                    {
                        var newRateData = new RateLimitingCounter {
                            Counter = 0, Expiration = DateTime.Now.AddSeconds(config.RestApiRateLimitSeconds)
                        };
                        CacheHelpers.CacheAdd(GetWanIpCacheName(wanip, restPath), newRateData, expirationDateTime: newRateData.Expiration);
                    }

                    var rateData = CacheHelpers.Cache <RateLimitingCounter>(GetWanIpCacheName(wanip, restPath));
                    rateData.Counter += 1;

                    CacheHelpers.CacheDelete(GetWanIpCacheName(wanip, restPath));
                    CacheHelpers.CacheAdd(GetWanIpCacheName(wanip, restPath), rateData, expirationDateTime: rateData.Expiration);

                    if (rateData.Counter <= config.RestApiRateLimitCount)
                    {
                        // Validate WAN IP address matches actual
                        if (string.IsNullOrEmpty(wanIpAddress) || (string.IsNullOrEmpty(wanIpAddress) == false && wanip == wanIpAddress))
                        {
                            if (string.IsNullOrEmpty(timestamp))
                            {
                                // Timestamp not used, request is good...

                                result.StatusCode = HttpStatusCode.OK;
                                result.StatusText = "OK";
                                result.Message    = "";
                            }

                            else
                            {
                                // Enforce UTC timestamp format of 2018-12-01T23:59:00Z
                                if (timestamp.Length > 19 && timestamp.Contains("T") && timestamp.EndsWith("Z"))
                                {
                                    if (string.IsNullOrWhiteSpace(timestamp) == false && timestamp.IsDate() && DateTime.Parse(timestamp, null, System.Globalization.DateTimeStyles.AdjustToUniversal).DateDiff <int>(DateTime.Now.ToUniversalTime(), DateDiffComparisonType.Minutes) <= config.RestApiTimestampMinutes)
                                    {
                                        // Validate timestamp, request is good...

                                        result.StatusCode = HttpStatusCode.OK;
                                        result.StatusText = "OK";
                                        result.Message    = "";
                                    }
                                }
                            }
                        }

                        else
                        {
                            result.StatusCode = HttpStatusCode.BadRequest;
                            result.StatusText = "Bad Request";
                            result.Message    = "You are not who you claim to be.";
                        }
                    }

                    else
                    {
                        result.StatusCode = (HttpStatusCode)429;
                        result.StatusText = "Too Many Requests";
                        result.Message    = "Slow down there partner.";
                    }
                }
            }

            catch (Exception e)
            {
                result.StatusCode = HttpStatusCode.InternalServerError;
                result.StatusText = "Internal Server Error";
                result.Message    = e.Message;
            }

            ProcessRestSecurityTracking(result, restPath, config);

            return(result);
        }
Exemplo n.º 4
0
        /// <summary><![CDATA[
        /// Task Interval methods help manage scheduled activities triggered by web page views.
        /// These methods only track status and time. They do not execute anything.
        ///
        /// Check to see if a task should be run again. It ensures that the task is not currently running
        /// and that the task time in seconds set in TimeIntervalInit() has elapsed.
        /// ]]></summary>
        /// <param name="activityName">Name of the activity, like "TwitterImport".</param>
        /// <param name="context">Manually set the context if running the method from a thread.</param>
        public static bool TaskShouldBeRun(string activityName, HttpContext context = null)
        {
            bool result = true;

            if (context == null)
            {
                context = HttpContext.Current;
            }

            if (CacheHelpers.CacheExists(activityName + "_Waiting", context) == false)
            {
                try
                {
                    if (TaskIsRunning(activityName, context) == true)
                    {
                        result = false;

                        Debug.WriteLine("Carbide.TemporalHelpers.TaskShouldBeRun (" + activityName + ") - ALREADY RUNNING");
                    }
                }

                catch (Exception e)
                {
                    Debug.WriteLine("Carbide.Temporal EXCEPTION: TaskShouldBeRun (" + activityName + ") - " + e.Message);
                }
            }

            else
            {
                result = false;

                Debug.WriteLine("Carbide.TemporalHelpers.TaskShouldBeRun (" + activityName + ") - TOO SOON; " + FormatTimer((int)(TemporalHelpers.DateDiff <double>(DateTime.Now, CacheHelpers.Cache <DateTime>(activityName + "_Waiting", context), DateDiffComparisonType.Seconds)), " ") + " to go");
            }

            Debug.WriteLine("Carbide.TemporalHelpers.TaskShouldBeRun (" + activityName + ") - " + (result == true ? "YES" : "NO"));

            return(result);
        }
Exemplo n.º 5
0
        /// <summary><![CDATA[
        /// Task Interval methods help manage scheduled activities triggered by web page views.
        /// These methods only track status and time. They do not execute anything.
        ///
        /// Run TimeIntervalStop() from your actual task method to indicate that it has ended.
        /// ]]></summary>
        /// <param name="activityName">Name of the activity, like "TwitterImport".</param>
        /// <param name="context">Manually set the context if running the method from a thread.</param>
        public static void TaskIntervalStop(string activityName, HttpContext context = null)
        {
            try
            {
                if (context == null)
                {
                    context = HttpContext.Current;
                }

                CacheHelpers.CacheAddPermanent(activityName + "_Running", false, context);

                if (CacheHelpers.CacheExists(activityName + "_Seconds", context))
                {
                    CacheHelpers.CacheAdd(activityName + "_Waiting", DateTime.Now.AddSeconds(CacheHelpers.Cache <double>(activityName + "_Seconds", context)), DateTime.Now.AddSeconds(CacheHelpers.Cache <double>(activityName + "_Seconds", context)), context);

                    Debug.WriteLine("Carbide.TemporalHelpers.TaskIntervalStop (" + activityName + ") - STOPPED");
                }

                else
                {
                    CacheHelpers.CacheDelete(activityName + "_Running", context);
                    CacheHelpers.CacheDelete(activityName + "_Seconds", context);
                    CacheHelpers.CacheDelete(activityName + "_Waiting", context);

                    Debug.WriteLine("Carbide.TemporalHelpers.TaskIntervalStop (" + activityName + ") - task time doesn't exist; resetting");
                }
            }

            catch (Exception e)
            {
                Debug.WriteLine("Carbide.Temporal EXCEPTION: TaskIntervalStop (" + activityName + ") - " + e.Message);
            }
        }