Phân tích tĩnh đề cập đến các kỹ thuật đánh giá mã nguồn nhằm cảnh báo các lỗ hổng an toàn tiềm tàng mà không thực thi chúng.
Một cách lý tưởng các công cụ tự động có thể tìm kiếm các lỗi an ninh với mức độ đảm bảo nhất định về các lỗi này song việc này vượt quá khả năng của rất nhiều công cụ hiện thời.
Các kỹ thuật kiểm tra phần mềm (testing) thông thường nhằm kiểm tra các hành vi (chức năng) với người dùng thông thường trong điều kiện thông thường nên rất khó để phát hiện ra các lỗi liên quan đến vấn đề an ninh và an toàn.
93 Việc đầu tiên công cụ phân tích tĩnh cần làm là chuyển mã chương trình thành mô hình chương trình (program model) như trong Hình 5-8. Mô hình chương trình thực chất là cấu trúc dữ liệu biểu diễn đoạn mã cần phân tích. Việc phân tích được tiến hành kết hợp với các tri thức về vấn đề an toàn biết trước hoặc dựa trên kinh nghiệm. Bước cuối cùng là biểu diễn kết quả phân tích theo yêu cầu an toàn đề ra. Dạng cơ bản là cách cảnh báo, các công cụ cao cấp có thể cung cấp các phản ví dụ (khi áp dụng các công cụ dựa trên lô-gíc hay kiểm chứng mô hình).
Các kỹ thuật xây dựng mô hình phân tích bao gồm:
Phân tích từ vựng (lexical analysis)
Phân tích câu (parsing)
Cú pháp khái quát (astract syntax)
Phân tích ngữ nghĩa (semantic analysis)
Phân tích luồng điều khiển (control flow)
Phân tích luồng dữ liệu (data flow)
Phân tích lan truyền lỗi (Taint propagation) a. Phân tích từ vựng
Chuyển đoạn mã thành chuỗi các thẻ (token) nhằm loại bỏ những thành phần không quan trọng trong đoạn mã chương trình. Các thẻ có thể chứa định danh (biến, tên hàm, ...) và vị trí của chúng trong đoạn mã. Đoạn mã dưới đây minh họa cho việc chuyển đổi dòng lệnh ra các thẻ.
b. Phân tích câu
Bộ phân tích câu sử dụng ngữ pháp phi ngữ cảnh (Context-free Grammar-CFG) để đối sánh các chuỗi từ hay thẻ. Bộ ngữ pháp sử dụng các luật sinh để diễn tả các ký hiệu của ngôn ngữ (lập trình).
Hình 5-9. Bộ luật sinh cây phân tích
Bộ phân tích câu thực hiện việc suy diễn bằng cách đối sánh chuỗi các ký hiệu (thẻ) với các luật sinh để tạo ra các cây phân tích. Hiện nay các bộ phân tích câu cho các ngôn ngữ như C, C++, hay Java được cung cấp dưới dạng mã nguồn mở hay cách dịch vụ cho phép người phát triển tùy biến theo yêu cầu của mình.
94
Hình 5-10. Cây cú pháp của câu lệnh
c. Cú pháp khái quát
Việc phân tích phức tạp có thể không phù hợp trên cây phân tích do mục tiêu của cây phân tích chỉ nhằm vào việc tách từ. Vì thế cây cú pháp khái quát cung cấp cấu trúc dữ liệu phù hợp với việc phân tích tiếp theo bằng cách loại bỏ các ký hiệu (thẻ) không phù hợp. Các ký hiệu trong cú pháp khái quát có thể ít hơn so với ngôn ngữ lập trình ban đầu.
Hình 5-11. Cây cú pháp khái quát
d. Phân tích ngữ nghĩa
Việc phân tích ngữ nghĩa có thể được bắt đầu bằng việc phân rã các ký hiệu (tên biến hay hàm) và kiểm tra kiểu dữ liệu. Việc phân tích này cho phép lập cấu trúc đoạn mã thông qua việc kiểm tra các lớp của đối tượng được sử dụng, đặc biệt hữu ích với lập trình hướng đối tượng. Chức năng phân tích ngữ nghĩa được sử dụng nhiều trong các bộ biên dịch (compiler) của các môi trường phát triển IDE (Integrated Development
95
Environment) vì nó cho phép kiểm tra các kiểu dữ liệu, phân rã các tên (định danh) trong chương trình mô tả các hằng, biến và hàm.
e. Phân tích luồng điều khiển
Mục tiêu của kỹ thuật này là để theo dõi các tình huống thực thi khác nhau của đoạn mã và thường được thực hiện bằng cách xây dựng đồ thị luồng điều khiển như trong ví dụ dưới đây. Việc xây dựng luồng điều khiển giống như việc xây dựng lưu đồ của chương trình từ đoạn mã, như vậy trái ngược với quá trình phát triển phần mềm thông thường (bắt đầu từ xây dựng lưu đồ rồi mới viết đoạn mã). Việc tái tạo lại luồng điều khiển cho người phân tích hình dung các khối cơ bản của đoạn mã và cách thức vận hành các khối này.
Hình 5-12. Luồng điều khiển
Đồ thị gọi hàm biểu diễn luồng điều khiển giữa các hàm hay phương thức và được xây dựng dựa trên đồ thị có hướng. Về cơ bản, đồ thị này thể hiện trạng thái hoạt động của chương trình thông qua việc hàm nào được kích hoạt (gọi). Kỹ thuật này thường kết hợp với việc phân tích luồng dữ liệu.
Hình 5-13. Đồ thị gọi hàm của ba phương thức larry, moe, curly
f. Phân tích luồng dữ liệu
Kỹ thuật này cho phép kiểm chứng cách dữ liệu di chuyển trong đoạn mã. Việc phân tích này thường kết hợp với luồng điều khiển để xác định vị trí bắt đầu và kết thúc của dữ liệu. Việc phân tích luồng có thể phát hiện những tình huống như sử dụng mật khẩu hay khóa cố định (hard-coded) trong đoạn mã.
96 g. Phân tích lan truyền lỗi
Kỹ thuật này được sử dụng để tìm hiểu các giá trị bên trong đoạn mã mà người tấn công có khả năng kiểm soát bằng cách sử dụng luồng dữ liệu. Việc này cần thông tin về việc biến chứa lỗi xuất hiện ở đâu trong chương trình và cách thức di chuyển trong chương trình. Mục đích của việc phân tích này là để hiểu cách thức các con trỏ có thể tham chiếu đến cùng vị trí nhớ. Việc phân tích này rất quan trọng với việc phân tích lan truyền lỗi.
Việc phân tích tĩnh được sử dụng một cách rộng rãi hơn là mọi người biết. Điều này một phần vì có nhiều kiểu công cụ phân tích tĩnh hướng tới các mục tiêu khác nhau. Hình dưới đây liệt kê một số công cụ phân tích tĩnh hướng tới các mục tiêu khác nhau.
Loại công cụ Địa chỉ
Kiểm tra kiểu lập trình
PMD Parasoft
http://pmd.sourceforge.net http://www.parasoft.com
Kiểm chứng chương trình
Praxis High Integrity Systems Escher Technologies
http://www.praxis-his.com http://www.eschertech.com
Kiểm tra thuộc tính
Polyspace Grammatech http://www.polyspace.com http://www.gramatech.com Tìm lỗi FindBugs
Visual Studio 2005 \analyze
http://www.findbugs.org http://msdn.microsoft.com/vstudio/ Đánh giá an ninh Fortify Software Ounce Labs http://www.fortify.com http://www.ouncelabs.com Hình 5-14. Các công cụ phân tích tĩnh
Một dạng ứng dụng phân tích tĩnh được nhiều người biết tới đó chính là kiểm tra kiểu dữ liệu sử dụng trong chương trình. Các trình soạn thảo sẽ cảnh báo cho người lập trình về các tình huống không tương hợp về kiểu. Điều này có thể dẫn đến việc mất mát dữ liệu và thậm chí gây mất an toàn cho chương trình như trong trường hợp kiểm tra độ dài chuỗi. Một dạng khác là kiểm tra kiểu lập trình (style checking) mà cụ thể yêu cầu người lập trình tuân thủ đầy đủ các qui ước viết mã, đặt tên cũng như sử dụng các cấu trúc điều khiển.
Kiểm tra thuộc tính và kiểm chứng chương trình là ứng dụng phức tạp hơn của việc phân tích tĩnh. Mục tiêu là kiểm chứng đoạn mã xây dựng có phù hợp với các đặc tả của chương trình hay các qui định và ràng buộc đề ra (hay thuộc tính an toàn) với đoạn mã.
97 Nếu các đặc tả về chương trình bao trùm toàn bộ các chức năng của chương trình, các công cụ kiểm chứng có thể thực hiện việc kiểm chứng tương đương để chắc chắn đoạn mã và chương trình tương ứng chính xác với nhau.
Thông thường, các đặc tả (chính tắc) chỉ áp dụng cho một số chức năng thiết yếu của chương trình (hệ thống) nên chỉ áp dụng việc kiểm chứng một phần. Đôi khi còn được gọi là kiểm tra thuộc tính. Đây là các điều kiện mà chương trình phải tuân theo. Các công cụ phức tạp cho phép phân tích cả về mặt thời gian hay cung cấp các ví dụ phản chứng giúp cho người lập trình hình dung tốt hơn về những vi phạm với yêu cầu đề ra. Tìm lỗi cũng là một ứng dụng quan trọng của phân tích tĩnh. Mục tiêu là chỉ ra tình huống mà chương trình có thể hoạt động không như người lập trình dự định. Công cụ tìm lỗi lý tưởng là chứng minh đủ và cần cung cấp phản ví dụ. Điều này cho biết tình huống có thể khi xảy ra lỗi.
Đánh giá an ninh là mục tiêu thu hút được nhiều người chú ý tới các công cụ phân tích tĩnh. Các công cụ thời kỳ đầu tập trung vào việc quét các hàm hay bị lạm dụng như strcpy và cần tiến hành việc đánh giá an ninh thủ công. Điều này đôi khi là cho người dùng coi các lỗi phát hiện được như là các lỗi lập trình hơn là các vấn đề an ninh cần được chú ý. Các công cụ mới thường kết hợp việc tìm lỗi và kiểm tra thuộc tính. Việc kiểm tra tràn bộ đệm có thể được diễn giải như là yêu cầu lập trình sao cho chương trình không truy nhập ngoài không gian nhớ được cấp phát.
Thách thức lớn nhất cho việc phân tích tĩnh là lựa chọn cách thức biểu diễn chương trình phù hợp mà nó cho phép cân đối giữa mức độ chính xác, khả năng mở rộng và kỹ thuật phân tích nhằm phát hiện vấn đề cần quan tâm. Bài toán cơ bản cho phân tích tĩnh thường sử dụng các kỹ thuật áp dụng trong trình dịch bao gồm việc phân tích luồng điều khiển và dữ liệu. Các công cụ phân tích tĩnh tốt thường sử dụng kiểu luật. Điều này cho phép các công cụ phân tích lập mô hình về môi trường hoạt động và kết quả của các lời gọi hệ thống và thư viện lập trình. Luật cũng cho phép định nghĩa các thuộc tính an toàn một cách thuận tiện và các công cụ có thể kiểm chứng được.
Như vậy công cụ phân tích tĩnh tốt cần dễ sử dụng ngay cả với những người không có chuyên môn về vấn đề an toàn. Điều này nghĩa là các kết quả phân tích cần dễ hiểu với người lập trình thông thường, có thể không hiểu biết sâu về an toàn, và hướng dẫn người dùng về thói quen lập trình an toàn. Đặc điểm quan trọng khác là dạng tri thức (tập các luật) công cụ hỗ trợ. Tập luật tốt trợ giúp người dùng đắc lực trong quá trình phân tích đoạn mã. Cuối cùng các công cụ tốt cho phép phát hiện triệt để các lỗi an toàn phổ biến.