Nhìn đi nhìn lại thì đã có khá nhiều bài post về Java được team chia sẻ, do vậy hôm nay mình sẽ đổi gió viết về ASP.NET. Đầu tiên mình sẽ nói tổng quan về ASP.NET Application Life Cycle.

Để có cái nhìn ban đầu mọi người nên đọc bài viết từ MS trước ASP.NET Application Life Cycle for IIS 5.0 and 6.0 và ASP.NET Application Life Cycle for IIS 7.0

Hiện giờ mình thấy IIS V5/6 ít được sử dụng nên mình chỉ đưa cái mô hình flow từ MS. Các object được khởi tạo ra sao mình sẽ nói sau.

Sự khác nhau dễ thấy nhất giữa IIS version cũ và mới nằm ở cách xử lý request hay còn gọi là request-processing pipeline.

Ở IIS6 thì ASP.NET được xem như là IIS ISAPI extension, khi một request được gửi đến thì đầu tiên nó đi qua IIS pipeline và sau khi xác định được handler xử lý tương ứng nó sẽ đi tiếp qua ASP.NET pipeline. Tuy nhiên từ IIS 7 trở đi nó đã được tích hợp vào IIS như một thể thống nhất, mọi request đều đi qua chung một chuỗi các stage (https://learn.microsoft.com/en-us/iis/application-frameworks/building-and-running-aspnet-applications/aspnet-integration-with-iis).

Quan sát trong IIS Manager.

Còn về ExecuteHandler nó sẽ mapping các file extension được người dùng request tới các handler để xử lý và trả về response. Ví dụ .aspx -> System.Web.UI.PageHandlerFactory.

Tóm gọn lại thì các stage chính của ASP.NET như sau:
1. Khởi tạo instance của ApplicationManager class cho request đầu tiên khi được gửi từ browser (có thể hiểu nó chính là Application Domain).

2. Tạo instance của HostingEnviroment class.

3. Các object HttpContex, HttpRequest, HttpResponse được tạo.

4. object HttpApplication được khởi tạo để chạy ứng dụng.

Sau đó request sẽ đi qua một chuỗi các pipeline event như hình bên dưới (do nhiều quá nên mình đưa luôn ảnh). Đặc biệt có event MapRequestHandler thì đây là nơi sẽ cho chúng ta biết thằng handler nào sẽ được chọn để xử lý. Vd: aspx, ashx, asmx.

Ngoài ra mình sẽ giải thích thêm về Application Pool và Application Domain:

Application Pool: Nếu hai ứng dụng được cấu hình với hai AppPool khác nhau thì khi run sẽ tạo ra hai process w3wp.exe riêng biệt (isolate process). Được quản lý bởi IIS.
Application Domain: Nếu hai ứng dụng được cấu hình chung một AppPool (sẽ chạy dưới cùng một w3wp.exe process), lúc này nó sẽ chạy dưới hai Application Domain khác nhau. Có thể kiểm tra thông qua AppDomain.CurrentDomain.FriendlyName và nó được quản lý bởi ASP.NET runtime.

Để hiểu rõ hơn, mình có setup lab cơ bản để debug. Câu hỏi đặt ra ở đầy là điều gì xảy ra khi access một file .aspx ?

Giả sử file test.aspx được truy cập lần đầu tiên.

<%@ Page Language=”C#” Debug=”True”%>
<script runat=”server”>
protected void Page_Load(object sender, EventArgs e)
  {
HttpContext.Current.Response.Write(“Hello World”);
</script>

Entrypoint sẽ bắt đầu từ ProcessRequestNotification.

Object IIS7WorkerRequest và HttpContext được tạo bên trong method ProcessRequestNotificationHelper. Hai object đó sẽ được đưa vào HttpRuntime để xử lý request.

Gọi tới EnsureFirstRequestInit để thực hiện kiểm tra quyền cho thư mục Temporary ASP.NET Files cũng như load assembly trong thư mục bin, đọc thông tin cấu hình…vv.

[+] context.Response.InitResponseWriter() -> tạo object HttpResponse.

[+] httpHandler = HttpApplicationFactory.GetApplicationInstance(context) ->tạo object HttpApplication.

Khi quá trình khởi tạo kết thúc, gọi tới BeginProcessRequestNotification để tiếp tục xử lý.

System.Web.HttpApplication.MaterializeHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()

Đây là nơi mapping file extension -> handler.

UnsafeIISMethods.MgdGetHandlerTypeString() -> Native DLL -> System.Web.UI.PageHandlerFactory.

Như vậy file test.aspx cuối cùng sẽ được build thành Page object -> render -> return response.

Đi vào phân tích file test.aspx được build thành Page object.

Vì file test.aspx được request lần đầu do đó bên trong method GetVPathBuildResultInternal sẽ thực hiện convert nội dung file test.aspx -> C# code.

Call tới csc.exe để compile source C# -> file .dll

File App_Web_f0yyzpmp.dll được lưu tại C:\Windows\Microsoft.NET\Framework64\[version]\Temporary ASP.NET Files\

File App_Web_f0yyzpmp.dll sẽ được load lên, chỗ này cũng giải thích vì sao khi file test.aspx được request lần đầu sẽ tốn thời gian hơn để phản hồi.

Để cải thiện về hiệu năng, thì sau khi build xong dll sẽ lưu vào trong Memory.

Những request sau đó kết quả sẽ được lấy luôn từ Memory thông qua cacheKey.

Như chúng ta đã biết trên IIS thì sau 20′ nếu không có request nào được gửi đến thì process w3wp.exe sẽ bị terminate ( do tối ưu về hiệu năng).

Một câu hỏi tiếp theo đặt ra là nếu w3wp.exe bị terminate và có request mới phát sinh tới file test.aspx -> Lại thực hiện compile lại từ đầu ?

Câu trả lời là không. Quay ngược lên phía trên thì file .dll ứng với file test.aspx đã được lưu ra disk ở folder Temporary ASP.NET Files.

Lúc này thông tin file .dll sẽ được lấy ra từ file test.aspx.cdcab7d2.compiled

Đơn giản có thể hình dung như này.

Có thể khi đọc đến đây bạn sẽ thắc mắc là sao không thấy có attack vector nào mà phải viết dài dòng vậy, một vài kỹ thuật mình sẽ viết sau và đây cũng là tiền đề cho những kỹ thuật đó. Cảm ơn mọi người đã đọc, nếu có sai sót rất mong nhận được góp ý qua https://twitter.com/Dien_PV.

Link tham khảo:

https://learn.microsoft.com/en-us/previous-versions/aspnet/bb470252(v=vs.100)

https://www.west-wind.com/presentations/howaspnetworks/howaspnetworks.html

https://learn.microsoft.com/en-us/iis/get-started/introduction-to-iis/introduction-to-iis-architecture

https://blog.viettelcybersecurity.com/deep-understand-aspx-file-handling-and-some-related-attack-vector/

556 lượt xem