Bình luận đề thi Registered Information Security Specialist, Xuân 2024 (Reiwa 6), câu hỏi buổi chiều Q1 — JWT alg=none, ủy quyền API và giảm thiểu tạm thời bằng WAF

· · Registered Information Security Specialist, Chuyên viên bảo mật đã đăng ký, API, Bảo mật API, JWT, Xác thực, Ủy quyền, WAF, Log4Shell, An ninh thông tin, Lỗ hổng, IPA

“Chúng ta kiểm tra chữ ký JWT, nên ID người dùng có thể tin được.”

Câu đó chỉ đúng một nửa.

Câu hỏi 1 của buổi chiều (PM) kỳ thi Registered Information Security Specialist Xuân 2024 (Reiwa 6) được dựng quanh một API do ứng dụng điện thoại gọi.1 Khi xác thực thành công một JWT được phát hành, và JWT đó được gắn vào các lời gọi API lấy và cập nhật thông tin người dùng. Thoạt nhìn, đây là bố cục hoàn toàn thông thường.

Tuy nhiên, đánh giá phát hiện bốn vấn đề sau.

  1. Đổi alg trên header JWT thành none khiến JWT không chữ ký vẫn qua.
  2. Giữ JWT hợp lệ nhưng đổi mid sang ID người dùng khác cho phép đọc hoặc cập nhật thông tin của người khác.
  3. Thêm status=paid không có trong đặc tả biến người dùng miễn phí thành người dùng trả phí.
  4. Mã xác thực bốn chữ số gửi bằng email có thể bị vét cạn mà không giới hạn số lần thử.

Cả bốn trông như “lỗ hổng kề xác thực”, nhưng nguyên nhân không giống nhau. Những gì bị phá là các ranh giới tách biệt: tính toàn vẹn token, ủy quyền cấp đối tượng, ủy quyền cấp thuộc tính, và giới hạn tốc độ thử.

Nửa sau của câu hỏi còn thêm một chủ đề. Một lỗ hổng nghiêm trọng được công bố trên thư viện nguồn mở dùng rộng, cho phép kẻ tấn công thực thi mã từ xa bằng cách lạm dụng JNDI Lookup. Chưa có bản vá cũng chưa có quy tắc WAF hoàn chỉnh. Trong lúc đó, câu hỏi hỏi cách xác nhận ảnh hưởng, WAF nên nhìn chỗ nào, và vì sao chế độ WAF ban đầu nên là “phát hiện” chứ không phải “chặn”.

Bài viết này lấy đáp án mẫu chính thức2 và bình luận chấm điểm3 làm nền, và đi không chỉ đáp án từng câu mà cả vì sao đó là đáp án, và thực tế bạn nên thiết kế chặt đến mức nào.

Tổng quan câu hỏiCho thấy ranh giới tin cậy bị phá ở từng giai đoạn - mã xác thực, JWT, ủy quyền API, và lỗ hổng thư việnKhông giới hạn lần thửCho phép alg=noneTin midstatus=paidJNDI/LDAP/HTTPỨng dụng người dùngMã xác thực 4 chữ sốPhát hành JWTAPI người dùngGhi nhật kýThư viện dễ tổn thươngThực thi mã từ xaĐọc/cập nhật dữ liệu người khácĐổi trạng thái thanh toán

Hình 1: Tổng quan câu hỏi. Một ranh giới tin cậy khác bị phá ở mỗi giai đoạn.

1. Kết luận trước

  • Tính chất của API RESTful không giữ trạng thái phiên được gọi là không trạng thái (stateless). Điều này không có nghĩa máy chủ không giữ cơ sở dữ liệu hay trạng thái người dùng nào
  • Mã xác thực bốn chữ số có 10.000 giá trị khả dĩ. Ở 10 lần thử mỗi giây, kẻ tấn công thành công sau trung bình 5.000 lần, tức 500 giây — ngắn hơn thời hạn 10 phút, nên chỉ hết hạn không chặn được
  • Biện pháp tối thiểu chống alg=none là xác nhận alg trên header JWT không phải NONE. Trong thực tế, bạn nên cố định tập thuật toán được phép phía máy chủ
  • Dù JWT hợp lệ, mid của yêu cầu cũng không được tin. Hoặc khớp ID người dùng trong JWT với mid, hoặc an toàn hơn, đừng nhận mid từ máy khách và xác định đích từ JWT
  • Thêm status=paid là vấn đề Mass Assignment, khi thuộc tính ngoài đặc tả bị gắn thẳng lên đối tượng nội bộ. Dùng DTO cập nhật làm danh sách cho phép, và đừng để người dùng đổi trạng thái thanh toán
  • Đáp án mẫu cho biện pháp vét cạn là logic khóa tài khoản khi số lần thất bại liên tiếp vượt ngưỡng. Trong thực tế, chồng thêm trễ tăng dần và kiểm soát theo nguồn
  • Để xác nhận ảnh hưởng của lỗ hổng nghiêm trọng mới công bố, thay vì ra lệnh phá hủy, hãy ghi các truy cập tới index.html của máy chủ thử để xác nhận thực thi mã từ xa thực sự tới được
  • Chuỗi tấn công được mang trong header HTTP, nên đích kiểm tra của WAF là Header. Làm biểu thức chính quy xử lý đổi chữ hoa thường, dùng dạng \W[jJ][nN][dD][iI]\W
  • Lợi ích của việc bắt đầu WAF ở chế độ “phát hiện” là ngăn lưu lượng nghiệp vụ hợp lệ bị chặn vì dương tính giả. Khi cảnh báo tới, xem xét đó có phải tấn công thật, rồi chuyển sang chặn sau khi tinh chỉnh quy tắc
  • WAF chỉ là biện pháp tạm thời; sửa gốc là cập nhật thư viện bị ảnh hưởng lên phiên bản đã vá

2. Tình huống ánh xạ sang các câu hỏi thế nào

Tình huống đặt tại Công ty G, đang ra mắt dịch vụ chăm sóc sức khỏe mới. Người dùng nhập dữ liệu như bữa ăn và cân nặng qua ứng dụng điện thoại và nhận đánh giá rủi ro sức khỏe cùng lời khuyên dinh dưỡng. Hệ thống dựng trên đám mây, kết hợp cổng API, xử lý theo sự kiện, và cơ sở dữ liệu quản lý.

Đề thi trừu tượng hóa tên sản phẩm và dịch vụ cụ thể. Bài viết này cũng không tái hiện sơ đồ hay văn bản của IPA, mà chỉ diễn lại cấu trúc cần để hiểu các câu hỏi.

Câu hỏi Chủ đề Chương của bài này
Câu 1 Bản chất của API RESTful Chương 4
Câu 2(1) Thời gian vét cạn mã 4 chữ số Chương 5
Câu 2(2) JWT alg=none Chương 6
Câu 2(3) Truy cập người dùng khác qua mid Chương 7
Câu 2(4) Lỗi chấp nhận status ngoài đặc tả Chương 8
Câu 2(5) Biện pháp chống vét cạn Chương 9
Câu 3(1) Xác nhận lỗ hổng tồn tại một cách an toàn Chương 11
Câu 3(2)(3) WAF nhìn chỗ nào, và biểu thức chính quy Chương 12
Câu 3(4) Lợi ích của chế độ phát hiện và cách vận hành Chương 13

Bình luận chấm điểm ghi nhận tỷ lệ đúng tổng thể ở mức trung bình. Nó cũng chỉ ra, tuy nhiên, rằng tỷ lệ đúng hơi thấp với biện pháp chống giả mạo JWT ở Câu 2(2) và cơ chế cần trên máy chủ kiểm tra ở Câu 3(1). Không câu nào trả lời được chỉ từ từ vựng. Bạn phải lần giá trị nào kẻ tấn công đổi, quá trình nào nó chảy vào, và chỗ nào nó bị tin.

3. Đây không chỉ là một “vấn đề xác thực”

Trải cả câu hỏi theo ranh giới tin cậy cho kết quả sau.

[ID người dùng / mật khẩu]
          |
          v
[Kiểm tra mã 4 chữ số] ---- không giới hạn lần thử ----> vét cạn
          |
          v
[Phát hành JWT]
          |
          v
[Thư viện JWT] ------- cho phép alg=none ------> giả mạo ID người dùng
          |
          v
[API người dùng]
    |             |
    |             +-- đưa status nguyên gói ----> lỗi ủy quyền cấp thuộc tính
    |
    +-- tin mid -----------------------> lỗi ủy quyền cấp đối tượng

