Thẻ tiêu đề cho “Giải thích Đánh giá mã bảo mật” hiển thị một bảng diff mã với một dòng gốc và một dòng đã thay đổi, cùng bên dưới là ba giai đoạn có nhãn: MẪU (tất định), rồi ĐẠT, rồi SUY LUẬN (mô hình).
Guides & Insights

Giải thích Đánh giá Bảo mật Mã nguồn: Người đánh giá tìm kiếm điều gì trong một Diff và tại sao

Tác giả

Rowan Sterling

Ngày đăng

Mô hình mới nhất · 20Xem tất cả mô hình →
Benchmark: Artificial Analysis · cập nhật hằng ngày
Quay lại tất cả bài viết

Đánh giá bảo mật mã nguồn là việc đọc mã nguồn một cách có chủ đích nhằm mục đích cụ thể là tìm ra những cách có thể khiến nó làm điều mà tác giả của nó không hề mong muốn. Định nghĩa đó nghe có vẻ hiển nhiên cho đến khi bạn so sánh nó với việc mà hầu hết các nhóm thực sự triển khai, đó là một buổi đánh giá chức năng với một danh sách kiểm tra bảo mật ở ngay bên cạnh. OWASP Secure Code Review Cheat Sheet vạch ra ranh giới một cách chính xác: đánh giá bảo mật khác với đánh giá chức năng ở chỗ nhắm vào việc xác thực đầu vào, xác thực danh tính, phân quyền, triển khai mật mã và các vectơ tấn công, thay vì tính đúng đắn. Một diff hoạt động hoàn hảo vẫn có thể trao cho kẻ tấn công một con đường vào hệ thống, và chỉ một trong hai kiểu đánh giá đó là đang tìm kiếm điều đó.

Điều khiến chủ đề này đáng có một bài viết ở thời điểm hiện tại thay vì vào năm 2020 là việc một mô hình ngôn ngữ có thể đọc một diff một cách thành thạo, với chi phí rẻ và liên tục — điều này thay đổi kinh tế học của việc review mà không thay đổi một chiều nào trong số các chiều đang được review. Bản checklist còn cũ hơn cả các mô hình và vẫn không hề nhúc nhích. Thứ đã nhúc nhích là tần suất bạn có thể đủ khả năng chạy nó.

Điều mà người đánh giá thực sự đang tìm kiếm khi đọc

Bảng tra cứu nhanh (cheat sheet) của OWASP chia công việc thành sáu lĩnh vực, và điều hữu ích ở danh sách này là mỗi lĩnh vực là một câu hỏi chứ không phải một hạng mục. Hạng mục thì dễ dẫn đến việc đánh dấu cho đủ; câu hỏi thì tạo ra phát hiện.

Card 'What a reviewer is actually looking for' listing six dimensions - input and injection, authn and session, authorization, cryptography, business logic, configuration and secrets - each with the question it answers about a diff.

Đầu vào và chèn. Vấn đề không phải là liệu mã có bộ làm sạch hay không; mà là liệu có thứ gì đó chạm tới một trình thông dịch — shell, SQL, template engine, bộ đánh giá biểu thức — và yêu cầu đó được cho là phải được thực thi ở lớp nào. Một diff thêm truy vấn được tham số hóa là tốt; một diff thêm truy vấn được tham số hóa vào một hàm mà những người gọi khác truyền chuỗi thô vào là một diff đã chuyển vấn đề đi chỗ khác.

Xác thực và phiên. Ai là bên gọi, và mã đã xác lập điều đó bằng cách nào? Lỗi tái diễn không phải là kiểm tra mật khẩu bị hỏng, mà là một phiên tồn tại qua thay đổi đặc quyền — việc nâng quyền diễn ra, token vẫn giữ các claim cũ, và một giờ sau các đặc quyền vẫn còn đó.

Phân quyền.Kiểm tra ở cấp độ đối tượng, mỗi lần, trên mọi route. Điều cụ thể cần tìm trong một diff là một route mới không kế thừa guard mà các route anh em của nó có, bởi vì middleware được đăng ký theo từng router và một route được thêm vào dưới một router khác đơn giản sẽ không được bao phủ.

Mật mã học. Thuật toán nguyên thủy, chế độ, độ dài khóa và nơi lưu khóa. Hai câu hỏi bắt được hầu hết các phát hiện thực tế: liệu bất kỳ ai đọc được mã nguồn này có cũng đọc được khóa hay không, và liệu đây có phải là phép so sánh không được rò rỉ thời gian hay không.

Logic nghiệp vụ.Liệu trình tự có thể bị sắp xếp lại, phát lại hoặc bị điều khiển bởi các giá trị do client chọn hay không. Giá cả, số lượng, vai trò hoặc chuyển đổi trạng thái đến từ client mà không được tính toán lại ở phía máy chủ đều là một phát hiện, bất kể nó được xác thực như thế nào.

