- vừa được xem lúc

Authentication có dễ vậy không?

0 0 25

Người đăng: Sữa

Theo Viblo Asia

Đây là một bài tự truyện về cách mình đã phát triển phương pháp authentication như nào và cũng chỉ dùng phương pháp cơ bản nhất: password.

Đơn giản nhất, so sánh mật khẩu

Bắt đầu từ lớp 10, lúc này mình implement authentication lần đầu tiên với 1 project cá nhân để học dev web - một trang web xem Doraemon, mình thích xem lắm, đến lúc mình làm xong cái ver đầu tiên của web thì mình cũng xem hết 900 tập lẻ của 3F luôn. Với cách này thì đơn giản lắm, chắc ai cũng từng nghĩ ra rồi:

if ($stored_user->password == $posted_password) { // generate authorization
} else { // show error
}

Cách này thì hay dở thế nào? Cái hay là nó dễ, trẻ con cũng nghĩ ra được nhưng có 2 thứ không ổn lắm với mình. 1 là nó không có hash mật khẩu trong database, 2 là nó gửi trực tiếp mật khẩu qua internet. Ngày đó làm chơi chơi làm gì có tiền mua SSL, dùng domain miễn phí mà thử thôi, password chềnh ềnh trên HTTP request.

Ờ thì trước tiên cứ fix cái hash đã rồi tiến tới cái tiếp theo:

if ($stored_user->password == hash("sha512", $posted_password)) { // generate authorization
} else { // show error
}

Ẩn cái mật khẩu đi, làm khùng làm điên cho hacker nó sợ

Cách này thì giống cách trên thôi, nhưng mà bạn che password đi, kiểu encode base64 hay asym encrypt (giống kiểu chơi 2 cái SSL cùng lúc ấy nhỉ, cách này facebook dùng này).

Không biết nữa, tạm gọi là unknown challenge nhé

Sang lớp 11, mình có nhận được từ thầy mình 1 yêu cầu làm web chấm bài cho đội tuyển (sau này cũng không được dùng, giờ mọi người cũng chuyển sang codeforces rồi). Lúc này thì cũng có tí kinh nghiệm, vấn đề bắt đầu phát sinh. Chấm bài cho đội tuyển thì thường là sẽ dùng trong mạng LAN, mạng LAN thì lại tương đối dễ để mọi người nghe lén nhau, ai đấy fake giống SSID wifi rồi bridge sang LAN thì có thể bắt hết HTTP request rồi. Vậy nhưng mà có vẻ như cũng không ai có ý định dùng thêm SSL xong đã thế còn có hôm ông cụ nhà mình trong bữa cơm khoe nay Viettel nó kêu hack được SSL đấy, vậy thôi, nâng cấp thì cũng chả hại ai, coi như bài tập về nhà 🤷‍♂️. Bài toán bây giờ là làm sao để không gửi mật khẩu qua đường truyền mạng.

Giải pháp là không gửi mật khẩu nữa, mà server gửi một câu hỏi và client trả lời câu hỏi đấy bằng mật khẩu, câu trả lời sẽ ở dạng không thể hiểu được (cơ bản là dùng hash).

Code nè (lúc đó nhảy sang dùng node rồi):

