Security cho web dev: OWASP Top 10 và các lỗi bảo mật phổ biến trong app React

3 tháng trước · 8 phút đọc
Tại sao dev React cần quan tâm đến OWASP?
Nhiều bạn nghĩ bảo mật là việc của backend. Frontend chỉ render UI thôi, có gì nguy hiểm đâu?
Thực tế khác hẳn. Hơn 60% trong các cuộc tấn công web liên quan đến lỗi ở tầng frontend - chủ yếu là XSS, CSRF, và lộ thông tin nhạy cảm qua client-side code. React không tự động miễn nhiễm với những lỗi này.
OWASP Top 10 là danh sách 10 rủi ro bảo mật phổ biến nhất cho ứng dụng web, được cập nhật định kỳ bởi Open Web Application Security Project (OWASP Top 10 chính thức). Đây là tài liệu tham khảo chuẩn trong ngành - nhiều công ty dùng nó làm checklist khi audit bảo mật.
OWASP Top 10 - tài liệu chuẩn mà mọi web dev nên biết
Bài này mình sẽ đi thẳng vào những lỗ hổng nào hay xuất hiện nhất trong app React, code ví dụ cụ thể, và cách fix. Không lý thuyết suông.
XSS (Cross-Site Scripting) - kẻ thù quen mặt nhất
XSS xảy ra khi attacker chèn được script độc hại vào trang web của bạn, và script đó chạy trong trình duyệt của người dùng khác. React có cơ chế tự escape HTML, nhưng có 2-3 trường hợp bạn dễ bỏ qua.
Lỗi phổ biến nhất: dangerouslySetInnerHTML
React đặt tên dangerouslySetInnerHTML không phải để dọa bạn - mà để nhắc bạn nhớ sanitize trước khi dùng.
Luôn sanitize trước khi dùng dangerouslySetInnerHTML
DOM-based XSS qua URL params
Đây là trường hợp tinh vi hơn:
Thư viện hữu ích: DOMPurify để sanitize HTML, validator.js để validate và escape string an toàn.
CSRF (Cross-Site Request Forgery) - lỗi bị underestimate
CSRF xảy ra khi một trang web độc hại trick người dùng thực hiện action trên trang web khác mà họ đang đăng nhập. Nghe có vẻ phức tạp, nhưng ví dụ sau sẽ rõ hơn.
Người dùng đang đăng nhập vào bank.com. Họ click vào link từ email lạ, trang đó có:
Nếu bank.com chỉ dùng cookie để xác thực và không có CSRF token, request trên sẽ được thực hiện với session cookie hợp lệ của người dùng.
Cách phòng:
Phía backend phải implement CSRF token. Phía React, bạn cần đảm bảo gửi token này trong mỗi request mutation:
CSRF hoạt động nhờ cookie được gửi tự động - CSRF token phá vỡ vòng lặp này
SameSite Cookie - defense in depth
Nếu bạn kiểm soát backend, set SameSite=Strict hoặc SameSite=Lax cho cookie authentication. Điều này ngăn browser gửi cookie khi request xuất phát từ domain khác, giảm đáng kể surface attack của CSRF.
Injection và quản lý secrets - đừng để lộ trên client
Injection (A03 trong OWASP Top 10) thường được nghĩ là SQL Injection ở backend. Nhưng trong React app, có một dạng injection khác hay bị bỏ qua: lộ API key và credentials trong source code frontend.
Mình thấy pattern này khá nhiều:
Bất kỳ ai mở DevTools đều đọc được key này. Bundle JavaScript không phải nơi lưu secret.
Public key ở frontend được, secret key phải ở backend
Quy tắc đơn giản: Nếu key bắt đầu bằng sk_, secret_, hay tương tự - nó không nên có mặt trong React code. Bao giờ hết.
Ngoài ra, check .gitignore của bạn ngay bây giờ. File .env.local có trong đó chưa? Hơn 39 triệu thông tin mật bị lộ trên GitHub trong năm 2024 (WhiteHat.vn dẫn thống kê của GitHub) vụ lộ thông tin nhạy cảm xảy ra do dev commit file .env lên GitHub công khai.
Security headers và cấu hình đúng cho React app
HTTP Security Headers là lớp phòng thủ dễ implement nhưng nhiều app bỏ qua. Chúng không thay thế code secure, nhưng giảm thiệt hại đáng kể khi có lỗ hổng xảy ra.
Content-Security-Policy (CSP) - quan trọng nhất:
CSP cho phép bạn khai báo nguồn tài nguyên nào được phép load. Nếu XSS xảy ra, CSP có thể chặn script độc hại chạy vì nó không thuộc nguồn được whitelist.
Các headers quan trọng khác:
Security headers là lớp phòng thủ ngoài cùng - rẻ mà hiệu quả
Bạn muốn kiểm tra headers của site mình? Dùng securityheaders.com - paste URL vào là ra report ngay, kèm gợi ý fix.
Một lưu ý với React SPA: Nếu bạn dùng Create React App hay Vite và deploy lên Vercel/Netlify, cấu hình headers thông qua file vercel.json hoặc netlify.toml - không cần Nginx riêng.
Authentication, token storage và broken access control
A01 và A07 trong OWASP 2021 - mất kiểm soát truy cập và lỗi xác thực - chiếm top đầu không phải ngẫu nhiên. Trong React app, hai vấn đề này thường xuất hiện ở chỗ lưu token và kiểm tra quyền.
localStorage vs httpOnly cookie - trade-off thực tế:
Nhiều tutorial dạy lưu JWT vào localStorage. Tiện, nhưng có rủi ro XSS đọc được token.
Tuy nhiên, httpOnly cookie đòi hỏi backend hỗ trợ và phức tạp hơn cho CORS setup. Nếu buộc phải dùng localStorage, ít nhất phải đảm bảo không có XSS vulnerability nào.
localStorage tiện nhưng dễ bị đọc - httpOnly cookie an toàn hơn về mặt XSS
Client-side authorization - lỗi ngây thơ:
Client-side check chỉ là UX - không phải security. Backend phải là nguồn sự thật duy nhất cho authorization.
Nếu bạn đang build app React với authentication từ đầu, khóa Xây Dựng Website với ReactJS cover các pattern cơ bản về state management và routing - base tốt trước khi implement auth flow phức tạp hơn.