Cấu hình và bí mật. Những gì diff đưa vào mà lẽ ra không bao giờ nên có trong một kho mã, và liệu các giá trị mặc định có an toàn ở đúng nơi mã thực sự chạy hay không. Một cờ debug mặc định là true là một phát hiện về môi trường production, chứ không phải về bản thân cờ đó.

Đánh giá đường cơ sở so với đánh giá khác biệt

OWASP phân biệt hai chế độ, và việc đánh đồng chúng là nguyên nhân phổ biến của cả nỗ lực lãng phí lẫn sự tự tin sai lầm. Một đánh giá cơ sở xem xét toàn diện cơ sở mã, và đây là công cụ phù hợp cho một ứng dụng mới hoặc một bản phát hành lớn, việc tiếp nhận một hệ thống cũ, đáp ứng một yêu cầu tuân thủ, hoặc thực hiện phân tích sau sự cố sau khi đã có điều gì đó xảy ra sai sót. Một đánh giá dựa trên diff chỉ xem xét những gì đã thay đổi, và đây là công cụ phù hợp cho pull request, commit, hoàn thành tính năng và xác thực liên tục.

Sự phân biệt này quan trọng vì hai loại tạo ra những công việc khác nhau. Một đánh giá cơ sở kết thúc bằng danh sách việc tồn đọng; một đánh giá diff kết thúc bằng quyết định hợp nhất. Chạy loại thứ nhất liên tục là điều không khả thi về chi phí, và chạy loại thứ hai trên một cơ sở mã chưa từng có loại thứ nhất chính là cách các nhóm tích lũy cảm giác sai lệch về độ bao phủ: mọi thay đổi đều qua đánh giá, còn đoạn nối chuỗi SQL vốn đã tồn tại vẫn nằm nguyên đó, không bị đụng tới.

Screenshot of the OWASP Cheat Sheet Series index entry for Secure Code Review, showing the introduction describing manual examination of source code for vulnerabilities automated tools miss, the baseline-versus-diff review split with their use cases, and a table of contents covering methodology, patterns, techniques, checklists and tools.

Các kỹ thuật và mỗi kỹ thuật phát hiện được gì

OWASP liệt kê bốn kỹ thuật rà soát, và chúng được phân biệt bởi bằng chứng mà người rà soát lần theo chứ không phải bởi mức độ kỹ lưỡng. Phân tích mẫu mã lần theo những dạng quen thuộc: lời gọi hàm nguy hiểm, việc giải tuần tự hóa đầu vào không đáng tin cậy, thiếu kiểm tra độ dài. Cách này nhanh và đúng là điều một công cụ tất định làm tốt. Phân tích luồng dữ liệu lần theo một giá trị từ nơi nó đi vào đến nơi nó được sử dụng, đây là cách duy nhất để trả lời liệu bộ làm sạch được áp dụng ba hàm trước đó có còn áp dụng ở đây hay không. Rà soát dựa trên mối đe dọa bắt đầu từ năng lực của kẻ tấn công và đi tiếp về phía trước, đó là cách bạn bắt được khoảng trống giữa hai thành phần riêng lẻ đều đúng đắn. Rà soát logic nghiệp vụ đọc quy trình công việc thay vì từng dòng mã, đặt câu hỏi chuỗi thao tác hợp lệ nào tạo ra trạng thái không hợp lệ.

Chỉ mục đầu tiên trong bốn mục đó là được máy quét xử lý tốt, và chỉ mục đầu tiên và thứ hai là được một lượt mô hình không có hướng dẫn xử lý tốt. Đó không phải là lập luận chống lại các mô hình; đó là lập luận ủng hộ việc nói cho người đánh giá biết bạn muốn kỹ thuật nào, bởi vì một lời nhắc yêu cầu “các vấn đề bảo mật” sẽ luôn tạo ra phân tích mẫu và dừng lại.

AI đã thay đổi điều gì và điều gì nó chưa thay đổi

Nghiên cứu về mã do AI viết đã làm cho điểm nghẽn đánh giá trở nên gay gắt hơn chứ không phải tạo ra nó. Nghiên cứu của NYU về các gợi ý của GitHub Copilot, được công bố tại IEEE S&P năm 2022, phát hiện khoảng 40% đoạn mã được tạo chứa một mẫu có thể bị khai thác trong 18 hạng mục CWE. Công trình của Stanford về hành vi của nhà phát triển, được công bố tại ACM CCS năm 2023, đã phát hiện tác động hệ trọng hơn: những nhà phát triển sử dụng trợ lý tự tin hơn rằng mã của họ an toàn trong khi lại tạo ra mã kém an toàn hơn ở mức có thể đo được. Sản lượng nhiều hơn cộng với sự tự tin cao hơn là một vấn đề của việc đánh giá.