function getQuestion(req, username) { let user; if ((user = findUserByName(username)) === null) { // throw not found } req.session.userId = user.id; req.session.question = crypto.randomBytes(32).toString('hex'); return req.session.question;
} function login(req, answer) { let user; if ((user = findUserById(req.session.userId)) === null) { // throw not found } // cái hàm generate answer này cũng sẽ chạy ở client // password truyền vào đều phải hash qua sha256 const acceptableAnswer = generateAnswer(req.session.question, user.password); if (answer === acceptableAnswer) { delete req.session.question; delete req.session.userId; // generate and return authorization } else { // throw error }
}

Đó và thế là mở devtools/Network ra xem và chỉ thấy 1 đống chữ linh tinh chả hiểu kiểu gì. Cách này sau đấy 1-2 năm thì mình có vô tình cũng thấy Garena áp dụng.

Ơ thế giờ web hiện đại phải phân tán ai chơi session nữa

Ờ thì đấy, đến năm 2 đại học, mình với mấy thằng bạn học môn lập trình web, mình cũng lại bê cái phương pháp bên trên vào. Nhưng mà giờ mình mới để ý, chạy npm start và express báo đừng có dùng session nha bồ không là mem leak đấy. Mình nhớ là cũng có nhiều recommendation khuyên không dùng session, đặc biệt là mình cũng nhắm thiết kế hệ thống stateless để dễ phân tán, tự nhiên dính ông authentication này thì sao nhỉ. Thì bỏ session thôi, tuy nhiên vẫn phải có gì đấy để đảm bảo request lại lần 2 sẽ không có tác dụng và cơ bản thì nó là bất khả thi 😃 (vì cái sự stateless của HTTP). Tuy nhiên trong đa số các tình huống bị tấn công MITM thì hacker sẽ log 1 đống thông tin ra rồi vào đọc sau nên chỉ cần đảm bảo cái request đăng nhập chỉ có thể dùng ngay lập tức mới có hiệu lực là được. Rồi là nàm thế lào, là thêm timestamp.

Thay vì lưu question, user id trong session, giờ chỉ có 1 request thôi, trong request sẽ có 1 cái cục thông tin được sign bằng hash của password (à nếu như hash mọi người dùng bcrypt chẳng hạn hoặc dùng thêm mắm muối thì có thể thêm 1 request trước đó để lấy phần thông tin đấy) kiểu như jwt ấy mà dùng jwt luôn cũng chả sao, trong payload sẽ chứa username và timestamp client thích điền gì thì điền miễn nó trong khoảng 10s trước khi server xử lý. Như vậy khi hacker nhìn thấy cái payload này và muốn chôm nó về thì cũng chả biết sao mà sửa được timestamp, quăng cái cũ lên thì nó hết hạn lâu rồi.

Code nào:

function login(receivedToken) { const { username, timestamp } = await decode(receivedToken); if (!username || !timestamp) // throw bad request if (Date.now() - bySeconds(10) > timestamp) // throw bad request const user = findUserByName(username); if (!user) // throw not found const { password } = user; const valid = await verify(receivedToken, password); if (!valid) // throw bad request // generate authorization and return
}

Tổng kết

Đây cơ bản chỉ là kể chuyện thôi, mọi người tham khảo nhé, có cách nào hay hơn thì có thể comment cho mình biết, mình vẫn đau đầu vì không nghĩ ra cách nào hoàn thiện hoàn toàn được cái vụ không dùng session (ờ thì có thể lưu session bên ngoài như dùng Redis hay MySQL cơ mà mình muốn ít lệ thuộc các công nghệ khác hơn và đặc biệt là tiết kiệm hơn, có bị DOS thì vẫn sống tốt).

Bình luận

Bài viết tương tự

- vừa được xem lúc

OAuth là gì? Cách thức hoạt động của OAuth

. OAuth cho phép các trang web và dịch vụ chia sẻ tài nguyên giữa những người dùng. Nó được sử dụng rộng rãi, nhưng hãy lưu ý về các lỗ hổng của nó.

0 0 53

- vừa được xem lúc

Login qua social network trong project Laravel

Lời nói đầu. .

0 0 44

- vừa được xem lúc

Từ OAuth đến OpenID Connect

Khi sử dụng các ứng dụng như Draw.io hay một số ứng dụng, trang web nào đó, bạn đã bao giờ gặp thông báo yêu cầu cấp quyền truy cập đến Google drive chưa.

0 0 104

- vừa được xem lúc

Các phương thức phổ biến dùng authentication

1.Basic Authentication. Định nghĩa. HTTP Basic authentication yêu cầu client cung cấp username và password mỗi lần gọi request.

0 0 33

- vừa được xem lúc

Cách triển khai RefreshToken và FreshToken P1: Đừng lưu tất cả phiên của người dùng

Bài viết có có nội dung cấm, chỉ đọc nếu bạn muốn logout jwt . . . Dùng JWT đi thời nào rồi còn dùng session nữa ba.

0 0 52

- vừa được xem lúc

Cùng mình tạo Boilerplate cho dự án NextJS v12 - Phần 2: i18n và NextAuth.js

Xin chào mọi người, ở phần trước chúng ta đã cùng nhau cấu hình dự án với axios-hooks để tăng tính linh hoạt và thuận tiện trong lúc phát triển cũng như bảo trì và cũng cài đặt MUI với @emotion/cache

0 0 22