Prevent web API from executing AT ALL if EnableCors Origin reason is invalid

I use the Microsofts EnableCors attribute for my Web API calls. Client-side behavior is executed as I expected: for example. the call returns in error when the source is invalid.

However, when I put a breakpoint inside the method and call from an invalid Origin ... the method still executes from top to bottom (even if the client receives an unsuccessful result). If Origin is not valid, I do not want it to execute AT ALL.

MY QUESTION:
How can I prevent the use of the AT ALL web API method if EnableCors Origin is not valid?

Help me Obi-Wan Kenobi ... you are my only hope.

MY CODE LOOKS LIKE:

 [HttpPost] [EnableCors(origins: "www.zippitydoodah.com", headers: "*", methods: "*")] public HttpResponseMessage Enqueue(HttpRequestMessage request) { // NONE OF THIS SHOULD RUN: If the Origin is bad...but (oddly) it is TraceHandler.TraceIn(TraceLevel.Info); string claimId = string.Empty; ClaimMessage claimMessage = null; try { claimId = GetClaimId(request); claimMessage = CreateClaimMessage(claimId, segmentClaimFullName); Enqueue(claimMessage); TraceHandler.TraceAppend(FORMAT_ENQUEUED_SUCCESS, claimId); } catch (Exception ex) { TraceHandler.TraceError(ex); TraceHandler.TraceOut(); EnqueueToPoison(ex, claimMessage); return Request.CreateResponse(HttpStatusCode.InternalServerError, GetHttpError()); } TraceHandler.TraceOut(); return Request.CreateResponse(HttpStatusCode.OK, string.Format(FORMAT_ENQUEUED_SUCCESS, claimId)); } 

MY CONFIG LOOKS LIKE:

 public static class WebApiConfig { #region <Methods> public static void Register(HttpConfiguration config) { // ENABLE CORS config.EnableCors(); // CREATE ROUTES config.Routes.MapHttpRoute( name: "DefaultRpcApiActions", routeTemplate: "api/{controller}/actions/{action}/{id}", defaults: new { id = RouteParameter.Optional }); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional }); } #endregion } 
+5
source share
1 answer

As it turns out ...

CORS headers should not PREVENT calls to the controller: MVC or Web API. This simply prevents the RESULTS from being returned to the browser. The method will execute no matter what ... therefore you must prevent it from being executed in other ways.

WHAT OTHER MEANS?
Perhaps you can do this with AuthorizeAttribute . But I wanted to do it at the ACTION level, so I selected ActionFilterAttribute - and performed the checks in OnActionExecuting

USAGE NOTES: ActionFilterAttribute

  • Go ahead and open CORS to everyone, and then restrict it to action (so everyone can PING)

  • All calls are supposed to come from a valid REFERRER

This means that things like SQL CLR calls from the database (this is what I am doing) will not work because the REFERRER is null (because of this I will post the best solution later).

  • ICorsPolicyProvider useless - I delete it (but enabled it here)

All the examples I saw included this, but I still have to find the scenario in which it is called. My constructor already creates CorsPolicy , and this policy is available during the lifetime of calls ... so the ICorsPolicyProvider method seems pretty useless (for now).

  • TraceHandler implementation - my own - keep using instead

  • An Access-Control-Allow-Origin header must be added to provide expected return message behavior for specific clients.

HERE CODE: ActionFilterAttribute

 namespace My.Application.Security { using My.Application.Diagnostics; using System; using System.Configuration; using System.Diagnostics; using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Web.Cors; using System.Web.Http.Controllers; using System.Web.Http.Cors; using System.Web.Http.Filters; [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] public class EnableWebApiCorsFromAppSettingsAttribute : ActionFilterAttribute, ICorsPolicyProvider { #region <Fields & Constants> private const string EXCEPTION_CONTEXT_NULL = "Access Denied: HttpActionContext cannot be null."; private const string EXCEPTION_REFERRER_NULL = "Access Denied: Referrer cannot be null."; private const string FORMAT_INVALID_REFERRER = "Access Denied: '{0}' is not a valid referrer."; private const string FORMAT_REFERRER = "Referrer: '{0}' was processed for this request."; private const string FORMAT_REFERRER_FOUND = "Referrer IsFound: {0}."; private readonly CorsPolicy policy; #endregion #region <Constructors> public EnableWebApiCorsFromAppSettingsAttribute(string appSettingKey, bool allowAnyHeader = true, bool allowAnyMethod = true, bool supportsCredentials = true) { policy = new CorsPolicy(); policy.AllowAnyOrigin = false; policy.AllowAnyHeader = allowAnyHeader; policy.AllowAnyMethod = allowAnyMethod; policy.SupportsCredentials = supportsCredentials; SetValidOrigins(appSettingKey); if (policy.Origins.Count == 0) policy.AllowAnyOrigin = true; } #endregion #region <Methods> #region public public override void OnActionExecuting(HttpActionContext actionContext) { TraceHandler.TraceIn(TraceLevel.Info); if (actionContext == null) throw new ArgumentNullException("HttpActionContext"); if (actionContext.Request.Headers.Referrer == null) actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Forbidden, EXCEPTION_REFERRER_NULL); var referrer = actionContext.Request.Headers.Referrer.ToString(); TraceHandler.TraceAppend(string.Format(FORMAT_REFERRER, referrer)); // If no Origins Are Set - Do Nothing if (policy.Origins.Count > 0) { var isFound = policy.Origins.Contains(referrer); TraceHandler.TraceAppend(string.Format(FORMAT_REFERRER_FOUND, isFound)); if (!isFound) { TraceHandler.TraceAppend("IsFound was FALSE"); actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Forbidden, string.Format(FORMAT_INVALID_REFERRER, referrer)); } } TraceHandler.TraceOut(); base.OnActionExecuting(actionContext); } public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (cancellationToken.CanBeCanceled && cancellationToken.IsCancellationRequested) return Task.FromResult<CorsPolicy>(null); return Task.FromResult(policy); } #endregion #region private private void SetValidOrigins(string appSettingKey) { // APP SETTING KEY: <add key="EnableCors.Origins" value="http://www.zippitydoodah.com" /> var origins = string.Empty; if (!string.IsNullOrEmpty(appSettingKey)) { origins = ConfigurationManager.AppSettings[appSettingKey]; if (!string.IsNullOrEmpty(origins)) { foreach (string origin in origins.Split(",;|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)) policy.Origins.Add(origin); } } } #endregion #endregion } } 

HERE USING THE CODE: ActionFilterAttribute

 namespace My.Application.Web.Controllers { using Security; using My.Application.Diagnostics; using My.Application.Framework.Configuration; using My.Application.Models; using My.Application.Process; using System; using System.Configuration; using System.Diagnostics; using System.IO; using System.Linq; using System.Messaging; using System.Net; using System.Net.Http; using System.Web.Http; using System.Web.Http.Cors; using System.Xml.Linq; using System.Xml.Serialization; [EnableCors(origins: "*", headers: "*", methods: "*")] public class OutboundEventController : ApiControllerBase { #region <Actions> [HttpGet] public HttpResponseMessage Ping() { TraceHandler.TraceIn(TraceLevel.Info); if (Request.Headers.Referrer == null) TraceHandler.TraceAppend(MESSAGE_REFERRER_NULL); if (Request.Headers.Referrer != null) TraceHandler.TraceAppend(string.Format(FORMAT_REFERRER, Request.Headers.Referrer)); TraceHandler.TraceOut(); return Request.CreateResponse(HttpStatusCode.OK, "Ping back at cha..."); } [HttpPost] [EnableWebApiCorsFromAppSettings("EnableCors.Origins")] public HttpResponseMessage Enqueue(HttpRequestMessage request) { TraceHandler.TraceIn(TraceLevel.Info); if (Request.Headers.Referrer == null) TraceHandler.TraceAppend(MESSAGE_REFERRER_NULL); if (Request.Headers.Referrer != null) TraceHandler.TraceAppend(string.Format(FORMAT_REFERRER, Request.Headers.Referrer)); try { // Do Amazing Stuff Here... TraceHandler.TraceAppend(FORMAT_ENQUEUED_SUCCESS, claimId); } catch (Exception ex) { TraceHandler.TraceError(ex); TraceHandler.TraceOut(); EnqueueToPoison(ex, claimMessage); return Request.CreateResponse(HttpStatusCode.InternalServerError, GetHttpError()); } TraceHandler.TraceOut(); // FORCE: Correct Header var response = Request.CreateResponse(HttpStatusCode.OK, string.Format(FORMAT_ENQUEUED_SUCCESS, claimId)); response.Headers.Add("Access-Control-Allow-Origin", "*"); return response; } #endregion private string GetClaimId(HttpRequestMessage request) { var stream = request.Content.ReadAsStreamAsync().Result; var xdoc = XDocument.Load(stream); var result = GetElementValue(xdoc, "ClaimId"); return result; } private ClaimMessage CreateClaimMessage(string claimId, string process) { ClaimMessage message = new ClaimMessage(); message.ClaimID = claimId; message.Process = process; return message; } private void Enqueue(ClaimMessage claimMessage) { var queueName = ConfigurationManager.AppSettings[Settings.Messaging.Queue.Name].ToString(); var queue = new MessageQueue(queueName); queue.DefaultPropertiesToSend.Recoverable = true; TraceHandler.TraceAppend(FORMAT_QUEUE_NAME, queueName); MessageQueueTransaction transaction; transaction = new MessageQueueTransaction(); transaction.Begin(); var message = new System.Messaging.Message(); message.Formatter = new XmlMessageFormatter(new Type[] { typeof(ClaimMessage) }); message.Label = "ClaimID " + claimMessage.ClaimID; message.Body = claimMessage; queue.Send(message, transaction); transaction.Commit(); queue.Close(); } private void EnqueueToPoison(Exception exception, ClaimMessage claimdata) { TraceHandler.TraceIn(TraceLevel.Info); var poison = ToPoisonMessage(exception, claimdata); var message = new System.Messaging.Message(); try { var poisonQueueName = ConfigurationManager.AppSettings[Settings.Messaging.PoisonQueue.Name].ToString(); TraceHandler.TraceAppend(FORMAT_QUEUE_NAME, poisonQueueName); if (MessageQueue.Exists(poisonQueueName)) { var queue = new MessageQueue(poisonQueueName); queue.DefaultPropertiesToSend.Recoverable = true; var transaction = new MessageQueueTransaction(); transaction.Begin(); message.Formatter = new XmlMessageFormatter(new Type[] { typeof(PoisonClaimMessage) }); message.Label = "Poison ClaimID " + poison.ClaimID; var xmlSerializer = new XmlSerializer(poison.GetType()); xmlSerializer.Serialize(message.BodyStream, poison); queue.Send(message, transaction); TraceHandler.TraceAppend(FORMAT_ENQUEUED_POISON_SUCCESS, poison.ClaimID); transaction.Commit(); queue.Close(); } } catch(Exception ex) { // An error occurred while enqueuing to POISON var poisonXml = ToString(poison); TraceHandler.TraceError(ex); TraceHandler.TraceAppend(poisonXml); } finally { TraceHandler.TraceOut(); } } #endregion } } 

APPLICATION SETTINGS: ActionFilterAttribute

  <appSettings> <add key="EnableCors.Origins" value="" /> </appSettings> 
+2
source

Source: https://habr.com/ru/post/1246892/


All Articles