Một mô hình ngôn ngữ với vai trò người đánh giá thay đổi một biến trong phương trình đó — thông lượng. Nó có thể đọc mọi pull request, với chi phí theo token thay vì theo ghế, và có thể được hỏi rõ ràng sáu câu hỏi ở trên. Nó không thay đổi các câu hỏi, và nó không có được hai thuộc tính khiến một bài đánh giá đáng tin cậy: tính tất định và khả năng thực thi mã. Cùng một diff được đánh giá hai lần có thể tạo ra các phát hiện khác nhau, mức độ nghiêm trọng thay đổi giữa các lần chạy, và người đánh giá không thể chứng minh rằng một đường dẫn bị gắn cờ là có thể đạt tới.

Screenshot of the OrcaCode Review product page on OrcaRouter headed 'Open engine. Open prompt. Open bill.', describing a pull-request reviewer whose rules and merge gate run on GitHub under an MIT licence and are billed per token, with an example finding about an idempotency key that still varies in time.

OrcaCode Review là dạng sản phẩm hóa của ý tưởng đó, và đáng để xem xét kỹ vì các lựa chọn thiết kế làm lộ ra những kiểu thất bại. Một đánh giá cô lập được trường hợp lỗi thành một bình luận cụ thể, có thể tái tạo — khóa này vẫn thay đổi theo thời gian, nên một khoản tính tiền được thử lại sẽ tạo ra khóa mới và có thể tính tiền hai lần, đây là thay đổi một dòng — là đang làm công việc mà một người đánh giá phải làm. Một đánh giá báo cáo “vấn đề bảo mật tiềm ẩn” mà không có bản tái hiện là đang phân tích mẫu với độ chính xác kém hơn cả bộ so khớp mẫu. Và một công cụ quy tắc và cổng kiểm soát được phát hành theo giấy phép mở đang đưa ra một tuyên bố ngầm đáng để kiểm chứng: rằng phán đoán của người đánh giá cũng nên có thể được xem xét lại.

Cách sắp xếp hai lớp hiệu quả

Với một trình quét tất định và một trình đánh giá xác suất, cách sắp xếp trụ được trong thực tế là giao cho mỗi bên công việc mà nó thực sự giỏi. Trình quét chạy trên mỗi lần push: vài giây, không mô hình, không biến thiên, bắt được secrets, các lệnh gọi API nguy hiểm, giải tuần tự không an toàn và các import do ảo giác. Trình đánh giá chạy trên mọi pull request: vài phút, dựa trên mô hình, bắt được logic nằm giữa các dòng — liệu kiểm tra phân quyền có chạy trước thao tác fetch không, liệu khóa idempotency có được gắn với một giá trị ổn định không, liệu route mới có kế thừa guard không.

Rồi đến lớp mà mọi người bỏ qua: cổng chặn. Một phát hiện không chặn được việc hợp nhất chỉ là một thông báo, mà thông báo thì người ta đọc vào thứ Sáu. Cổng chặn phải hẹp và phải tuyệt đối — phát hiện nghiêm trọng thì chặn, mọi thứ khác thì chỉ bình luận — và việc miễn trừ phải được ghi lại, vì giá trị của một cổng chặn không nằm ở việc nó chặn được phát hiện đầu tiên mà ở việc nó khiến mọi lần vượt chặn đều hiện rõ.

Đang xem xét thay đổi, không phải toàn bộ mã nguồn

Kỷ luật thực hành cho một buổi đánh giá diff là một chuỗi ngắn, và giá trị của nó nằm ở chỗ có thể dạy được trong một buổi chiều. Hãy đọc diff để xem nó bổ sung gì vào bề mặt tấn công thay vì xem nó thay đổi điều gì. Với mỗi điểm vào mới, hãy lần theo đầu vào thêm một bước vượt qua chỗ diff kết thúc — lỗ hổng thường nằm trong hàm mà nó gọi, hàm mà tác giả đã không đọc. Với mỗi tuyến mới, hãy kiểm tra đăng ký guard. Với mỗi phụ thuộc mới, hãy kiểm tra xem nó có tồn tại không và liệu người khác đã công bố tên đó chưa. Với mỗi giá trị cấu hình mới, hãy hỏi nó mặc định thành gì ở nơi nó sẽ thực sự được triển khai.

Không điều nào trong số đó đòi hỏi phải biết ngôn ngữ đó một cách thành thạo, đó là lý do vì sao nó có thể mở rộng cho những người đánh giá không phải chuyên gia bảo mật. Nó đòi hỏi người đánh giá phải xem diff thay vì xem tệp, bởi vì toàn bộ phương pháp phụ thuộc vào việc biết cái gì là mới.

Và đây là giới hạn thành thật: một đánh giá diff không thể tìm ra lỗ hổng đã tồn tại từ trước diff đó. Nếu ứng dụng chưa từng có một đánh giá cơ sở, thì công việc bảo mật có giá trị cao nhất không phải là một người đánh giá giỏi hơn — mà là một lượt rà soát có chủ đích trên toàn bộ codebase, ở những khu vực liên quan đến tiền, danh tính và truy cập tệp, với sáu câu hỏi ở trên. Sau đó, mọi đánh giá diff tiếp theo đều đang bảo vệ một thứ đã từng được xem xét một lần. Trước đó, mọi đánh giá diff đều đang bảo vệ một ẩn số.