Aspect-Oriented Programming và bảo mật

Rohit Sethi

Aspect-Oriented Programming (AOP) là một kiểu lập trình mới nhanh chóng thu hút được các nhà phát triển trong giới CNTT. Một trong các lý do phổ biến là vì nó được phân nhánh ra từ tính phổ biến của Java Spring framework, mọi người bắt đầu hiểu những lợi ích trọng yếu mà AOP mang lại cho vấn đề phát triển ứng dụng. Trong bài viết này chúng tôi muốn giới thiệu đến đông bảo mọi người về AOP. Trước hết chúng ta hãy đặt câu hỏi AOP là gì.

AOP là gì?

Nhiều người không thực sự hiểu AOP là gì và cho rằng AOP là sự thay thế cho ngôn ngữ lập trình hướng đối tượng (OOP), chính vì vậy mà chúng tôi cần giải thích sâu. Khái niệm này rõ ràng hoàn toàn sai: AOP dựa trên OOP. Nó tập trung vào các khái niệm cross-cutting hoặc các khía cạnh –phần mã chung cho các đối tượng khác nhau. Sử dụng một ngôn ngữ AOP (như AspectJ) hoặc các thư viện (như Spring), những người lập trình có thể viết mã cho chức năng này, sau đó định nghĩa vị trí đan kết nó vào trong các đối tượng đang tồn tại. Một ví dụ cần phải được đưa ra để làm sáng tỏ cho các bạn về vấn đề này. Giả dụ chung ta có các lớp Java dưới đây:

Ở đây chúng ta có thể thấy hàm ghi (logging) được nhân đôi thành hai lớp khác nhau. Với AOP, chúng ta cũng thực hiện như vậy (lưu ý cú pháp ở đây đã được đơn giản hóa để tạo sự rõ ràng rễ hiểu):

Lúc này chúng ta tạo một khía cạnh LogInterceptor để có thể xen trước và sau bất kỳ một lời gọi nào đến class (bỏ qua những chi tiết về cách xen xảy ra như thế nào). Chúng ta cần phải tạo một proxy cho các lời gọi. Thứ quan trọng cần lưu ý về ví dụ này là cả MyFirstClass và MySecondClass đều không biết và không phụ thuộc vào LogInterceptor. Trong ứng dụng thực mà mã logging có thể được sao chép thành hàng trăm file class khác nhau, chính vì vậy việc tập trung nó giống như vậy sẽ làm giảm số lượng mã nguồn một cách đáng kể. Ở đây có thể thực hiện rẽ nhánh vì chúng ta có thể bổ sung các khía cạnh bảo mật cho các ứng dụng mà không cần thay đổi mã đã tồn tại từ trước.

Những gợi ý bảo mật

Một vài khái niệm cross-cutting có liên quan đến bảo mật được tìm thấy rải rác thông qua logic ứng dụng:

  • Logging
  • Access control
  • Error handling
  • Transaction management
  • Session management (trong một số trường hợp)
  • Input/output validation

Sử dụng AOP, chúng ta có thể tách riêng một đoạn lớn các khái niệm ra khỏi cả mảng mã lớn và tập hợp chúng lại. Có một số trường hợp bạn sẽ không thể tập hợp (ví dụ như việc quản lý lỗi), nhưng lớn hơn, AOP cho phép các chuyên gia phát triển có thể tập hợp trên Plain Old Java Object (hoặc bất cứ ngôn ngữ gì mà bạn đang sử dụng). POJO về bản chất là mã các chuyên gia phát triển đã được học để viết ngay từ những ngày ban đầu; mã này chỉ tập trung vào sự logic và không có các khái niệm khác như sự hợp lệ hóa đầu vào, bản ghi, điều khiển truy cập và vấn đề quản lý lỗi. Mã càng rõ ràng hơn thì càng ít bị lỗi hơn (trong đó có cả các lỗi bảo mật).

Một ngụ ý khác về vấn đề này là các chuyên gia lão luyện có thể chịu trách nhiệm cho việc viết mã các khía cạnh bảo mật. Đây chính là những người đã được đào tạo một cách đặc biệt và có kinh nghiệm trong lĩnh vực như quản lý session hoặc điều khiển truy cập, có thể quản lý phần lớn nhất của mã đó trong toàn bộ một dự án hoặc một thành phần của dự án.

Ví dụ thực: Bổ sung sự hợp lệ hóa đầu vào cho ứng dụng có trước

Rõ ràng, việc sử dụng AOP với toàn bộ tiềm lực của nó trên các ứng dụng đang tồn tại trước sẽ cần phải gần như viết lại ứng dụng – thứ thường không mấy khả thi. Những điều thú vị đối với cộng đồng bảo mật là cách sử dụng AOP trong một cơ sở mã đã tồn tại trước đó cho các chức năng bảo mật không được bổ sung.