[Ghi đầu vào bên ngoài vào nhật ký]
          |
          v
[Thư viện dễ tổn thương] ---- JNDI/LDAP/HTTP ------> thực thi mã từ xa

Phân biệt quan trọng nhất ở đây là như sau.

Kiểm tra Câu hỏi nó đặt Ví dụ bị phá trong tình huống này
Xác thực Bạn là ai Vét cạn mã 4 chữ số
Kiểm tra token Thông tin danh tính đó đã bị giả mạo chưa alg=none
Ủy quyền cấp đối tượng Người dùng này có được truy cập dữ liệu của người dùng kia không Đổi mid
Ủy quyền cấp thuộc tính Trường này có được đổi không status=paid
Ranh giới từ đầu vào tới thực thi Đầu vào bên ngoài có bị diễn giải như lệnh không JNDI Lookup

Vượt một kiểm tra không bao giờ là lý do bỏ kiểm tra kế. Người dùng có JWT hợp lệ chưa chắc được đọc dữ liệu của người khác. Người dùng được cập nhật dữ liệu của mình chưa chắc được đổi cả trạng thái thanh toán.

Khi tách được các giai đoạn này, đáp án từng câu thôi không còn là thứ phải thuộc lòng.

Khác biệt giữa xác thực và ủy quyềnXác thực xác nhận chủ thể, ủy quyền xác nhận chủ thể đó được phép làm gìXác thựcbạn là aiỦy quyềnbạn được làm gì

Hình 2: Khác biệt giữa xác thực và ủy quyền. Xác thực đi trước; ủy quyền là kiểm tra riêng.

4. Câu 1 — “Không trạng thái” nghĩa là gì

Câu 1 hỏi về một nguyên tắc thiết kế của API RESTful: tính chất không quản lý phiên.

Đáp án là không trạng thái (stateless).

Không trạng thái nghĩa là máy chủ không cần nhớ trạng thái hội thoại của yêu cầu trước, vì mỗi yêu cầu tự mang mọi thứ cần để xử lý. Trong câu hỏi này, ứng dụng điện thoại gắn JWT vào header Authorization trên mọi yêu cầu. Máy chủ kiểm tra JWT đó và nhận diện người dùng của yêu cầu từ đó.

Một cách đọc sai phổ biến là coi “không trạng thái” là “máy chủ không giữ trạng thái nào”. Thực tế nó thường giữ các trạng thái sau.

  • Cơ sở dữ liệu lưu thông tin người dùng và dữ liệu sức khỏe
  • Trạng thái thanh toán
  • Giá trị, hạn dùng, và bộ đếm thất bại của mã xác thực
  • Khóa ký JWT
  • Thông tin thu hồi, với thiết kế dùng danh sách thu hồi
  • Nhật ký và hồ sơ kiểm toán

Điều nó không giữ là trạng thái phiên phía máy chủ tồn tại thuần để nối hội thoại, như điều kiện tiên quyết mỗi lời gọi API phụ thuộc.

Không trạng thái cũng không tự cải thiện bảo mật. Gửi JWT mọi yêu cầu giúp mở rộng ngang dễ hơn, nhưng nếu kiểm tra JWT sai, lỗi đó cũng lan đều mọi nút. Tính chất kiến trúc và tính đúng bảo mật là hai việc khác nhau.

5. Câu 2(1) — Mã bốn chữ số bị phá trong trung bình 500 giây

API xác thực gửi số bốn chữ số bằng email khi ID người dùng và mật khẩu khớp. Rồi phát hành JWT khi ID người dùng và mã bốn chữ số khớp. Mã có hiệu lực 10 phút kể từ khi tạo.

Trong đánh giá, 10 lần thử mỗi giây là khả thi. Câu hỏi hỏi trung bình mất bao nhiêu giây để xuyên qua.

Phép tính là “một nửa không gian ứng viên”

Một số bốn chữ số, kể cả số 0 đứng đầu, có 10.000 khả năng sau.

0000, 0001, 0002, ... , 9999

Nếu đáp án đúng được chọn đều ngẫu nhiên, kẻ tấn công thử ứng viên theo thứ tự không lặp sẽ tới đáp án đúng, trung bình, sau một nửa không gian ứng viên.

Số lần thử trung bình = 10,000 / 2 = 5,000
Thời gian trung bình                = 5,000 / 10 lần mỗi giây = 500 giây

Vậy ô b là 500.

Trường hợp xấu nhất mất tới 1.000 giây, nhưng câu hỏi hỏi trung bình. Và thời hạn của mã là 600 giây — dài hơn thời gian phá trung bình 500 giây. Đó là lý do nó bị đánh giá “có khả năng bị phá”.

Cảm nhận quy mô mã xác thực 4 chữ sốThử 10.000 ứng viên ở 10 lần mỗi giây trung bình 5.000 lần và 500 giây, ít hơn thời hạn 600 giâyTrung bình 10,000 / 2 = 5,000 lần500 giây ít hơn 600 giây10.000 ứng viênThời gian phá trung bình 500 giâyThời hạn 600 giâyPhá được trong thời hạn

Hình 9: Cảm nhận quy mô mã xác thực bốn chữ số. Thử, trung bình, một nửa không gian ứng viên phá được trong thời hạn.

Chỉ rút ngắn hạn dùng sẽ thua nếu không gian ứng viên nhỏ

Độ mạnh của mã xác thực không do riêng số chữ số hay riêng thời hạn quyết định.

Số lần thử khả thi trong thời hạn
= lần mỗi giây x thời hạn
= 10 x 600
= 6,000 lần

Thử các giá trị không lặp theo thứ tự, kẻ tấn công kiểm được 60% trong 10.000 khả năng trong thời hạn. Đặt hạn dùng một mình không đủ nếu số lần thử không bị giới hạn.

NIST SP 800-63B hiện hành đòi ít nhất sáu chữ số cho bí mật ngắn hạn dùng trong xác thực ngoài băng, và bắt buộc giới hạn tốc độ thử khi bí mật có ít hơn 64 bit entropy. Nó cũng kêu gọi không dùng email cho xác thực ngoài băng.4 Đáp án đề thi làm việc trong đặc tả đã cho của mã bốn chữ số gửi email, nhưng với thiết kế mới trong thực tế, chính tiền đề đó nên được xem lại.

6. Câu 2(2) — alg=none là vấn đề “để kẻ tấn công chọn phương pháp kiểm tra”

JWT trong câu hỏi này gồm ba phần: header, payload, và chữ ký.

base64url(header).base64url(payload).base64url(signature)

Header ghi RS256 là thuật toán dùng để ký. Payload chứa ID người dùng, thời điểm phát hành, và hạn dùng.

Người đánh giá đổi hai điều sau.

  1. Đổi alg trên header từ RS256 thành NONE.
  2. Đổi ID người dùng trên payload thành người dùng khác.

Gửi JWT đó, kiểm tra thành công và cho phép mạo danh người khác.

Luồng tấn công JWT alg=noneĐổi alg thành none trên JWT hợp lệ rồi viết lại ID người dùng khiến yêu cầu quaĐổi alg header thành noneBỏ qua kiểm tra chữ kýJWT hợp lệalg=RS256user=user01JWT bị giả mạoalg=noneuser=user02Máy chủ chấp nhậnlà user02

Hình 3: Luồng tấn công JWT alg=none. Kẻ tấn công chọn thuật toán kiểm tra.

none không phải lỗi chính tả

RFC 7519 định nghĩa “Unsecured JWT” — JWT không chữ ký cũng không mã hóa, với algnone.5 Vậy giá trị none không phải thứ đơn giản là không tồn tại trong đặc tả.

Vấn đề là một API vốn chỉ nên nhận JWT đã ký lại chấp nhận none mà kẻ tấn công chỉ định.

Viết theo khái niệm, quá trình dễ tổn thương trông như sau.

1. Đọc header JWT.
2. Nhìn alg viết trên header, và chọn phương pháp kiểm tra.
3. Nếu alg là none, không kiểm tra chữ ký.
4. Tin ID người dùng trong payload.

Chính sức mạnh của bảo mật đang được chọn từ đầu vào kẻ tấn công kiểm soát.

Đáp án đề thi

Câu hỏi hỏi, trong 20 ký tự trở xuống mỗi mục, dữ liệu nào thư viện Q đã sửa nên kiểm tra, và kiểm tra đó nên xác nhận gì.

