private static void OnBeginRequest(object sender, EventArgs e) { _lastRequestDateTime = DateTime.UtcNow; var httpContext = ((HttpApplication)sender).Context; var httpRequest = new HttpRequestWrapper(httpContext.Request); // HACK: This is abusing the trace module // Disallow GET requests from CSM extensions bridge // Except if owner or coadmin (aka legacy or non-rbac) authorization if (!String.IsNullOrEmpty(httpRequest.Headers["X-MS-VIA-EXTENSIONS-ROUTE"]) && httpRequest.HttpMethod.Equals(HttpMethod.Get.Method, StringComparison.OrdinalIgnoreCase) && !String.Equals(httpRequest.Headers["X-MS-CLIENT-AUTHORIZATION-SOURCE"], "legacy", StringComparison.OrdinalIgnoreCase)) { httpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; httpContext.Response.End(); } // HACK: If it's a Razor extension, add a dummy extension to prevent WebPages for blocking it, // as we need to serve those files via /vfs // Yes, this is an abuse of the trace module if (httpRequest.FilePath.IndexOf("vfs/", StringComparison.OrdinalIgnoreCase) >= 0 && (httpRequest.FilePath.EndsWith(".cshtml", StringComparison.OrdinalIgnoreCase) || httpRequest.FilePath.EndsWith(".vbhtml", StringComparison.OrdinalIgnoreCase))) { httpContext.Server.TransferRequest(httpRequest.FilePath + Constants.DummyRazorExtension, preserveForm: true); return; } // Always trace the startup request. ITracer tracer = TraceStartup(httpContext); // Skip certain paths if (TraceExtensions.ShouldSkipRequest(httpRequest)) { TraceServices.RemoveRequestTracer(httpContext); return; } tracer = tracer ?? TraceServices.CreateRequestTracer(httpContext); if (tracer == null || tracer.TraceLevel <= TraceLevel.Off) { return; } var attribs = GetTraceAttributes(httpContext); AddTraceLevel(httpContext, attribs); foreach (string key in httpContext.Request.Headers) { if (!key.Equals("Authorization", StringComparison.OrdinalIgnoreCase) && !key.Equals("X-MS-CLIENT-PRINCIPAL-NAME", StringComparison.OrdinalIgnoreCase)) { attribs[key] = httpContext.Request.Headers[key]; } } httpContext.Items[_stepKey] = tracer.Step(XmlTracer.IncomingRequestTrace, attribs); }
private static void OnBeginRequest(object sender, EventArgs e) { _lastRequestDateTime = DateTime.UtcNow; var httpContext = ((HttpApplication)sender).Context; var httpRequest = new HttpRequestWrapper(httpContext.Request); LogBeginRequest(httpContext); // HACK: This is abusing the trace module // Disallow GET requests from CSM extensions bridge // Except if owner or coadmin (aka legacy or non-rbac) authorization if (!String.IsNullOrEmpty(httpRequest.Headers["X-MS-VIA-EXTENSIONS-ROUTE"]) && httpRequest.HttpMethod.Equals(HttpMethod.Get.Method, StringComparison.OrdinalIgnoreCase) && !String.Equals(httpRequest.Headers["X-MS-CLIENT-AUTHORIZATION-SOURCE"], "legacy", StringComparison.OrdinalIgnoreCase) && !IsRbacWhiteListPaths(httpRequest.Url.AbsolutePath)) { httpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized; httpContext.Response.End(); } TryConvertSpecialHeadersToEnvironmentVariable(httpRequest); // HACK: If it's a Razor extension, add a dummy extension to prevent WebPages for blocking it, // as we need to serve those files via /vfs // Yes, this is an abuse of the trace module if (httpRequest.FilePath.IndexOf("vfs/", StringComparison.OrdinalIgnoreCase) >= 0 && (httpRequest.FilePath.EndsWith(".cshtml", StringComparison.OrdinalIgnoreCase) || httpRequest.FilePath.EndsWith(".vbhtml", StringComparison.OrdinalIgnoreCase))) { httpContext.Server.TransferRequest(httpRequest.FilePath + Constants.DummyRazorExtension, preserveForm: true); return; } // Always trace the startup request. ITracer tracer = TraceStartup(httpContext); // Trace heartbeat periodically TraceHeartbeat(); // Skip certain paths if (TraceExtensions.ShouldSkipRequest(httpRequest)) { // this is to prevent Kudu being IFrame (typically where host != referer) // to optimize where we return X-FRAME-OPTIONS DENY header, only return when // in Azure env, browser non-ajax requests and referer mismatch with host // since browser uses referer for other scenarios (such as href, redirect), we may return // this header (benign) in such cases. if (Kudu.Core.Environment.IsAzureEnvironment() && !TraceExtensions.IsAjaxRequest(httpRequest) && TraceExtensions.MismatchedHostReferer(httpRequest)) { httpContext.Response.Headers.Add("X-FRAME-OPTIONS", "DENY"); } if (TraceServices.TraceLevel != TraceLevel.Verbose) { TraceServices.RemoveRequestTracer(httpContext); // enable just ETW tracer tracer = TraceServices.EnsureETWTracer(httpContext); } } tracer = tracer ?? TraceServices.CreateRequestTracer(httpContext); if (tracer == null || tracer.TraceLevel <= TraceLevel.Off) { return; } var attribs = GetTraceAttributes(httpContext); AddTraceLevel(httpContext, attribs); foreach (string key in httpContext.Request.Headers) { if (!key.Equals("Authorization", StringComparison.OrdinalIgnoreCase) && !key.Equals("X-MS-CLIENT-PRINCIPAL-NAME", StringComparison.OrdinalIgnoreCase)) { attribs[key] = httpContext.Request.Headers[key]; } else { // for sensitive header, we only trace first 3 characters following by "..." var value = httpContext.Request.Headers[key]; attribs[key] = string.IsNullOrEmpty(value) ? value : (value.Substring(0, Math.Min(3, value.Length)) + "..."); } } httpContext.Items[_stepKey] = tracer.Step(XmlTracer.IncomingRequestTrace, attribs); }
private void BeginRequest(HttpContext httpContext) { var httpRequest = httpContext.Request; // If an external deployment ID is injected, inject it back into the response string deploymentId = GetExternalDeploymentId(httpRequest); if (string.IsNullOrEmpty(deploymentId)) { httpContext.Response.Headers.Add(Constants.ScmDeploymentIdHeader, deploymentId); } _lastRequestDateTime = DateTime.UtcNow; /* CORE TODO missing functionality: * Disallow GET requests from CSM extensions bridge * Razor dummy extension for vfs */ // Always trace the startup request. ITracer tracer = TraceStartup(httpContext); LogBeginRequest(httpContext); // Trace heartbeat periodically TraceHeartbeat(); TryConvertSpecialHeadersToEnvironmentVariable(httpRequest); // Skip certain paths if (TraceExtensions.ShouldSkipRequest(httpRequest)) { // this is to prevent Kudu being IFrame (typically where host != referer) // to optimize where we return X-FRAME-OPTIONS DENY header, only return when // in Azure env, browser non-ajax requests and referer mismatch with host // since browser uses referer for other scenarios (such as href, redirect), we may return // this header (benign) in such cases. if (Environment.IsAzureEnvironment() && !TraceExtensions.IsAjaxRequest(httpRequest) && TraceExtensions.MismatchedHostReferer(httpRequest)) { httpContext.Response.Headers.Add("X-FRAME-OPTIONS", "DENY"); } if (TraceServices.TraceLevel != TraceLevel.Verbose) { TraceServices.RemoveRequestTracer(httpContext); // enable just ETW tracer tracer = TraceServices.EnsureEtwTracer(httpContext); } } tracer = tracer ?? TraceServices.CreateRequestTracer(httpContext); if (tracer == null || tracer.TraceLevel <= TraceLevel.Off) { return; } var attribs = GetTraceAttributes(httpContext); AddTraceLevel(httpContext, attribs); foreach (string key in httpContext.Request.Headers.Keys) { if (key != null) { if (!key.Equals("Authorization", StringComparison.OrdinalIgnoreCase) && !key.Equals("X-MS-CLIENT-PRINCIPAL-NAME", StringComparison.OrdinalIgnoreCase)) { attribs[key] = httpContext.Request.Headers[key]; } else { // for sensitive header, we only trace first 3 characters following by "..." var value = httpContext.Request.Headers[key].ToString(); attribs[key] = string.IsNullOrEmpty(value) ? value : (value.Substring(0, Math.Min(3, value.Length)) + "..."); } } } httpContext.Items[_stepKey] = tracer.Step(XmlTracer.IncomingRequestTrace, attribs); AddTrackingHeader(httpContext); }
public void Init(HttpApplication app) { app.BeginRequest += (sender, e) => { var httpContext = ((HttpApplication)sender).Context; // Skip certain paths if (httpContext.Request.RawUrl.EndsWith("favicon.ico", StringComparison.OrdinalIgnoreCase) || httpContext.Request.Path.StartsWith("/dump", StringComparison.OrdinalIgnoreCase) || httpContext.Request.RawUrl == "/") { return; } // Setup the request for the tracer var tracer = TraceServices.CreateRequesTracer(httpContext); var attribs = new Dictionary <string, string> { { "url", httpContext.Request.RawUrl }, { "method", httpContext.Request.HttpMethod }, { "type", "request" } }; foreach (string key in httpContext.Request.Headers) { attribs["h_" + key] = httpContext.Request.Headers[key]; } if (httpContext.Request.RawUrl.Contains(".git") || httpContext.Request.RawUrl.EndsWith("/deploy", StringComparison.OrdinalIgnoreCase)) { // Mark git requests specially attribs.Add("git", "true"); } httpContext.Items[_stepKey] = tracer.Step("Incoming Request", attribs); }; app.Error += (sender, e) => { try { var httpContext = ((HttpApplication)sender).Context; var tracer = TraceServices.GetRequestTracer(httpContext); if (tracer == null) { return; } tracer.TraceError(app.Server.GetLastError()); } catch (Exception ex) { Debug.WriteLine(ex.Message); } }; app.EndRequest += (sender, e) => { var httpContext = ((HttpApplication)sender).Context; var tracer = TraceServices.GetRequestTracer(httpContext); if (tracer == null) { return; } var attribs = new Dictionary <string, string> { { "type", "response" }, { "statusCode", httpContext.Response.StatusCode.ToString() }, { "statusText", httpContext.Response.StatusDescription } }; foreach (string key in httpContext.Response.Headers) { attribs["h_" + key] = httpContext.Response.Headers[key]; } tracer.Trace("Outgoing response", attribs); var requestStep = (IDisposable)httpContext.Items[_stepKey]; if (requestStep != null) { requestStep.Dispose(); } }; }