how to prevent script injections centrally for .net core mvc application - asp.net-mvc

I just need some opinions on implementing a logic to centrally check if there is no scripts added to the inputs.
I am planning to use antiXSS (Sanitizer.GetSafeHtmlFragment("value")) and check the output if it is null, meaning it could contain script and handle the error.
I could come up with an logic to through the model properties and check the value and if there is anything suspicious throws an error.
I wonder if there is a better way of handling this injections for all input fields in one go rather than adding validations for each field.
Lets say if I have a model like:
public class Login {
public string Email {get; set;}
public string Password {get; set;}
}
Can I just add some sort of filtering to check if non of the inputs contain any scripts before hitting the action rather than adding some attributes to the model or validation express and then do html encode individually for each field and then throw an error.
I want something very at top so I don't go through each actions or model and make some changes.

I used filter action and added such a code to check for string type of any model in the request and encode it. It works perfectly fine for us.
public static class HttpEncode
{
public static void ParseProperties(this object model)
{
if (model == null) return;
if (IsPropertyArrayOrList(model.GetType()))
{
ParsePropertiesOfList(model);
}
else
{
GetAllProperties(model).ForEach(t => EncodeField(t, model));
}
}
private static void ParsePropertiesOfList(object model)
{
foreach (var item in (IEnumerable) model)
{
ParseProperties(item);
}
}
private static List<PropertyInfo> GetAllProperties(object value) => value?.GetType()?.GetProperties()?.ToList();
private static void EncodeField(PropertyInfo p, object arg)
{
try
{
if (p.GetIndexParameters().Length != 0 || p.GetValue(arg) == null)
return;
if (IsUserDefinedClass(p.PropertyType) && p.CanWrite)
{
ParseProperties(p.GetValue(arg));
}
else if (IsPropertyArrayOrList(p.PropertyType) && p.CanWrite)
{
ParseArrayOrListProperty(p, arg);
}
else if (p.PropertyType == typeof(string) && p.CanWrite)
{
var encodedValue = HtmlEncode(p.GetValue(arg)?.ToString());
SetPropertyValue(p, arg, encodedValue);
}
}
catch (Exception ex)
{
// ignored
}
}
private static void ParseArrayOrListProperty(PropertyInfo p, object arg)
{
if (p.GetValue(arg) is string[] || p.GetValue(arg) is List<string>)
{
SetPropertyValueOfStaringArrayType(p, arg);
}
else
{
ParsePropertiesOfList(p.GetValue(arg));
}
}
private static void SetPropertyValueOfStaringArrayType(PropertyInfo propertyInfo, object arg)
{
if (propertyInfo.GetValue(arg) is string[] stringValue)
{
var result = new List<string>();
stringValue.ToList().ForEach(l => result.Add(HtmlEncode(l)));
SetPropertyValue(propertyInfo, arg, result.Any() ? result.ToArray() : null);
}
else if (propertyInfo.GetValue(arg) is List<string> listValue)
{
var result = new List<string>();
listValue.ForEach(l => result.Add(HtmlEncode(l)));
SetPropertyValue(propertyInfo, arg, result.Any() ? result : null);
}
}
private static bool IsUserDefinedClass(Type type) =>
type.IsClass &&
!type.FullName.StartsWith("System.");
private static bool IsPropertyArrayOrList(Type type) =>
type.IsArray && type.GetElementType() == typeof(string) ||
(type != typeof(string) && type.GetInterface(typeof(IEnumerable<>).FullName) != null);
private static void SetPropertyValue(PropertyInfo propertyInfo, object allValue, object value)
{
propertyInfo.SetValue(allValue, value);
}
private static string HtmlEncode(string value) => HttpUtility.HtmlEncode(value);
}
public class EncodeInputsActionFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next)
{
ProcessHtmlEncoding(context);
var resultContext = await next();
// do something after the action executes; resultContext.Result will be set
}
private static void ProcessHtmlEncoding(ActionExecutingContext context)
{
context.ActionArguments.ToList().ForEach(arg => { arg.Value.ParseProperties(); });
}
}

Related

Hide a header from being displayed in Swagger Swashbuckle

