Cùng với phân tích rủi ro ở góc độ kiến trúc, việc review code phục vụ cho mục đích bảo mật là rất quan trọng trong quá trình phát triển phần mềm.

Catching Implementation Bugs Early

Các công cụ static analysis chỉ phân tích mã nguồn của chương trình mà không cố thực hiện chúng. Theo lý thuyết, các công cụ này có thể phân tích cả dạng đã được biên dịch của chương trình mà vẫn cho kết quả tương tự, mặc dù việc decode chương trình có thể khó khăn.

Kiểm tra thủ công, là một dạng của static analysis và tiêu tốn rất nhiều thời gian. Để có thể thực hiện nó một cách hiệu quả, người thực hiện phải biết trước các lỗ hổng bảo mật trông ra sao trước khi họ có thể phân tích mã nguồn một cách kỹ lưỡng.

Giống như một lập trình viên có thể dựa vào compiler để sửa chữa những lỗi nhỏ ở trong cú pháp của ngôn ngữ, người vận hành của một công cụ static analysis tốt có thể áp công cụ đó một cách hiệu quả mà không cần biết quá chi tiết về các loại lỗi bảo mật.

Aim for Good, Not Perfect

Các công cụ static analysis tìm một tập các pattern hay rule cố định ở trong code. Nếu rule dùng để tìm một vấn đề cụ thể nào đó chưa được viết, công cụ sẽ không bao giờ tìm ra vấn đề đó.

Output của công cụ static analysis vẫn cần sự đánh giá của con người. Bởi vì không có cách nào mà một tool có thể tự động biết được vấn đề nào quan trọng hơn vấn đề nào.

Các cá nhân có kiến thức vẫn cần phải dùng đến thiết kế của chương trình để phòng tránh những yếu điểm. Bởi vì, mặc dù các công cụ static analysis có thể tìm được các bugs một cách chi tiết nhưng nó vẫn không thể đánh giá được thiết kế của chương trình.

Một vấn đề nữa của static analysis là nó bắt buộc phải sử dụng những xấp xỉ để tìm bug mà các xấp xỉ này có thể dẫn đến các output kém hoàn hảo.

Đồng thời, các công cụ static analysis có thể sinh ra các âm tính giả - false negative (chương trình có tồn tại bug nhưng mà công cụ không báo cáo) hoặc các dương tính giả - false positive (công cụ báo cáo các bug mà chương trình không có).

Một công cụ được coi là “đáng tin cậy” khi nó có xu hướng không tạo các kết quả false negative, nhưng có thể gây ra một số kết quả false positive. Mặt khác, một công cụ được coi là “không đáng tin cậy” khi nó cố gắng giảm số lượng các kết quả false positive nhưng đôi khi không phát hiện được các lỗi thực sự, dẫn đến các kết quả false negative.

Approaches to Static Analysis

Hiển nhiên, cách tiếp cận đơn giản nhất của static analysis là chương trình grep của các hệ điều hành Unix. Nhược điểm của công cụ này là nó không hiểu bất cứ thứ gì về tập tin mà nó quét.

Lexical analysis là cách tiếp cận được sử dụng bởi các công cụ static analysis đời đầu. Nó tiền xử lý và tokenize các tập tin mã nguồn (tương tự như cách mà compiler thực hiện khi bắt đầu biên dịch chương trình) và sau đó match luồng các token đó với một thư viện có chứa các cấu trúc dễ bị tấn công.

Mặc dù lexical analysis tốt hơn grep, nhưng nó vẫn sản sinh ra một số lượng lớn các false positive bởi vì nó không cố gắng giải thích ngữ nghĩa của mã nguồn.

Bằng cách xây dựng một cây cú pháp trừu tượng (Abstract Syntax Tree - AST) từ mã nguồn, chúng ta có thể giải thích ngữ nghĩa cơ bản của chương trình đang được phân tích. Với các AST, bước tiếp theo mà ta cần làm là xác định phạm vi của việc phân tích:

  • Local analysis: chỉ xem xét một hàm trong chương trình tại một thời điểm và không xem xét các mối quan hệ giữa các hàm.
  • Module-level analysis: xem xét một class hoặc một compilation unit tại một thời điểm và do đó nó có xem xét các mối quan hệ của các hàm ở trong cùng một module cũng như là các thuộc tính được áp dụng cho các class. Tất nhiên, nó sẽ không phân tích các mối quan hệ giữa các module.
  • Global analysis: phân tích toàn bộ chương trình nên nó có xem xét tất cả các mối quan hệ của các hàm.

Phạm vi của việc phân tích giúp xác định lượng ngữ cảnh mà công cụ đang khảo sát. Ngữ cảnh càng rộng thì sẽ càng tốt trong việc giảm thiểu các false positive, nhưng nó có thể dẫn đến một lượng lớn các tính toán cần phải thực hiện.