Chúng ta hãy xem xét một chút vào ứng dụng Customer Relationship Management (CRM) đang tồn tại, ứng dụng này có một số lỗ hổng XSS (Cross-site Scripting). Khi nhập một đầu vào các hàng hóa mới một trong các biểu mẫu web với mô tả <script>alert(document.cookie)</script>.

Chúng ta nhận được:

Biểu mẫu này là một lỗ hổng XSS. Chúng tôi biết có một số phần mã dính líu đến lỗ hổng này. Chính vì vậy chúng tôi quyết định bảo vệ đối tượng NewLead trong lớp doanh nghiệp bằng cách thêm sự hợp lệ hóa đầu vào ở bất cứ thời điểm nào mà phương pháp setter trên thuộc tính String bị gọi. Đây là một mảng cắt nhỏ của lớp NewLead có một số phương pháp setter:

Chúng tôi đã tạo một đoạn mã dưới đây dưới ngôn ngữ lập trình AspectJ, đoạn mã này dễ dàng được tích hợp vào trong các ứng dụng Java đang tồn tại – để làm giảm bớt XSS trong ứng dụng. Bạn không cần phải lo lắng nếu trông chúng có lộn xộn đôi chút. Chúng tôi sẽ giải thích ví dụ một cách chi tiết:

public aspect InputValidator-

Một Aspect trong AspectJ gần giống như một lớp. Nó có thể có các biến trường hợp và các phương pháp bên trong. Có một số sự khác biệt chính ở đây đó là pointcut và lời chỉ dẫn.

pointcut myPointcut(String argument): execution(* NewLeadBean.set*(String)) && args(argument)-

Pointcut trong AOP định nghĩa nơi chúng ta muốn các aspect tương tác với phần còn lại của mã. Trong trường hợp này, pointcut myPoint() sẽ được triệu gọi mỗi khi ai đó gọi setName(), setSalesStage()… từ lớp NewLeadBean. Cú pháp NewLead.set*(String) rất trực giác: bất kỳ phương pháp nào là một phần của lớp NewLead đều được bắt đầu với "set", và có một tham số chuỗi riêng.

&& args(argument) ý nói rằng chúng ta muốn lưu các tham số chuỗi đến một biến có tên argument (nghĩa là nếu ai đó gọi myNewLeadObject.setName("rohit") thì rohit sẽ được lưu vào biến argument). Ở đây các pointcut có thể khá phức tạp đôi chút.

before (String argument): myPointcut(argument)

Đây là những gì mà chúng tôi gọi là “lời chỉ dẫn”. Ý muốn nói rằng, trước một triệu gọi nào đó của myPointcut (nghĩa là trước khi NewLead.set*() được gọi), sự thực thi tuân theo mã. Lưu ý chúng ta có thể phân biệt sau mỗi một vòng (nghĩa là cả trước và sau) mỗi một triệu gọi của pointcut. Argument là một chuỗi được chuyển qua như một tham số. Ví dụ này thể hiện rằng chúng ta đang lấy một argument chuỗi (trong trường hợp của chúng ta là "rohit") và kiểm tra xem nó có đưa qua hợp lệ danh sách trắng bằng bộ kiểm tra biểu thức thông thường hay không. Nếu chúng ta đã nhập vào đó dòng <script>alert(document.cookie)</script> như tên của đầu vào người bán hàng thì trang tiếp theo của màn hình sẽ như sau:

Điều này thể hiện rằng aspect đã dừng một cách thành công tấn công.

Ngoài ra nó còn có một số vấn đề tốt hơn đó là bạn sẽ có một phần mã trung tâm có thể thay đổi một cách dễ dàng. Giả dụ khi phát hiện ra một danh sách trắng bị hổng trong một biểu mẫu của XSS thì bạn hoàn toàn có thể điều chỉnh danh sách này đúng chỗ và tự động phổ biến rộng rãi. Tuy vậy, nếu phát hiện thấy các phần khác của mã cũng có lỗ hổng XSS thì bạn có thể định nghĩa một cách đơn giản các pointcut bổ sung, nhanh hay chậm còn phụ thuộc vào các tấn công như thế nào. Thêm vào đó, nếu bạn phát hiện ra có một số danh sách trắng cần trong ứng dụng của mình thì có thể định nghĩa các biểu thức thông thường bên trong cùng một aspect.

Có một số cấu trúc tăng sự sử dụng AOP cho vấn đề bảo mật. Ví dụ, một trong những thách thức lớn nhất với việc hợp lệ hóa đầu vào ngày nay là có quá nhiều giao diện trong một ứng dụng phức hợp. Nhiều ứng dụng doanh nghiệp lớn có nhiều giao diện web HTML đơn, mỗi một ứng dụng Web đơn lại có thể có các điểm vào thông qua:

  • Các dịch vụ Web
  • Sự đa dạng máy khách (ví dụ như các ứng dụng, applet)
  • Enterprise-Java Beans
  • Các kết nối CORBA
  • Các kết nối tầng giữa khác