As I already have a common authorization added how do I remove separate(authorization header) from each and every API as shown in the image link below?
swaggerHub_Link
I figured it out:
You can simply create a custom attibute and an operation filter inhering from Swashbuckle.AspNetCore.SwaggerGen.IOperationFilter in order to hide the header from being displayed in swagger.json
public class OpenApiHeaderIgnoreAttribute : System.Attribute
{
}
class name should end with the name of the base class.
public class OpenApiHeaderIgnoreFilter : Swashbuckle.AspNetCore.SwaggerGen.IOperationFilter
{
public void Apply(Microsoft.OpenApi.Models.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context)
{
if (operation == null || context == null || context.ApiDescription?.ParameterDescriptions == null)
return;
var parametersToHide = context.ApiDescription.ParameterDescriptions
.Where(parameterDescription => ParameterHasIgnoreAttribute(parameterDescription))
.ToList();
if (parametersToHide.Count == 0)
return;
foreach (var parameterToHide in parametersToHide)
{
var parameter = operation.Parameters.FirstOrDefault(parameter => string.Equals(parameter.Name, parameterToHide.Name, System.StringComparison.Ordinal));
if (parameter != null)
operation.Parameters.Remove(parameter);
}
}
private static bool ParameterHasIgnoreAttribute(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterDescription parameterDescription)
{
if (parameterDescription.ModelMetadata is Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadata metadata)
{
return metadata.Attributes.ParameterAttributes.Any(attribute => attribute.GetType() == typeof(OpenApiParameterIgnoreAttribute));
}
return false;
}
}
and set this filter class in your startup class configureServices method as below:
public void ConfigureServices(IServiceCollection services)
{
services.AddSwaggerGen(c =>
{
c.OperationFilter<OpenApiHeaderIgnoreFilter>();
}
}
and also update your API method in controller as below:
[EnableQuery]
[SwaggerOperation(Tags = new[] { "Odata" })]
public async Task<IActionResult> Get([OpenApiHeaderIgnore] [FromHeader(Name = ClaimNames.AccessToken)] string token)
{
// code
}

ASP.NET Core [FromBody] vs MVC 5 binding

I got an MVC 5 application that i'm porting to asp.net Core.
In the MVC application call to controller we're made using AngularJS $resource (sending JSON) and we we're POSTing data doing :
ressource.save({ entries: vm.entries, projectId: vm.project.id }).$promise...
that will send a JSON body like:
{
entries:
[
{
// lots of fields
}
],
projectId:12
}
the MVC controller looked like this :
[HttpPost]
public JsonResult Save(List<EntryViewModel> entries, int projectId) {
// code here
}
How can I replicate the same behaviour with .NET Core since we can't have multiple [FromBody]
you cannot have multiple parameter with the FromBody attibute in an action method. If that is need, use a complex type such as a class with properties equivalent to the parameter or dynamic type like that
[HttpPost("save/{projectId}")]
public JsonResult Save(int projectId, [FromBody] dynamic entries) {
// code here
}
As pointed out in the comment, one possible solution is to unify the properties you're posting onto a single model class.
Something like the following should do the trick:
public class SaveModel
{
public List<EntryViewModel> Entries{get;set;}
public int ProjectId {get;set;}
}
Don't forget to decorate the model with the [FromBody] attribute:
[HttpPost]
public JsonResult Save([FromBody]SaveViewModel model)
{
// code here
}
Hope this helps!
It's still rough but I made a Filter to mimic the feature.
public class OldMVCFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
}
public void OnActionExecuting(ActionExecutingContext context)
{
if (context.HttpContext.Request.Method != "GET")
{
var body = context.HttpContext.Request.Body;
JToken token = null;
var param = context.ActionDescriptor.Parameters;
using (var reader = new StreamReader(body))
using (var jsonReader = new JsonTextReader(reader))
{
jsonReader.CloseInput = false;
token = JToken.Load(jsonReader);
}
if (token != null)
{
var serializer = new JsonSerializer();
serializer.DefaultValueHandling = DefaultValueHandling.Populate;
serializer.FloatFormatHandling = FloatFormatHandling.DefaultValue;
foreach (var item in param)
{
JToken model = token[item.Name];
if (model == null)
{
// try to cast the full body as the current object
model = token.Root;
}
if (model != null)
{
model = this.RemoveEmptyChildren(model, item.ParameterType);
var res = model.ToObject(item.ParameterType, serializer);
context.ActionArguments[item.Name] = res;
}
}
}
}
}
private JToken RemoveEmptyChildren(JToken token, Type type)
{
var HasBaseType = type.GenericTypeArguments.Count() > 0;
List<PropertyInfo> PIList = new List<PropertyInfo>();
if (HasBaseType)
{
PIList.AddRange(type.GenericTypeArguments.FirstOrDefault().GetProperties().ToList());
}
else
{
PIList.AddRange(type.GetTypeInfo().GetProperties().ToList());
}
if (token != null)
{
if (token.Type == JTokenType.Object)
{
JObject copy = new JObject();
foreach (JProperty jProp in token.Children<JProperty>())
{
var pi = PIList.FirstOrDefault(p => p.Name == jProp.Name);
if (pi != null) // If destination type dont have this property we ignore it
{
JToken child = jProp.Value;
if (child.HasValues)
{
child = RemoveEmptyChildren(child, pi.PropertyType);
}
if (!IsEmpty(child))
{
if (child.Type == JTokenType.Object || child.Type == JTokenType.Array)
{
// nested value has been checked, we add the object
copy.Add(jProp.Name, child);
}
else
{
if (!pi.Name.ToLowerInvariant().Contains("string"))
{
// ignore empty value when type is not string
var Val = (string)child;
if (!string.IsNullOrWhiteSpace(Val))
{
// we add the property only if it contain meningfull data
copy.Add(jProp.Name, child);
}
}
}
}
}
}
return copy;
}
else if (token.Type == JTokenType.Array)
{
JArray copy = new JArray();
foreach (JToken item in token.Children())
{
JToken child = item;
if (child.HasValues)
{
child = RemoveEmptyChildren(child, type);
}
if (!IsEmpty(child))
{
copy.Add(child);
}
}
return copy;
}
return token;
}
return null;
}
private bool IsEmpty(JToken token)
{
return (token.Type == JTokenType.Null || token.Type == JTokenType.Undefined);
}
}

Add multiple included dynamically

I put together some helpers that will allow me to register includes by type. Looks like this:
Dictionary<Type, LambdaExpression[]> includes =
new Dictionary<Type, LambdaExpression[]>();
I register includes like this:
public void Add<T>(params Expression<Func<T, object>>[] includes)
{
this.includes.Add(typeof(T), includes);
}
Add<Customer>(e =>
e.Address.City.Province.Country,
e.Products.Select(p => p.Category));
Notice there are two includes for Customer. I then have this method that gets includes by type:
DbSet<T> entitySet = null;
void LoadIncludes()
{
var includes = Includes.Instance.Get<T>().DirectCast<Expression<Func<T, object>>[]>();
if (includes != null)
{
foreach (var include in includes)
{
entitySet.Include(include).Load();
}
}
}
When getting my entity, I do this:
public T GetById(int id)
{
LoadIncludes();
return entitySet.SingleOrDefault(x => x.Id == id);
}
It works well, but it's so slow, and it's because of the .Load() method I am calling in LoadIncludes(). Is there a faster way to do what I want to do here?
You shouldn't call Load, but build and use a Queryable<T> with Include chain.
Replace LoadIncludes with private function:
private IQueryable<T> GetEntitySet()
{
var set = entitySet.AsQueryable();
var includes = Includes.Instance.Get<T>().DirectCast<Expression<Func<T, object>>[]>();
if (includes != null)
{
foreach (var include in includes)
{
set = set.Include(include);
}
}
return set;
}
and use it as follows:
public T GetById(int id)
{
return GetEntitySet().SingleOrDefault(x => x.Id == id);
}

MVC-Create a ViewModel class instance from the urlreferrer url

