Created
September 10, 2017 08:37
-
-
Save toepoke/fec7a6ac083bb720c819feb8492b65b2 to your computer and use it in GitHub Desktop.
MVC 2 version of http://haacked.com/archive/2017/08/10/mvc-action-security-audit/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
using System.Linq; | |
using System.Reflection; | |
using System.Text; | |
using System.Web.Mvc; | |
using System; | |
/// MVC2 compatiable version of @haacked security checking class, for details, see | |
/// - http://haacked.com/archive/2017/08/10/mvc-action-security-audit/ | |
public class SystemController : Controller { | |
protected override void OnActionExecuting(ActionExecutingContext filterContext) { | |
if (!ControllerContext.HttpContext.Request.IsLocal) { | |
filterContext.HttpContext.Response.StatusCode = 404; | |
filterContext.HttpContext.Response.Flush(); | |
filterContext.HttpContext.Response.End(); | |
return; | |
} | |
base.OnActionExecuting(filterContext); | |
} | |
public ActionResult Index(string ignore) { | |
ignore = ignore ?? ""; | |
if (!ControllerContext.HttpContext.Request.IsLocal) | |
return null; | |
var assembly = Assembly.GetExecutingAssembly(); | |
var controllers = assembly.GetTypes() | |
.Where(type => typeof(Controller).IsAssignableFrom(type)) //filter controllers | |
.Select(type => new ReflectedControllerDescriptor(type)); | |
var controllerIssues = new Dictionary<string, Dictionary<string, List<string>>>(); | |
foreach (var controller in controllers) { | |
var actions = controller.GetCanonicalActions(); | |
foreach (var action in actions) { | |
var attributes = action.GetCustomAttributes(true); | |
if (!ignore.Contains("antiforgery")) { | |
CheckAction(() => (ContainsHttpMutateAttribute(attributes) | |
&& !ContainsAttribute<ValidateAntiForgeryTokenAttribute>(attributes)) | |
, controller | |
, action | |
, "HTTP attribute that could mutate a resource does not have a <code>[ValidateAntiForgeryToken]</code> attribute applied." | |
, controllerIssues); | |
} | |
if (!ignore.Contains("authorization")) { | |
CheckAction(() => (ContainsHttpMutateAttribute(attributes) | |
&& !ContainsAttribute<AuthorizeAttribute>(attributes) | |
&& !ContainsAttribute<AuthorizeAttribute>(controller.GetCustomAttributes(true))) | |
, controller | |
, action | |
, "HTTP attribute that could mutate a resource does not have an <code>[Authorize]</code> attribute applied. You may also want to apply the attribute to the <code>GET</code> action (if any) that corresponds to this action." | |
, controllerIssues); | |
} | |
} | |
} | |
var response = new StringBuilder(); | |
response.Append("<html><head>"); | |
response.Append("<title>System Check</title>"); | |
response.Append("<style>"); | |
response.Append("body {font-family: arial,helvetica,san-serif; font-size: 0.9em;}"); | |
response.Append("h3 {padding-left: 8px;"); | |
response.Append("</style>"); | |
response.Append("</head>"); | |
response.Append("<body>"); | |
response.Append("<div><h1>System Check: Potential Issues Found</h1>"); | |
response.Append(string.Format("<p>Reflecting over controllers and actions in the assembly <code>{0}</code> found the following potential issues. Note that some of these issues may be by design. For example, you probably DO NOT want an <code>Authorize</code> attribute on a <code>Login</code> action.</p>", assembly.FullName)); | |
response.Append(@"<p>To ignore antiforgery issues <a href=""?ignore=antiforgery"">click here</a>.</p>"); | |
response.Append(@"<p>To ignore authorization issues <a href=""?ignore=authorization"">click here</a></p>"); | |
response.Append(@"<p>To clear the ignore filters <a href=""?ignore="">click here</a></p>"); | |
foreach (var controller in controllerIssues.Keys) { | |
response.Append(string.Format("<h2><code>{0}Controller</code></h2>", controller)); | |
foreach (var action in controllerIssues[controller]) { | |
response.Append(string.Format("<h3><code>{0}</code></h3>", action.Key)); | |
response.Append("<ul>"); | |
foreach (var issue in action.Value) { | |
response.Append(string.Format("<li>{0}</li>", issue)); | |
} | |
response.Append("</ul>"); | |
} | |
} | |
response.Append("</body>"); | |
response.Append("</html>"); | |
return Content(response.ToString()); | |
} | |
static bool ContainsAttribute<T>(object[] attributes) where T : Attribute { | |
return attributes.Any(attr => attr as T != null); | |
} | |
static bool ContainsHttpMutateAttribute(object[] attributes) { | |
return ContainsAttribute<HttpPostAttribute>(attributes) | |
|| ContainsAttribute<HttpPutAttribute>(attributes) | |
|| ContainsAttribute<HttpDeleteAttribute>(attributes) | |
// || ContainsAttribute<HttpAttribute>(attributes) | |
; | |
} | |
static void CheckAction(Func<bool> check, ControllerDescriptor controller, ActionDescriptor action, string checkTrueMessage, Dictionary<string, Dictionary<string, List<string>>> controllerIssues) { | |
if (check != null && check()) { | |
if (!controllerIssues.ContainsKey(controller.ControllerName)) { | |
controllerIssues[controller.ControllerName] = new Dictionary<string, List<string>>(); | |
} | |
var actionDictionary = controllerIssues[controller.ControllerName]; | |
if (!actionDictionary.ContainsKey(action.ActionName)) { | |
actionDictionary[action.ActionName] = new List<string>(); | |
} | |
actionDictionary[action.ActionName].Add(checkTrueMessage); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment