.Net Core Penetration Testing – Cookies and Headers

When performing a penetration test on a .Net Core web site to find security vulnerabilities, some common issues may be found that are not handled by the default .Net Core template in Visual Studio. One of the tools I use to carry out penetration tests in the Zed Attack Proxy (ZAP) that is part of Open Web Application Security Project (OWASP).

Below is a list of some of the common alerts that may be flagged by ZAP or similar tools, and how to fix each one in .Net Core. All fixes are done within the Configure() method of the Startup class. At the end I will also include a code listing of all the fixes together.

X-Frame-Options Header Not Set

Risk: Medium
ZAP description: X-Frame-Options header is not included in the HTTP response to protect against ‘ClickJacking’ attacks.
Fix:
            app.Use(async (context, next) =>
            {
                context.Response.Headers.Add(“X-Frame-Options”, “SAMEORIGIN”);
                await next();
            });

Cookie No HttpOnly Flag

Risk: Low
ZAP description: A cookie has been set without the HttpOnly flag, which means that the cookie can be accessed by JavaScript. If a malicious script can be run on this page then the cookie will be accessible and can be transmitted to another site. If this is a session cookie then session hijacking may be possible.
Fix:
            app.UseCookiePolicy(new CookiePolicyOptions
            {
                HttpOnly = HttpOnlyPolicy.Always
            });

Cookie Without Secure Flag

Risk: Low
ZAP description: A cookie has been set without the secure flag, which means that the cookie can be accessed via unencrypted connections.
Fix:
            app.UseCookiePolicy(new CookiePolicyOptions
            {
                Secure = CookieSecurePolicy.Always
            });

Incomplete or No Cache-control and Pragma HTTP Header Set

Risk: Low
ZAP description: The cache-control and pragma HTTP header have not been set properly or are missing allowing the browser and proxies to cache content.
Fix:
            app.UseStaticFiles(new StaticFileOptions
            {
                OnPrepareResponse = ctx =>
                {
                    ctx.Context.Response.Headers.Add(“Cache-Control”, “no-cache, no-store, must-revalidate”);
                }
            });
app.UseMiddleware<NoCacheMiddleware>();
Add a new Middleware Class:
public class NoCacheMiddleware
        {
            private readonly RequestDelegate m_next;
            public NoCacheMiddleware(RequestDelegate next)
            {
                m_next = next;
            }
            public async Task Invoke(HttpContext httpContext)
            {
                httpContext.Response.OnStarting((state) =>
                {
                    httpContext.Response.Headers.Append(“Cache-Control”, “no-cache, no-store, must-revalidate”);
                    httpContext.Response.Headers.Append(“Pragma”, “no-cache”);
                    httpContext.Response.Headers.Append(“Expires”, “0”);
                    return Task.FromResult(0);
                }, null);
                await m_next.Invoke(httpContext);
            }
        }

Web Browser XSS Protection Not Enabled

Risk: Low
ZAP description: Web Browser XSS Protection is not enabled, or is disabled by the configuration of the ‘X-XSS-Protection’ HTTP response header on the web server
Reference: 
Fix:
            app.Use(async (context, next) =>
            {
                context.Response.Headers.Add(“X-Xss-Protection”, “1”);
                await next();
            });

X-Content-Type-Options Header Missing

Risk: Low
ZAP description: The Anti-MIME-Sniffing header X-Content-Type-Options was not set to ‘nosniff’. This allows older versions of Internet Explorer and Chrome to perform MIME-sniffing on the response body, potentially causing the response body to be interpreted and displayed as a content type other than the declared content type. Current (early 2014) and legacy versions of Firefox will use the declared content type (if one is set), rather than performing MIME-sniffing.
Reference:
Fix:
            app.UseStaticFiles(new StaticFileOptions
            {
                OnPrepareResponse = ctx =>
                {
                    ctx.Context.Response.Headers.Add(“X-Content-Type-Options”, “nosniff”);
                }
            });
            app.Use(async (context, next) =>
            {
                context.Response.Headers.Add(“X-Content-Type-Options”, “nosniff”);
                await next();
            });

Complete listing

Below is a ‘complete listing’ combining the individual fixes. I have removed some of the unrelated code to help with readability.
Startup class
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            app.UseResponseCompression();
            loggerFactory.AddLog4Net();
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();
            }
            else
            {
                app.UseExceptionHandler(“/Home/Error”);
            }
            app.UseStaticFiles(new StaticFileOptions
            {
                //Pen test fix
                OnPrepareResponse = ctx =>
                {
                    ctx.Context.Response.Headers.Add(“Cache-Control”, “no-cache, no-store, must-revalidate”);
                    ctx.Context.Response.Headers.Add(“X-Content-Type-Options”, “nosniff”);
                }
            });
            app.UseAuthentication();
            app.UseSession();
            //Pen test fix
            app.UseCookiePolicy(new CookiePolicyOptions
            {
                HttpOnly = HttpOnlyPolicy.Always,
                Secure = CookieSecurePolicy.Always
            });
            //Pen test fix
            app.Use(async (context, next) =>
            {
                context.Response.Headers.Add(“X-Frame-Options”, “SAMEORIGIN”);
                context.Response.Headers.Add(“X-Content-Type-Options”, “nosniff”);
                context.Response.Headers.Add(“X-Xss-Protection”, “1”);
                await next();
            });
            //Pen test fix
            app.UseMiddleware<NoCacheMiddleware>();
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: “default”,
                    template: “{controller=Home}/{action=Index}/{id?}”);
            });
        }
Middleware Class:
public class NoCacheMiddleware
        {
            private readonly RequestDelegate m_next;
            public NoCacheMiddleware(RequestDelegate next)
            {
                m_next = next;
            }
            public async Task Invoke(HttpContext httpContext)
            {
                httpContext.Response.OnStarting((state) =>
                {
                    httpContext.Response.Headers.Append(“Cache-Control”, “no-cache, no-store, must-revalidate”);
                    httpContext.Response.Headers.Append(“Pragma”, “no-cache”);
                    httpContext.Response.Headers.Append(“Expires”, “0”);
                    return Task.FromResult(0);
                }, null);
                await m_next.Invoke(httpContext);
            }
        }

Extra: X-Powered-By and Server Headers

The following headers are added by Azure to each request:
  • X-Powered-By: ASP.NET
  • Server: Kestrel

If you want to remove these header’s to make it harder for a potential attacked to know what server is your app is running on, then add the following to your web.config file:

  <system.webServer>
        <!– Additional entries removed to help readability–>
    <security>
      <requestFiltering removeServerHeader =”true”></requestFiltering>
    </security>
    <httpProtocol>
      <customHeaders>
        <remove name=”X-Powered-By”/>
      </customHeaders>
    </httpProtocol>
  </system.webServer>

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s