Given:
request.UrlReferrer.LocalPath = "/MyApp/MyHome/List";
and I have a Route Mapping that handles this where MyHome is my controller and List is an action that takes a ViewModel. Other variations of this Route include paging and sorting but these are captured by the ViewModel.
My question is this:
How can I use the above URL to generate an instance of the related ViewModel?
EDIT: I have an JQuery Dialog that is adding/updating/deleting an item in a list that is shown by the url in the urlreferrer- the example given is the most basic. When the dialog sends the data to be a/u/d, I want to return the updated body of the list and display that. This information is handled by a different ViewModel than what is instantiated on the POST from the dialog (the url posted to is "/MyApp/MyHome/Edit/True" - for creating a new whatever). This piece follows the standard MVC process and of course works. What I want to do is create a second ViewModel based on the ViewModel for the list action and return this as a partial view containing the updated paged list.
Ok... I think I have this figured out. This is not pretty but it works. I welcome anybody's input to actually feed this through a ModelBinder or any other MVC artifact but here's what I came up with:
First we need to fake a request using the UrlReferrer instead of the actual url being requested:
public class FakeHttpContext : HttpContextBase
{
public FakeHttpContext(HttpContextBase currentContext)
{
_request = new FakeHttpRequest(currentContext.Request);
}
HttpRequestBase _request;
public override HttpRequestBase Request
{
get
{
return _request;
}
}
HttpResponseBase _response = new FakeHttpResponse();
public override HttpResponseBase Response
{
get
{
return _response;
}
}
class FakeHttpRequest : HttpRequestBase
{
HttpRequestBase _request;
public FakeHttpRequest(HttpRequestBase currentRequest)
{
if(currentRequest == null)
throw new ArgumentNullException();
this._request = currentRequest;
}
public override string ApplicationPath
{
get
{
return this._request.ApplicationPath;
}
}
public override string AppRelativeCurrentExecutionFilePath
{
get
{
return "~" + this._request.UrlReferrer.AbsolutePath.Remove(0, this._request.ApplicationPath.Length);
}
}
public override string PathInfo
{
get
{
return this._request.PathInfo;
}
}
}
class FakeHttpResponse : HttpResponseBase
{
}
}
Next, we feed the fake call through the RouteTable to get it broken down. and match up properties to the RouteData.Values.
public static class RouteAndModelBinder
{
public static void BuildViewModel<TViewModel>(ControllerContext context, TViewModel model)
{
FakeHttpContext fake = new FakeHttpContext(context.HttpContext);
RouteData test = RouteTable.Routes.GetRouteData(fake);
PropertyInfo[] properties = typeof(TViewModel).GetProperties();
string value;
foreach(PropertyInfo info in properties)
{
if(test.Values.ContainsKey(info.Name))
{
value = (string)test.Values[info.Name];
if(value == null)
{
continue;
}
if(info.PropertyType.IsGenericType &&
info.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
Type[] nullables = info.PropertyType.GetGenericArguments();
if(nullables.Length > 0)
{
Type nullableType = nullables[0];
if(nullableType.BaseType == typeof(Enum))
{
object o = Enum.Parse(nullableType, value);
info.SetValue(model, o, null);
}
else if(nullableType == typeof(Int32))
{
info.SetValue(model, int.Parse(value), null);
}
else
{
info.SetValue(model, Convert.ChangeType(value, info.PropertyType), null);
}
}
}
else
{
if(info.PropertyType.BaseType == typeof(Enum))
{
object o = Enum.Parse(info.PropertyType.BaseType, value);
info.SetValue(model, o, null);
}
else if(info.PropertyType == typeof(Int32))
{
info.SetValue(model, int.Parse(value), null);
}
else
{
info.SetValue(model, value, null);
}
}
}
}
}
}
Again, I welcome anybody's suggestions on how I can do this with already established MVC code (ie, ModelBinders, etc). I took some ideas and probably code from here (for the nullable type) and here.

Norm.MongoException: Connection timeout trying to get connection from connection pool