Đáp án mẫu như sau.

Mục Cốt lõi đáp án
Dữ liệu cần kiểm tra Giá trị chỉ định trong alg trên header JWT
Điều cần kiểm tra Rằng nó không phải NONE

Là sửa trực tiếp cho lỗ hổng mô tả trong câu hỏi, điều này đúng.

Trong thực tế, đừng dừng ở “bất cứ gì trừ NONE”

Ở đây cần tách đáp án đề thi khỏi khuyến nghị thực tế.

RFC 8725 nêu rằng thư viện JWT nên để bên gọi chỉ định tập thuật toán được phép, và không gì ngoài tập đó được dùng.6 Nói cách khác, ý tưởng là thế này.

Cách xấu:
  Chấp nhận nếu token.header.alg != "none"

Cách tốt:
  Chỉ chấp nhận nếu nằm trong serverConfig.allowedAlgorithms
  ví dụ allowedAlgorithms = ["RS256"]

Chỉ từ chối none vẫn có thể để lại thuật toán yếu khác, hoặc khả năng nhầm thuật toán khi sơ đồ khóa công khai bị hiểu thành khóa đối xứng. Nguyên tắc là đừng cứ thêm điều kiện phủ định cho thứ sẽ nhận, mà hãy cố định một tập dương hẹp những gì được phép.

Kiểm tra JWT nên xác nhận không chỉ thuật toán mà, tùy trường hợp sử dụng, ít nhất các mục sau nữa.

Mục Điều cần xác nhận
Chữ ký Có kiểm tra được bằng khóa và thuật toán kỳ vọng không
iss Có phải nhà phát hành tin cậy không
aud Token có được phát cho API này không
exp Có còn trong thời hạn không
nbf Có đang trước thời “chưa có hiệu lực” không
sub hoặc ID người dùng Có phải chủ thể hợp lệ trong ứng dụng không
Loại token ID token có bị nhầm với access token không, v.v.

Trong câu hỏi này tên khóa payload là user, nhưng thực tế bạn nên dùng sub chuẩn hoặc định nghĩa rõ nghĩa của claim tùy chỉnh.

Kiểm tra JWT an toàn so với không an toànKiểm tra không an toàn phụ thuộc alg, kiểm tra an toàn dùng danh sách cho phép phía máy chủKiểm tra an toànThuật toán được phép cấu hình máy chủví dụ RS256Xác nhận alg trên header JWTnằm trong danh sách cho phépKiểm tra chữ ký, iss, aud, expKiểm tra không an toànĐọc alg từ header JWTChấp nhận nếu alg là none

Hình 4: Kiểm tra an toàn so với không an toàn. Trong thực tế, cố định tập thuật toán được phép hẹp.

Base64url không phải mã hóa

Còn một hiểu lầm phổ biến khác về JWT. Header và payload được biểu diễn bằng base64url, nhưng đó không phải mã hóa. Ai cũng giải mã và đọc được.

Điều chữ ký bảo đảm, và chỉ khi kiểm tra đúng, là nội dung chưa bị giả mạo kể từ khi phát hành. Nó không có nghĩa thông tin cá nhân bạn muốn giữ kín được phép đưa vào payload của JWT đã ký.

7. Câu 2(3) — Dù JWT hợp lệ, đổi mid vẫn đọc được dữ liệu người khác

Tiếp theo là cuộc tấn công không giả mạo bản thân JWT.

API người dùng nhận ID người dùng gọi là mid trên GET hoặc PUT. Mô-đun dùng chung P lấy hoặc cập nhật, trong cơ sở dữ liệu, thông tin người dùng gắn với mid đó.

Cấu trúc tấn công đơn giản.

ID người dùng trong JWT: user01    <- JWT ký đúng
mid của yêu cầu:          user02   <- kẻ tấn công đổi

Chữ ký JWT hợp lệ, nên xác thực thành công. Nhưng API tin mid=user02 như được cho, và trả thông tin của user02.

Đây là trường hợp sách giáo khoa của điều OWASP API Security Top 10 2023 gọi là Broken Object Level Authorization (BOLA). Mỗi khi dữ liệu được truy cập bằng ID đối tượng do người dùng chỉ định, ủy quyền cho đúng đối tượng đó phải được kiểm tra mỗi lần.7

Tấn công BOLADùng JWT hợp lệ trong khi đổi mid của yêu cầu sang ID người dùng khácJWT user01mid user02Tin midKẻ tấn côngAPI người dùngTrả dữ liệu user02 từ DB

Hình 5: Tấn công BOLA. Xác thực qua, nhưng ủy quyền chưa từng được kiểm tra.

Đáp án của câu hỏi

Gạch chân 2 trong Bảng 5 hỏi, trong 40 ký tự trở xuống, xử lý cần thêm vào lời gọi mô-đun dùng chung P.

Đáp án mẫu là:

Logic kiểm tra ID người dùng chứa trong JWT có khớp giá trị mid hay không

Lợi ích của việc kiểm tra trong mô-đun dùng chung P là cùng một kiểm tra ủy quyền dễ áp cho cả GET và PUT, và cho mọi API tương lai cũng dùng P. Chép cùng một so sánh vào từng màn hình hay điểm cuối nghĩa là nó sẽ thiếu ở đâu đó.

Thiết kế an toàn hơn là không nhận mid chút nào

Với API chỉ lấy hoặc cập nhật thông tin của chính người gọi, không cần nhận ID người dùng từ máy khách chút nào.

GET /users/me
Authorization: Bearer <JWT>

Phía máy chủ, chủ thể được trích từ JWT đã kiểm tra.

principal = validateJwt(request.authorization)
userId = principal.subject
return repository.getUser(userId)

Cập nhật cũng vậy.

principal = validateJwt(request.authorization)
input = validateProfileUpdate(request.body)
repository.updateProfile(
    userId = principal.subject,
    name = input.name,
    age = input.age
)

Kiểm tra so sánh bảo vệ bạn nếu bạn viết nó. Nhưng thiết kế không bao giờ nhận ID đích từ bên ngoài giảm chính sự tồn tại của lớp lỗi quên viết so sánh đó.

Nếu quản trị viên cần thao tác thông tin người dùng khác, hãy tách như sau.

PUT /users/me                  Cho người dùng thường
PUT /admin/users/{userId}      Cho quản trị viên

Với tuyến quản trị viên, đòi quyền riêng, nhật ký kiểm toán, và nếu cần, xác thực lại. Việc này làm ranh giới chính sách ủy quyền rõ hơn nhiều so với “thêm ngoại lệ cho quản trị viên trên API người dùng thường”.

Cách ngăn BOLAThay vì dùng mid của yêu cầu, quyết định hoặc kiểm tra đích từ subject của JWTGET /users/me + JWTLấy sub từ JWTKhớpKhông khớpKhông có midNgười dùngAPINếu có midcó khớp sub khôngTrả dữ liệu của mìnhTừ chối 403Tra DB theo JWT sub

Hình 6: Cách ngăn BOLA. Hoặc đừng nhận mid, hoặc kiểm nó với subject của JWT.

Phân biệt xác thực và ủy quyền trong một câu

Cả trên đề thi lẫn trong thực tế, cách nói sau giúp.

  • Xác thực: bạn là ai
  • Ủy quyền: người đó được làm gì

Kiểm tra chữ ký JWT thành công chỉ đưa bạn tới “chủ thể mà token này đại diện có thể tin được”. Việc “chủ thể đó được đọc user02” phải được xác nhận riêng.

8. Câu 2(4) — status=paid là lỗi ủy quyền cấp thuộc tính

Đặc tả API người dùng định nghĩa các tham số cập nhật sau.

mid   ID người dùng
name  Tên
age   Tuổi

Tuy nhiên, người đánh giá thêm giá trị sau, vốn không có trong đặc tả.

status=paid

Trạng thái của người dùng miễn phí rồi đổi thành người dùng trả phí.

Theo câu hỏi, dịch vụ L không kiểm tra các tham số nhận được; nó đưa tất cả thẳng tới mô-đun dùng chung P, vốn được dựng để cập nhật cơ sở dữ liệu trực tiếp.

Đáp án ô c là mô-đun dùng chung P.

Mass Assignmentstatus=paid ngoài đặc tả được thêm rồi áp nguyên gói lên đối tượng nội bộKẻ tấn công thêm status=paidGắn tự độngLưu vào DBĐặc tả APImid / name / ageThân yêu cầuMô-đun dùng chung PTrạng thái thanh toán đổi thành paid