Việc xây dựng vấn đề kiểm soát hợp lệ hóa đầu vào trong giao diện kiểu như vậy không ngăn chặn được các tấn công khi nhập vào trong một giao diện khác (ví dụ như các dịch vụ Web). Cũng thời điểm này, các tấn công như cross-site scripting chỉ ảnh hưởng lên một số giao diện nào đó (ví dụ như các trình duyệt Web) chứ không phải tất cả (ví dụ như các ứng dụng phong phú). Với AOP, bạn có thể định nghĩa cách phòng chống như các biểu thức thông thường theo các aspect, sau đó định nghĩa nơi chúng sẽ được bổ sung thông qua pointcut. Điều này có nghĩa rằng một số phòng chống có thể được trỏ đến mã giao diện, trong khi đó các phương pháp khác lại trỏ đến logic doanh nghiệp (như chúng ta đã thực hiện trong ví dụ NewLead).

Bổ sung chi tiết và sự ảnh hưởng về hiệu suất

AOP làm việc chính xác như thế nào? Điều đó phụ thuộc vào nền tảng mà bạn đang sử dụng. Chúng tôi đã thảo luận về AspectJ ở đây qua một số lý do cụ thể:

• Rất dễ dàng tích hợp AspectJ vào trong các ứng dụng Java đang tồn tại. AspectJ đơn giản chỉ bổ sung Aspect vào các mã nhị phân Java bằng cách sử dụng kỹ thuật được gọi là "weaving", về bản chất là thay đổi mã đã biên dịch để bổ sung lời khuyên tại các pointcut cụ thể. Tại runtime đối với các ứng dụng Web bạn chỉ cần thêm thư viện aspectjrt.jar vào WEB-INF\lib trong dự án của mình.

• Nếu bạn đang sử dụng Eclipse Integrated Development Environment (IDE), biến đổi một ứng dụng Java đang tồn tại sang AspectJ là hoàn toàn bình thường với AspectJ Development Tools.

• Do AspectJ làm việc tại thời điểm biên dịch nên nó rất hiệu quả và không phải gánh chịu những ảnh hưởng xấu đến vấn đề hiệu suất.

Như chúng tôi đã đề cập ở phần đầu của bài này, Spring Framework cũng có sự hỗ trợ AOP. Có một số sự khác nhau chính trong thiết kế với Spring AOP và AspectJ, đáng kể nhất đó là Spring làm việc trên phần đỉnh của Java và không yêu cầu một bộ biên dịch khác. Do AOP của Spring được thiết kế cho Spring-controlled bean, có nghĩa việc tích hợp nó vào trong ứng dụng non-Spring đang tồn tại có thể sẽ khó khăn. Điều đó đã nói lên rằng AOP của Spring vẫn rất có giá khi AspectJ không khả thi hoặc trong các ứng dụng đã sử dụng Spring. Các chuyên gia phát triển Spring cố ý đã cung cấp sự hỗ trợ tích hợp khả năng Inversion of Control của Spring với sức mạnh AOP của AspectJ.

Tiếp theo là gì?

Khả năng sử dụng AOP trong bảo mật ứng dụng là rất lớn. Ở đây có một số các ứng dụng mà AOP được sử dụng cho việc bảo mật:

• Thực hiện điều khiển truy cập một cách độc lập với logic ứng dụng. Thay vì phải có các check rõ ràng như checkAccess(User) trong mỗi hàm nhạy cảm, bạn có thể hoàn thành được vấn đề này thông qua các aspect và cho phép các chuyên gia phát triển tập trung vào logic doanh nghiệp.

• Thực hiện các chính sách bảo mật ứng dụng như cấm nhà lập trình gọi các thư viện SQL động (ví dụ như executeQuery()). Bất cứ khi nào thư viện đó được gọi, bạn có thể sử dụng aspect để đưa vào một ngoại lệ và ghi một cách chính xác địa điểm xuất hiện các lời gọi nguy hiểm.

Các chuyên gia phát triển phần mềm đang quay sang bình chọn cho AOP. JBoss, WebSphere, và WebLogic đã tích hợp AOP hoặc có các thông báo thực hiện trong tương lai. Lúc này cộng đồng bảo mật ứng dụng cần phải theo một bộ hoàn chỉnh bằng cách cung cấp hướng dẫn về AOP có thể được sử dụng như thế nào trong vấn đề bảo mật.

Chúng ta có thể thực hiện điều này bằng cách bảo đảm bổ sung AOP vào chương trình đào tạo bảo mật ứng dụng, bên cạnh đó là tiến hành nghiên cứu nhiều hơn nữa về cách AOP làm việc như thế nào trong việc bảo mật các ứng dụng trong sản xuất (gồm có các điểm chuẩn cho sự ảnh hưởng về hiệu suất) và cung cấp thêm nhiều ví dụ về mã cho các chuyên gia phát triển.

Thứ Năm, 03/04/2008 09:07
31 👨 6.970
0 Bình luận
Sắp xếp theo
    ❖ Kiến thức cơ bản