I'm using Rob's mvc startesite http://mvcstarter.codeplex.com/ with ASP.Net MVC 2, Ninject2, NoRM (http://github.com/atheken/NoRM) and MongoDB. It works so fast and the developpement is even faster but I'm facing a big problem, I at some points, get connection timeout. I can't figure out what I'm doing wrong.
I already asked a question here : I get this error that I don't understand why, using NoRM and Mongo in my MVC project and here http://groups.google.com/group/norm-mongodb/browse_thread/thread/7882be16f030eb29 but I still in the dark.
Thanks a lot for the help!
EDITED*
Here's my MongoSession object :
public class MongoSession : ISession{
private readonly Mongo _server;
public MongoSession()
{
//this looks for a connection string in your Web.config - you can override this if you want
_server = Mongo.Create("MongoDB");
}
public T Single<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T : class {
return _server.GetCollection<T>().AsQueryable().Where(expression).SingleOrDefault();
}
public IQueryable<T> All<T>() where T : class {
return _server.GetCollection<T>().AsQueryable();
}
public void Save<T>(IEnumerable<T> items) where T : class {
foreach (T item in items) {
Save(item);
}
}
public void Save<T>(T item) where T : class {
var errors = DataAnnotationsValidationRunner.GetErrors(item);
if (errors.Count() > 0)
{
throw new RulesException(errors);
}
_server.Database.GetCollection<T>().Save(item);
}
public void Delete<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T : class
{
var items = All<T>().Where(expression);
foreach (T item in items)
{
Delete(item);
}
}
public void Delete<T>(T item) where T : class
{
_server.GetCollection<T>().Delete(item);
}
public void Drop<T>() where T : class
{
_server.Database.DropCollection(typeof(T).Name);
}
public void Dispose() {
_server.Dispose();
}
}
And now my MongoRepositoryBase
public abstract class MongoRepositoryBase<T> : ISession<T> where T : MongoObject
{
protected ISession _session;
protected MongoRepositoryBase(ISession session)
{
_session = session;
}
public T Single(ObjectId id)
{
return _session.All<T>().Where(x => x.Id == id).FirstOrDefault();
}
public T Single(Expression<Func<T, bool>> expression)
{
return _session.Single(expression);
}
public IQueryable<T> All()
{
return _session.All<T>();
}
public void Save(IEnumerable<T> items)
{
foreach (T item in items)
{
Save(item);
}
}
public void Save(T item)
{
_session.Save(item);
}
public void Delete(System.Linq.Expressions.Expression<Func<T, bool>> expression)
{
var items = _session.All<T>().Where(expression);
foreach (T item in items)
{
Delete(item);
}
}
public void DeleteAll()
{
var items = _session.All<T>();
foreach (T item in items)
{
Delete(item);
}
}
public void Delete(T item)
{
_session.Delete(item);
}
public void Drop()
{
_session.Drop<T>();
}
public void Dispose()
{
_session.Dispose();
}
}
And an exemple of an other Repository implemantation :
public class PlaceRepository : MongoRepositoryBase<Place>, IPlaceRepository
{
public PlaceRepository(ISession session) : base(session)
{
}
public List<Place> GetByCategory(PlaceCategory category, bool publishedOnly)
{
var query = _session.All<Place>()
.OrderBy(x => x.Name)
.Where(x => x.Category == category);
if (publishedOnly) query = query.Where(x => x.Published);
if (publishedOnly) query = query.Where(x => x.ShowOnMap);
return query.ToList();
}
public Place FindByName(string name)
{
var query = _session.All<Place>()
.Where(x => x.Name.ToLower().Contains(name.ToLower()))
.Where(x => x.Published);
return query.FirstOrDefault();
}
public string[] FindSuggestionsByName(string name)
{
var query = _session.All<Place>()
.OrderBy(x => x.Name)
.Where(x => x.Name.ToLower().StartsWith(name.ToLower()))
.Where(x => x.Published);
var places = query.ToList();
var names = new string[places.Count];
var i = 0;
foreach (var place in places)
{
names[i++] = place.Name;
}
return names;
}
}
Vinny,
I've never used Ninject, so I could be way off with this suggestion. But it seems possible that having a static MongoSession instance might be holding connections open. Have you tried TransientBehavior instead of SingletonBehavior? Or maybe change your code to call Dispose (or use using) after you convert your ShortcutLinks to a List? All
var shortcutLionks = _session.All<ShortcutLinks>().ToList();
_session.Dispose();
A better approach might be to use some sort of repository or DAO where the session details are hidden from the controller. I have a RepositoryBase sample at http://www.codevoyeur.com/Articles/20/A-NoRM-MongoDB-Repository-Base-Class.aspx.
Stuart Harris has a similar, arguably more complete implementation at http://red-badger.com/Blog/post/A-simple-IRepository3cT3e-implementation-for-MongoDB-and-NoRM.aspx
Pooled MongoDB connections are relatively cheap to create, so it's probably best to make sure the data access methods are disposing after your done getting/saving data.
If I add throw new NotImplementedException(); in the Dispose() method of my MongoRepositoryBase class it does not get call so I guess Ninject does not handle this for me, If I had
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
_recipeRepo.Dispose();
base.OnActionExecuted(filterContext);
}
In my controller it does get call. It seems to be fine, thx!

Resources