Hình 7: Mass Assignment. Một thuộc tính ngoài đặc tả được áp nguyên gói lên đối tượng nội bộ.

Khác biệt với BOLA

Việc đổi mid ở chương trước và việc thêm status lần này trông giống nhau, nhưng độ hạt được bảo vệ khác.

Lỗ hổng Thứ kẻ tấn công đổi Thứ thực sự nên kiểm tra
Đổi mid Đối tượng đích Người dùng này có được truy cập bản ghi người dùng kia không
Thêm status Một thuộc tính trong đối tượng Người dùng này có được đổi trường này không

OWASP API Security Top 10 2023 coi cái sau là Broken Object Property Level Authorization, gấp điều từng gọi là Mass Assignment vào hạng mục này.8

“Nhét JSON thẳng vào thực thể” thì nguy hiểm

Cài đặt dễ tổn thương, theo khái niệm, trông như sau.

entity = repository.find(body.mid)
bindAllProperties(entity, body)
repository.save(entity)

Dù màn hình chỉ có ô nhập nameage, kẻ tấn công có thể tạo yêu cầu HTTP trực tiếp. Việc một trường vắng trên giao diện không phải ranh giới bảo mật.

Cài đặt an toàn làm các trường cập nhật được rõ ràng.

input = parseExactSchema(body, fields = ["name", "age"])

entity = repository.find(authenticatedUserId)
entity.name = input.name
entity.age  = input.age
repository.save(entity)

Hai điều quan trọng ở đây.

  1. Kiểu đầu vào cập nhật chỉ nên giữ các trường người dùng được phép đổi.
  2. Thay vì lặng lẽ bỏ qua các trường lạ ngoài đặc tả, hãy từ chối chúng như lỗi nếu có thể.

Lặng lẽ bỏ qua trường lạ giấu việc một cuộc tấn công thất bại, nhưng cũng khiến bạn bỏ sót lỗi cài đặt máy khách và dấu hiệu tấn công. Trừ khi có lý do tương thích, từ chối bằng lược đồ chặt giúp điều tra dễ hơn.

Ủy quyền cấp thuộc tínhDTO cập nhật chỉ giữ danh sách cho phép, và thuộc tính lạ bị từ chốiKiểm tra lược đồKhôngĐường riêngDTO cập nhật - danh sách cho phépnameageThân yêu cầuChỉ các trườngđược phép có mặtCập nhật name/age thực thểTrả lỗiDịch vụ thanh toánthông báo đã xác minhCập nhật status=paid

Hình 8: Ủy quyền cấp thuộc tính. Hạn chế trường cập nhật được bằng danh sách cho phép, và đổi trạng thái thanh toán chỉ qua đường riêng.

status chỉ nên đổi từ kết quả thanh toán

status=paid không phải phần của hồ sơ người dùng. Đó là trạng thái suy ra từ sự thật phía máy chủ: thanh toán đã thành công.

Cập nhật hồ sơ người dùng
  -> chỉ name / age được đổi

Thông báo đã xác minh từ dịch vụ thanh toán
  -> khớp paymentId
  -> ngăn xử lý trùng
  -> đổi status thành paid

Dù lưu trong cùng cột cơ sở dữ liệu, quyền đổi nó và đường dùng để đổi là hai việc khác nhau. Dùng thực thể nội bộ trực tiếp làm kiểu đầu vào của API bên ngoài xóa ranh giới đó.

9. Câu 2(5) — Biện pháp chống vét cạn giữ bộ đếm thất bại như trạng thái

Với việc vét cạn mã bốn chữ số, ô d trong Bảng 5 hỏi, trong 30 ký tự trở xuống, xử lý thuộc chỗ đó. Ngưỡng là 10.

Đáp án mẫu là:

Logic khóa tài khoản khi số lần thất bại liên tiếp vượt ngưỡng

Điều này không mâu thuẫn với tính không trạng thái từ Câu 1. Không giữ trạng thái hội thoại của lời gọi API như phiên máy chủ, và lưu bền bộ đếm thất bại cần cho quyết định bảo mật, là hai việc khác nhau.

Có và không có giới hạn tốc độ thửKhông giới hạn thì mã bị phá trong trung bình 500 giây, nhưng giới hạn bộ đếm thất bại làm chậm tấn công mạnhCó giới hạnTốc độ tấn công sụpTài khoản bị khóaKhóa sau 10 lần thất bạiTrễ tăng dầnKhông giới hạnKhoảng 500 giâyXác thực thành công10 lần thử mỗi giây

Hình 10: Có và không có giới hạn tốc độ thử. Giới hạn bộ đếm thất bại có thể chặn vét cạn trên thực tế.

Trong thực tế, đừng chỉ dựa vào khóa vĩnh viễn

Giới hạn lần thử theo tài khoản là cần, nhưng nếu kẻ tấn công biết ID người dùng của người khác, họ có thể cố tình thất bại 10 lần để khóa người dùng hợp lệ ra ngoài. Trong thực tế, vì thế hãy kết hợp các mục sau.

Kiểm soát Vai trò
Bộ đếm thất bại theo tài khoản Chặn vét cạn một tài khoản
Thời gian chờ tăng dần Chịu lỗi nhập của người dùng hợp lệ trong khi làm chậm tấn công
Kiểm soát theo IP nguồn, thiết bị, ASN, v.v. Kìm tấn công thử ít lần trên nhiều tài khoản
Quyết định theo rủi ro Áp hạn chế mạnh hơn cho vùng, thiết bị, hoặc tốc độ bất thường
Thông báo người dùng Để người dùng nhận ra tấn công hoặc lỗi của mình
Quy trình khôi phục an toàn Không để kênh mở khóa tự thành đường tấn công

Hơn nữa, khi mã được gửi lại, bộ đếm thất bại không được đưa về không — nếu không kẻ tấn công bổ sung ngân sách thử mỗi lần gọi API gửi lại. NIST SP 800-63B hiện hành cũng đòi bộ đếm thất bại không được đặt lại dù bí mật xác thực mới được tạo.4

Làm mã xác thực dùng một lần

Câu hỏi tập trung vào hạn dùng, nhưng thực tế còn cần các điều sau.

  • Vô hiệu hóa ngay mã đã thành công.
  • Từ chối dùng lại cùng một mã.
  • Không để bản thân mã trong nhật ký.
  • Làm phản hồi sao cho thành công hay thất bại của kiểm tra mã không dùng để suy ra người dùng có tồn tại không.
  • Đặt giới hạn lần thử trên API gửi mã nữa.

Miễn là bí mật ngắn đang được dùng, bảo mật không thể giao cho riêng việc sinh ngẫu nhiên.

Biện pháp cho mã xác thựcNgoài số chữ số và hạn dùng, bảo vệ bằng giới hạn lần thử, từ chối dùng lại, thông báo, và hơn thếMã xác thựcTăng số chữ sốRút ngắn hạn dùngGiới hạn tốc độ thửVô hiệu hóa sau thành côngKhông đặt lại bộ đếm thất bại khi gửi lạiKhông để mã trong nhật kýKiểm soát theo nguồn

Hình 11: Biện pháp cho mã xác thực. Kết hợp số chữ số và hạn dùng với kiểm soát lần thử và vận hành.

10. Phân biệt bốn phần của Câu 2 trên một trang

Các điểm ở Câu 2 dễ lẫn, sắp theo giá trị kẻ tấn công kiểm soát.

Tấn công Giá trị kẻ tấn công đổi Thứ không nên tin Sửa gốc
Giả mạo JWT alg trên header JWT, ID người dùng trên payload Thuật toán kiểm tra do chính token tuyên bố Cố định thuật toán được phép phía máy chủ
Đọc thông tin người dùng khác mid của yêu cầu ID đích do máy khách chỉ định Khớp với subject của JWT, hoặc xác định ID đích từ JWT
Nâng thành người dùng trả phí status ngoài đặc tả Mọi thuộc tính gắn tự động Làm thuộc tính cập nhật được thành danh sách cho phép
Phá mã 4 chữ số Ứng viên của otp Lần thử xác thực không giới hạn Thêm giới hạn tốc độ thử, trễ, và quyết định rủi ro

Quan trọng là đừng gộp tất cả thành “kiểm tra đầu vào”.

  • alg là chính sách mật mã.
  • mid là ủy quyền cấp đối tượng.
  • status là ủy quyền cấp thuộc tính.
  • otp là sức chống đoán trực tuyến.

Dù trong cùng một yêu cầu HTTP, lý do mỗi thứ phải được bảo vệ khác nhau.

11. Câu 3(1) — Xác nhận thực thi mã từ xa mà không gây thiệt hại

Sau khi dịch vụ ra mắt, lỗ hổng nghiêm trọng V được công bố trên thư viện H, một thư viện nguồn mở dùng rộng. Chuỗi sự kiện của câu hỏi như sau.

  1. Kẻ tấn công đưa chuỗi chứa JNDI Lookup vào header HTTP rồi gửi.
  2. Máy chủ đích ghi giá trị đó vào nhật ký.
  3. Thư viện dễ tổn thương đánh giá JNDI Lookup và truy vấn máy chủ LDAP của kẻ tấn công.
  4. Phản hồi LDAP trả URL của máy chủ HTTP của kẻ tấn công.
  5. Máy chủ đích lấy tệp lớp và thực thi lệnh.

Với tên sản phẩm cụ thể bị giấu, điều này đọc như một cuộc tấn công kiểu Log4Shell (CVE-2021-44228). Mô tả của chính Apache cũng mô tả lỗ hổng là nếu kẻ tấn công kiểm soát thông điệp nhật ký hoặc tham số, họ có thể thực thi mã tùy ý tải từ máy chủ LDAP.9

Luồng xác nhận lỗ hổng kiểu Log4ShellDùng callback vô hại để xác nhận chuỗi từ JNDI tới thực thi mã từ xa có thực sự đi quaTiêm payload jndi/ldapvào x-api-versionJNDI LookupPhản hồi URL HTTPGhi GETXác nhận tới đượcKẻ tấn côngMáy chủ dễ tổn thươngXử lý nhật kýMáy chủ LDAP độc hạiMáy chủ HTTP độc hạiindex.htmlMáy chủ thửnhật ký truy cậpXác nhận lỗ hổng

Hình 12: Luồng xác nhận lỗ hổng kiểu Log4Shell. Khả năng tới được được xác nhận bằng ghi truy cập HTTP chứ không ra lệnh phá hủy.

Mã kiểm tra chỉ kích hoạt một truy cập HTTP vô hại

Công ty G chạy mã kiểm tra không ảnh hưởng hệ thống, để xác nhận lỗ hổng V có thể bị khai thác từ bên ngoài. Lệnh duy nhất mã kiểm tra ra là lấy index.html của máy chủ thử.

Câu 3(1) hỏi cần triển khai gì trên máy chủ thử để xác nhận lệnh đã được thực thi.

Đáp án mẫu là:

Cơ chế ghi và cho phép xác nhận các truy cập tới index.html của máy chủ thử

Nếu nhật ký truy cập của máy chủ web ghi một GET từ máy chủ đích, điều đó xác nhận, ít nhất, rằng chuỗi sau đã đi qua.

Yêu cầu HTTP bên ngoài
  -> xử lý nhật ký
  -> JNDI Lookup
  -> phản hồi LDAP
  -> lấy lớp
  -> thực thi lệnh kiểm tra
  -> truy cập HTTP tới máy chủ thử

Vì sao “hiện chữ trên màn hình” không đủ

Đích của cuộc tấn công là máy chủ. Không có bảo đảm gì sẽ đổi trên màn hình trình duyệt của người dùng. Ngoài ra, dù lỗ hổng tồn tại, phần liên lạc ra ngoài giữa chừng có thể bị tường lửa chặn.

Ghi truy cập phía máy chủ thử tạo bằng chứng quan sát được rằng máy chủ đích thực sự tới được thế giới bên ngoài.

Khi thực hiện kiểu kiểm tra này trong thực tế, luôn tuân các điều sau.

  • Xin phép rõ ràng từ chủ sở hữu hệ thống đích.
  • Dùng phương pháp kiểm tra không ảnh hưởng sản xuất, hoặc ảnh hưởng nhỏ chấp nhận được.
  • Không dùng lệnh phá hủy như ghi, xóa, hay đổi cấu hình.
  • Tự quản lý miền và máy chủ kiểm tra.
  • Ghi thời điểm kiểm tra, nguồn, đích, và callback kỳ vọng.
  • Tháo máy chủ LDAP hoặc HTTP tạm thời và thông tin xác thực sau kiểm tra.

“Xác nhận rằng thực thi mã tùy ý là khả thi” và “thực thi mã nguy hiểm tùy ý” không phải cùng một việc. Giữ tác dụng phụ ở mức tối thiểu cần để đạt mục tiêu.

12. Câu 3(2)(3) — WAF kiểm tra header HTTP

WAF của dịch vụ N cho phép chọn GET, POST, PUT, ANY, Header, COOKIE, hoặc Multipart làm đích kiểm tra.

Mã tấn công đi vào giá trị của header HTTP gọi là x-api-version. Vậy ô e và f trong Bảng 6 đều là Header.

Ánh xạ vị trí đề cho thẳng sang đích kiểm tra của WAF

Đây ít phải kiến thức chung hơn là đọc luồng dữ liệu trong đề.

Chỗ đặt chuỗi tấn công:
  header x-api-version
          |
          v
Đích kiểm tra của WAF:
  Header

Không phải tham số GET cũng không phải thân POST. Thay vì nhìn danh sách tính năng WAF rồi chọn ANY vì “trông như tấn công”, hãy trả lời bằng vị trí đề nói kẻ tấn công đặt giá trị.

Xử lý đổi chữ hoa thường

Đề xuất đầu, theo khái niệm, là quy tắc sau.

Header  \Wjndi\W  chặn
Header  \Wldap\W  chặn

Nhưng đổi chữ hoa thường, như jNdI, lẩn mẫu chỉ khớp chữ thường.

Đáp án mẫu cho Câu 3(3) là một trong hai sau.

\W[jJ][nN][dD][iI]\W
\W(j|J)(n|N)(d|D)(i|I)\W

Trong tập đề, dấu gạch chéo ngược có thể trông như ký hiệu yên trong hình chữ địa phương Nhật, nhưng như biểu thức chính quy nó là \W. \W khớp mọi ký tự không phải chữ số và gạch dưới. Trong cú pháp JNDI Lookup, các ký tự không phải từ như ${: xuất hiện ngay trước và sau jndi, và mẫu được viết để bắt cả những cái đó.

Cùng ý tưởng có thể áp để làm phía ldap không phân biệt hoa thường.

\W[lL][dD][aA][pP]\W

Đừng coi regex này là “biện pháp Log4Shell hoàn chỉnh”

Đề thi hỏi biểu thức chính quy xử lý kỹ thuật lẩn được nêu trong đề. Tấn công thực tế có thể gồm tách chuỗi, Lookup thay thế, mã hóa, giao thức khác, và các biến thể khác khó phủ bằng riêng chữ ký.

Vì thế, vị trí thực tế như sau.

  1. Tạm chặn các mẫu tấn công hiện đã biết bằng WAF.
  2. Điều tra xem thư viện bị ảnh hưởng có thực sự có mặt không.
  3. Hạn chế LDAP, RMI, và HTTP ra ngoài không cần.
  4. Cập nhật lên phiên bản đã vá.
  5. Sau cập nhật, vẫn kiểm nhật ký và điều tra xem đã bị xâm phạm chưa.

WAF là lớp mua thời gian cho tới khi phiên bản đã vá sẵn.

WAF nằm chỗ nàoWAF là lớp giảm thiểu tạm thời, sửa gốc là cập nhật thư viện lên phiên bản đã váQuy tắc WAFphát hiện/chặnHạn chế lưu lượng ra ngoàiLỗ hổng nghiêm trọng được công bốXác nhận ảnh hưởngGiảm thiểu tạm thờiTạm dừng mẫu tấn côngKhép đường lạm dụngCập nhật lên thư viện đã váRà soát sau và phòng ngừa

Hình 13: WAF nằm chỗ nào. WAF chỉ mua thời gian tới khi bản vá tới; sửa gốc là cập nhật.

13. Câu 3(4) — Vì sao bắt đầu bằng “phát hiện”

Với quy tắc WAF đã cập nhật, Z, một Chuyên viên bảo mật đã đăng ký, khuyên đặt chế độ là “phát hiện” chứ không phải “chặn” trong một khoảng cố định sau khi lên sản xuất.

Câu hỏi hỏi, trong 25 ký tự trở xuống mỗi mục, lợi ích của chế độ phát hiện và việc nên làm để giảm thiệt hại.

Đáp án mẫu là:

Mục Cốt lõi đáp án
Lợi ích Có thể ngăn chặn do dương tính giả
Việc cần làm Xem xét đó có phải tấn công mỗi khi nhận cảnh báo

Chế độ phát hiện không phải chế độ “không làm gì”

Ở chế độ phát hiện, lưu lượng khớp quy tắc vẫn được cho qua, nhưng được ghi nhật ký và cảnh báo. Dù chuỗi jndi hoặc ldap tình cờ xuất hiện trong lời gọi API hợp lệ, nghiệp vụ không bị dừng ngay.

Đổi lại, phía vận hành cần làm các việc sau.

Nhận cảnh báo
   |
   v
Kiểm yêu cầu liên quan
   |
   +-- Lưu lượng hợp lệ -> thu hẹp quy tắc, cân nhắc ngoại lệ
   |
   +-- Tấn công             -> cô lập đích, bảo toàn nhật ký, điều tra ảnh hưởng, chuyển sang chặn

Nếu không ai xem cảnh báo, chế độ phát hiện chẳng có hiệu lực bảo vệ. Phát hiện chỉ hoạt động cặp với quy trình vận hành quan sát và phán đoán.

Đường từ phát hiện tới chặn

Một quy trình triển khai điển hình như sau.

  1. Chạy chế độ phát hiện trên lưu lượng thật.
  2. Phân loại các lần trúng thành dương tính giả hoặc dương tính thật.
  3. Tinh chỉnh header đích, đường, API, ranh giới từ, v.v.
  4. Xác nhận ảnh hưởng lên lưu lượng hợp lệ chấp nhận được.
  5. Chuyển sang chế độ chặn.
  6. Giám sát số lần chặn và ảnh hưởng nghiệp vụ.

Tuy nhiên, đây là nguyên tắc thời bình. Khi lỗ hổng nghiêm trọng, đang bị khai thác chủ động, và không có lựa chọn khác, sự cố do xâm phạm có thể bị đánh giá nặng hơn sự cố do dương tính giả, và chặn từ đầu được chọn thay. Trong tình huống đề thi, chế độ phát hiện được chọn trước để xác nhận dịch vụ vẫn vận hành như cũ.

Từ chế độ phát hiện WAF sang chế độ chặnQuan sát cảnh báo ở chế độ phát hiện, tinh chỉnh dương tính giả, rồi chuyển sang chế độ chặnLưu lượng thậtDương tính giảTấn côngChế độ phát hiệnCảnh báo được nêuTấn công haydương tính giảTinh chỉnh quy tắcChuyển sang chế độ chặnGiám sát số lần chặn và ảnh hưởng nghiệp vụ

Hình 14: Từ phát hiện tới chặn. Quan sát và tinh chỉnh trước, xác nhận ảnh hưởng chấp nhận được, rồi mới chặn.

14. WAF là biện pháp tạm thời; cập nhật là sửa gốc

Trong câu hỏi, trang chính thức của thư viện H chưa có bản vá cũng chưa có cách tạm, và ngay cả quy tắc WAF toàn diện của nhà cung cấp đám mây cũng mất tới 72 giờ. Vì thế Công ty G tự xác nhận ảnh hưởng và tạm chặn ít nhất các mẫu đã xác định.

Chuỗi này là hình cơ bản của ứng phó sự cố.

Giai đoạn Mục đích Ứng phó trong câu hỏi này
Xác nhận ảnh hưởng Phán tổ chức mình có thực sự gặp rủi ro Xác nhận khả năng khai thác từ ngoài bằng callback vô hại
Giảm thiểu tạm thời Mua thời gian tới khi có bản vá Quy tắc WAF, phát hiện/chặn, hạn chế lưu lượng ra ngoài
Sửa gốc Gỡ nguyên nhân dễ tổn thương Cập nhật lên thư viện đã vá
Rà soát sau Kiểm đã bị khai thác chưa Điều tra nhật ký WAF, ứng dụng, DNS, proxy, và khác
Phòng tái diễn Tăng tốc quyết định lần sau Kiểm kê phụ thuộc, SBOM, quy trình cập nhật, đường liên hệ

“Không biết mình có đang dùng không” là nguồn chậm lớn nhất

Trong câu hỏi, dù Công ty G hỏi Công ty F có dùng thư viện H không, câu trả lời mất thời gian vì cần phân tích cấu hình chi tiết.

Trong thực tế, nếu bạn mới bắt đầu tìm tệp JAR sau khi lỗ hổng nghiêm trọng được công bố, phản ứng bị chậm. Bạn nên có ít nhất các thứ sau trong thời bình.

  • Kiểm kê phụ thuộc trực tiếp và gián tiếp.
  • Thành phần và phiên bản thực sự nằm trong hiện vật.
  • Chúng được triển khai tới dịch vụ, container, và thiết bị nào.
  • Quy trình cập nhật thư viện phụ thuộc rồi dựng lại/phân phối lại.
  • Đường liên hệ phê duyệt thay đổi khẩn.
  • Các đích được phép của lưu lượng ra ngoài, và ảnh hưởng khi chặn chúng.
  • Nhật ký được lưu ở đâu và cách tìm.

SBOM không phải mục tiêu tự thân. Đó là mục lục để trả lời, trong thời gian ngắn, “lỗ hổng này ảnh hưởng hệ thống đang chạy nào”.

Đừng dừng điều tra khi đã cập nhật

Bạn có thể đã bị tấn công quanh thời điểm lỗ hổng được công bố. Cập nhật lên phiên bản đã vá dừng khai thác tương lai, nhưng không xóa thông tin xác thực đã bị chiếm hay cửa hậu đã được trồng.

Với lỗ hổng kiểu Log4Shell, điều tra ít nhất các góc sau.

  • Yêu cầu HTTP chứa chuỗi đáng ngờ chỉ JNDI hoặc LDAP.
  • Liên lạc từ máy chủ ứng dụng ra LDAP, RMI, hoặc HTTP bên ngoài.
  • Tiến trình con bất thường được khởi chạy.
  • Tạo JAR, lớp, tập lệnh, hoặc tệp thực thi đáng ngờ.
  • Truy cập thông tin xác thực đám mây hoặc biến môi trường.
  • Xác thực, đổi quyền, và chuyển ra ngoài quanh thời điểm cập nhật.

Quan trọng là đừng kết luận “chúng ta không bị tấn công” chỉ từ nhật ký WAF. Có đường nội bộ không bao giờ đi qua WAF, và nhật ký không được giữ trong quá khứ.

15. Cách đọc giúp lấy điểm dễ hơn trên đề thi

Câu hỏi này ít là kiểm tra kiến thức hơn là bài tập đọc khoảng trống giữa đặc tả và cài đặt.

15.1 Tách “đặc tả” khỏi “cài đặt” trong các bảng

Ở vấn đề status, một giá trị vắng trong đặc tả API lại qua ở phần cài đặt.

Đặc tả:
  mid / name / age

Cài đặt:
  gửi mọi tham số nhận được tới P

Khi thấy khoảng trống này, rõ ràng ô c là mô-đun dùng chung P.

15.2 Gạch dưới giá trị kẻ tấn công đổi

Giá trị đổi trong từng cuộc tấn công như sau.

  • alg trên header JWT
  • ID người dùng trên payload JWT
  • Tham số API mid
  • status ngoài đặc tả
  • otp của API xác thực
  • Header HTTP x-api-version

Hầu như mọi câu hỏi đang hỏi “giá trị đó nên được kiểm tra ở đâu”.

15.3 Đưa đáp án về đúng thuật ngữ của đề

Trong thực tế bạn có thể gọi những thứ này là “BOLA”, “Mass Assignment”, và “rate limiting”. Nhưng điều đề hỏi là xử lý cụ thể khớp cấu trúc đề.

Ví dụ kém:

Thực hiện ủy quyền thích hợp.

Ví dụ tốt:

Kiểm tra ID người dùng chứa trong JWT có khớp giá trị mid hay không.

Ví dụ kém:

Áp biện pháp chống vét cạn.

Ví dụ tốt:

Khóa tài khoản khi số lần thất bại liên tiếp vượt ngưỡng.

Chỉ biết tên trừu tượng không tạo ra đáp án chấm được trong giới hạn ký tự.

15.4 Với WAF, lần “nó được đặt ở đâu”

Đích kiểm tra của WAF không quyết bằng đoán từ loại tấn công, mà từ chỗ chuỗi tấn công được đặt.

Đặt vào header x-api-version
        ↓
Đích kiểm tra là Header

Ghi chú trong bình luận chấm điểm rằng tỷ lệ đúng Câu 3(1) hơi thấp cũng vì nhiều đáp án không khớp luồng tấn công ở Hình 6. Chỉ vẽ lại chuỗi tấn công bằng mũi tên đã lộ ra điều nên quan sát.

16. Danh sách kiểm cho đánh giá API thực tế

Danh sách kiểm để mang câu hỏi này về thiết kế và đánh giá mã thật.

Kiểm tra JWT

  • Thuật toán chữ ký được phép được cố định trong cấu hình máy chủ.
  • none và các thuật toán bất ngờ bị từ chối.
  • Chữ ký, iss, aud, exp, và nbf được kiểm tra tùy trường hợp sử dụng.
  • ID token, access token, và refresh token không bị nhầm lẫn nhau.
  • Có quy trình xoay khóa và thu hồi.
  • Thông tin phải giữ kín không được đưa vào payload JWT.

Ủy quyền cấp đối tượng

  • Đổi ID trong yêu cầu không tới được dữ liệu người dùng khác.
  • Ủy quyền được thi hành trên liệt kê, chi tiết, cập nhật, xóa, và tải xuống như nhau.
  • Ủy quyền được thi hành ở lớp dùng chung chạm dữ liệu, không phải trên màn hình.
  • Với API chỉ-của-mình, bạn đã cân nhắc suy ra ID đích từ token thay thế.
  • Thao tác quản trị viên dùng chính sách riêng với API người dùng thường.

Ủy quyền cấp thuộc tính

  • Kiểu đầu vào bên ngoài và thực thể cơ sở dữ liệu được giữ tách.
  • Các trường cập nhật được được liệt kê thành danh sách cho phép.
  • Thuộc tính ngoài đặc tả bị từ chối hoặc kiểm toán.
  • Trạng thái như quyền, thanh toán, phê duyệt, và sở hữu không đổi được từ đầu vào người dùng.
  • Phản hồi cũng loại thuộc tính bí mật không cần.

Lần thử xác thực

  • Có giới hạn bộ đếm thất bại theo tài khoản.
  • Có trễ tăng dần và kiểm soát theo nguồn.
  • Bộ đếm thất bại không bị đặt lại khi mã được phát lại.
  • Mã xác thực chỉ dùng được một lần.
  • Mã xác thực và mật khẩu không bị để trong nhật ký.
  • Quy trình mở khóa/khôi phục không tự là đường xác thực yếu hơn.

Lỗ hổng nghiêm trọng trên thư viện phụ thuộc

  • Dịch vụ đang chạy có thể ánh xạ sang phiên bản phụ thuộc.
  • Có quy trình xác minh ảnh hưởng bằng phương pháp vô hại.
  • Biện pháp tạm thời như quy tắc WAF và hạn chế ra ngoài có thể áp dụng.
  • Có quy trình vận hành để người chịu trách nhiệm xem cảnh báo phát hiện.
  • Có đường phát hành khẩn để cập nhật lên phiên bản đã vá.
  • Nhật ký được điều tra về khả năng khai thác trước cập nhật.

17. Hai mặt của thành phần dùng chung, nhìn qua câu hỏi này

Câu hỏi này có hai thành phần dùng chung: thư viện quản lý JWT Q và mô-đun dùng chung P.

Thành phần dùng chung có lợi lớn.

  • Sửa một chỗ lan ra mọi API dùng nó.
  • Logic ủy quyền và kiểm tra không phải nhân đôi vào từng tính năng.
  • Đích kiểm thử có thể gom lại.
  • Định dạng nhật ký và kiểm toán có thể thống nhất.

Mặt khác, sai cũng lan khắp hệ thống.

  • Nếu thư viện Q chấp nhận alg=none, mọi API dùng JWT trở nên dễ tổn thương.
  • Nếu mô-đun dùng chung P chấp nhận mid hoặc status tùy ý, cả GET và PUT trở nên dễ tổn thương.
  • Nếu thư viện H dễ tổn thương được dùng ở nền, mọi tuyến ghi header HTTP trở thành phần bề mặt tấn công.

Vì thế, thứ nên dùng chung không phải chỉ truy cập dữ liệu. Cần dùng chung chính các bất biến bảo mật, và kiểm tra nghiêm ngặt thành phần dùng chung đó một mình.

Ví dụ, làm hợp đồng của P như sau.

P.getOwnUser(authenticatedSubject)
P.updateOwnProfile(authenticatedSubject, ProfileUpdate{name, age})

An toàn hơn là không mở API mức thấp như sau trực tiếp cho bên gọi thông thường.

P.getUser(arbitraryMid)
P.updateUser(arbitraryMap)

Cái sau chỉ cần cho tập tuyến hạn chế, chẳng hạn xử lý quản trị. Đưa tự do mức thấp đó cho mọi API tạo ra thiết kế phụ thuộc việc mọi bên gọi dùng đúng, mỗi lần.

18. Tóm tắt

Câu hỏi 1 buổi chiều Xuân 2024 (Reiwa 6) là câu hỏi đọc tách từng chủ đề bảo mật API một.

Dùng JWT không có nghĩa xác thực an toàn. Để kẻ tấn công chọn thuật toán chữ ký cho phép viết lại ID người dùng.

Chữ ký JWT hợp lệ không có nghĩa ủy quyền đúng. Tin mid của yêu cầu cho phép người dùng hợp lệ truy cập thông tin của người khác.

Cập nhật được đối tượng của mình không có nghĩa mọi thuộc tính được đổi. Gắn tự động trạng thái nội bộ như status cho phép viết lại quyền hoặc trạng thái thanh toán.

Có hạn dùng trên mã xác thực không có nghĩa chống được vét cạn. Bạn cần tính không gian ứng viên và tốc độ thử, rồi giới hạn số lần thất bại.

Đưa quy tắc vào WAF không có nghĩa lỗ hổng đã được sửa. Phát hiện và chặn chỉ mua thời gian; bạn xác nhận ảnh hưởng rồi cuối cùng cập nhật thư viện.

Ánh xạ lỗ hổng sang biện phápÁnh xạ mỗi lỗ hổng tới ranh giới tin cậy và biện pháp của nóKiểm tra tokenỦy quyền cấp đối tượngỦy quyền cấp thuộc tínhKiểm soát lần thử xác thựcTừ đầu vào tới thực thiGiả mạo JWTCố định thuật toán được phépĐổi midKhớp subject JWT/không cần midstatus=paidLàm DTO cập nhật thành danh sách cho phépVét cạn mã 4 chữ sốGiới hạn thất bại/trễLỗ hổng kiểu Log4ShellCập nhật thư viện/WAF

Hình 15: Ánh xạ lỗ hổng sang biện pháp. Tách sửa theo ranh giới nào bị phá.

Một nguyên tắc xuyên suốt cả câu hỏi này.

Đừng bao giờ để thành công của kiểm tra trước thành lý do bỏ ranh giới tin cậy kế.

Các bài trước trong loạt này đề cập lỗ hổng XSS lưu trữ ở Câu 1 buổi chiều Mùa thu 2023 (Reiwa 5)đưa dữ liệu ra qua Wi-Fi khách ở Câu 2 buổi chiều Mùa thu 2023 (Reiwa 5). Để xem những gì cần kiểm trên cả website, xem thêm Dùng “Cách bảo vệ website” của IPA làm danh sách kiểm.

Tóm tắt cuốiCho thấy thành công của kiểm tra trước không bao giờ là lý do bỏ ranh giới tin cậy kếXác thực thành côngKiểm tra chữ ký JWTỦy quyền cấp đối tượngỦy quyền cấp thuộc tínhGiới hạn tốc độ thửRanh giới từ đầu vào tới thực thiWAF/cập nhật thư viện

Hình 16: Tóm tắt cuối. Ranh giới tin cậy được kiểm tra theo giai đoạn, và không giai đoạn nào được bỏ.

Liên kết tham khảo

  1. IPA, Spring 2024 (Reiwa 6) Registered Information Security Specialist Examination, PM Question Booklet. Văn bản đề mà bài viết này dựa trên. 

  2. IPA, Spring 2024 (Reiwa 6) Registered Information Security Specialist Examination, PM Model Answers. Đáp án mẫu chính thức cho từng câu. 

  3. IPA, Spring 2024 (Reiwa 6) Registered Information Security Specialist Examination, PM Grading Commentary. Giải thích tỷ lệ đúng và các lỗi thường gặp. 

  4. NIST, SP 800-63B: Authentication and Authenticator Management. Nêu số chữ số cho bí mật ngắn hạn, giới hạn tốc độ thử, bộ đếm thất bại khi phát lại, và không dùng email cho xác thực ngoài băng, cùng các yêu cầu khác.  2

  5. RFC Editor, RFC 7519: JSON Web Token (JWT). Đặc tả JWT, gồm Unsecured JWT và alg=none

  6. RFC Editor, RFC 8725: JSON Web Token Best Current Practices. BCP nêu việc cố định tập thuật toán được phép, và kiểm tra nhà phát hành, chủ thể, và audience, cùng các thực hành khác. 

  7. OWASP, API1:2023 Broken Object Level Authorization. Giải thích nhu cầu kiểm tra ủy quyền cho mọi ID đối tượng người dùng chỉ định. 

  8. OWASP, API3:2023 Broken Object Property Level Authorization. Giải thích lỗi ủy quyền cấp thuộc tính, gồm Mass Assignment, và các biện pháp. 

  9. Apache Logging Services, Security. Giải thích ảnh hưởng của CVE-2021-44228, thực thi mã qua JNDI và LDAP, và các phiên bản đã sửa. 

Các bài viết gần đây có cùng thẻ để tìm hiểu sâu hơn những chủ đề lân cận.

Các trang này đặt chủ đề trong bối cảnh rộng hơn của dịch vụ và quyết định.

Bài viết liên quan trực tiếp đến các dịch vụ sau.

Phát triển website

Vì trong API thành viên và tích hợp điện thoại, kiểm tra JWT, ủy quyền cấp đối tượng, và giới hạn thuộc tính nào được cập nhật ánh xạ trực tiếp lên bảo mật của chính hệ thống web.

Câu hỏi thường gặp

Các câu hỏi thường gặp khi tư vấn về chủ đề của bài viết.

Vì sao kẻ tấn công vẫn mạo danh người khác dù kiểm tra chữ ký JWT đã thành công?
Trong câu hỏi này, thư viện quản lý JWT chấp nhận alg trên header JWT đúng như kẻ tấn công chỉ định, và coi JWT alg=none là hợp lệ dù không có chữ ký. Vì thế viết lại ID người dùng trong payload vẫn vượt kiểm tra. Đáp án đề thi là kiểm tra alg trên header JWT và xác nhận giá trị đó không phải NONE. Trong thực tế, chỉ từ chối NONE thì chưa đủ. Hãy cố định các thuật toán được phép dùng — ví dụ RS256 — trong cấu hình phía máy chủ, để thuật toán mà token tự tuyên bố không bao giờ được dùng trực tiếp để chọn. Cũng nên kiểm tra issuer, audience, hạn dùng, subject, v.v. tùy trường hợp sử dụng.
So ID người dùng trong JWT với mid của yêu cầu đã đủ làm biện pháp ủy quyền chưa?
Với đáp án của đề thi này thì đủ. Kiểm tra, trong mô-đun dùng chung P, rằng ID người dùng chứa trong JWT khớp mid sẽ chặn cuộc tấn công chỉ định mid của người khác. Tuy nhiên, với API chỉ xử lý thông tin của chính người gọi, thực tế an toàn hơn là không nhận mid từ máy khách chút nào, và xác định ID người dùng từ subject của JWT đã kiểm tra. Dùng GET /users/me hoặc PUT /users/me khiến logic so sánh khó bị thiếu. API mà quản trị viên thao tác trên người dùng khác nên tách thành điểm cuối riêng với chính sách ủy quyền riêng.
Vì sao chỉ kiểm tra đầu vào thông thường không chặn được cuộc tấn công thêm status?
Vì dù bạn kiểm tra độ dài của name hay khoảng của age, điều đó chẳng giúp gì nếu status — một trường vốn không được nhận — bị gắn tự động rồi đưa thẳng vào đối tượng nội bộ. Vấn đề không phải định dạng giá trị; đó là ủy quyền cấp thuộc tính, tức người dùng có được phép đổi thuộc tính đó hay không. Chỉ định nghĩa name và age trong kiểu đầu vào cập nhật, và từ chối thuộc tính lạ. Trạng thái thanh toán chỉ được đổi từ sự kiện máy chủ tin cậy, chẳng hạn kết quả thành công từ dịch vụ thanh toán.
Mã xác thực bốn chữ số hết hạn sau 10 phút — vì sao vẫn nguy hiểm?
Vì chỉ có 10.000 ứng viên từ 0000 đến 9999, và ở 10 lần thử mỗi giây kẻ tấn công thành công sau trung bình 5.000 lần — 500 giây. Thời hạn 10 phút là 600 giây, nên thử các ứng viên không lặp theo thứ tự cho phép kiểm 6.000 ứng viên trong cửa sổ đó. Chỉ thời hạn không chặn được vét cạn. Không gian ứng viên, tốc độ thử, và trần số lần thử phải được thiết kế cùng nhau.
Biện pháp của đề thi là khóa tài khoản — khóa ngay một mình trong thực tế đã đủ chưa?
Chưa. Ô trống trong câu hỏi đòi logic khóa tài khoản khi số lần thất bại liên tiếp vượt ngưỡng, nhưng khóa cố định, vĩnh viễn một mình cho phép kẻ tấn công cố tình khóa tài khoản người khác thành từ chối dịch vụ. Trong thực tế, kết hợp bộ đếm thất bại theo tài khoản với thời gian chờ tăng dần, đánh giá rủi ro nguồn và thiết bị, thông báo, và quy trình khôi phục. Việc phát hành mã mới không được đưa bộ đếm thất bại về không cũng quan trọng.
Điểm của việc đặt WAF ở phát hiện thay vì chặn là gì?
Nghĩa là lưu lượng nghiệp vụ hợp lệ không bị dừng dù một chuỗi bình thường bị phán nhầm là tấn công. Trong đáp án mẫu, lợi ích là có thể ngăn chặn do dương tính giả, và việc cần làm là xem xét đó có phải tấn công mỗi khi nhận cảnh báo. Chế độ phát hiện không phải thiết lập để bỏ đó. Nó dùng làm giai đoạn quan sát để kiểm nhật ký, lọc dương tính giả, tinh chỉnh quy tắc, rồi chuyển sang chặn. Trong tình huống khẩn khi lỗ hổng nghiêm trọng đã biết đang bị khai thác thật, có thể hợp lý khi cân rủi ro sẵn sàng phục vụ và chọn chặn ngay từ đầu.
Thư viện H trong câu hỏi này có phải Log4j không?
Câu hỏi giấu tên sản phẩm, nhưng chuỗi tấn công — JNDI Lookup, máy chủ LDAP, lấy lớp từ máy chủ HTTP, chuỗi nhúng trong header HTTP, và điểm cơ sở CVSS v3.1 cao — đọc tự nhiên như sự trừu tượng hóa của CVE-2021-44228, còn gọi là Log4Shell. Bài viết này giải thích sự tương ứng đó, nhưng đề thi không đòi bạn nêu tên sản phẩm cụ thể. Có thể trả lời thuần từ quy trình tấn công đã cho và đặc tả WAF.
Nên mang gì từ câu hỏi này về thực tế?
Rằng xác thực thành công, JWT chưa bị giả mạo, được phép truy cập đối tượng đích, và được phép đổi thuộc tính đích là những kiểm tra tách biệt. Ngoài ra, mã xác thực ngắn cần giới hạn tốc độ thử, và với lỗ hổng thư viện nghiêm trọng bạn chạy xác nhận ảnh hưởng, phòng thủ tạm thời, và sửa gốc song song. Những điều cốt lõi mang về thực tế là: gom ủy quyền vào thành phần dùng chung, biến lược đồ đầu vào thành danh sách cho phép, cố định điều kiện kiểm tra JWT phía máy chủ, và nắm thư viện phụ thuộc để cập nhật được.

Hồ sơ tác giả

Trang giới thiệu tác giả bài viết.

Go Komura

Đại diện của KomuraSoft LLC

Chuyên về phát triển phần mềm Windows, tư vấn kỹ thuật và điều tra lỗi, đặc biệt trong các dự án có hệ thống hiện hữu và lỗi khó tái hiện.

Liên kết công khai

Quay lại blog