Tạo file .htpasswd cho Apache và Nginx với bốn thuật toán băm mật khẩu
Công cụ quản lý nhiều cặp tên đăng nhập và mật khẩu trong một bảng, băm bằng bcrypt, MD5-crypt kiểu Apache, SHA-1 hoặc crypt truyền thống, rồi xuất ra nội dung file .htpasswd hoàn chỉnh. Kèm khối cấu hình mẫu cho cả Apache và Nginx, cùng hướng dẫn đặt file ngoài thư mục web và phân quyền cho đúng.
Tính năng nổi bật
- Bảng quản lý nhiều cặp tên đăng nhập và mật khẩu, thêm hoặc xóa dòng tùy ý và băm tất cả trong một lần bấm
- Bốn thuật toán: bcrypt, MD5-crypt kiểu Apache dạng $apr1$, SHA-1 dạng {SHA} và crypt truyền thống dựa trên DES
- Mỗi thuật toán có nhãn đánh giá còn an toàn hay chỉ để tương thích hệ cũ, kèm ghi chú Apache và Nginx đọc được đến đâu
- Thanh trượt cost factor từ 4 đến 14 khi chọn bcrypt, hiện luôn số vòng lặp tương ứng
- Salt sinh bằng crypto.getRandomValues, mỗi người dùng một salt riêng nên hai người cùng mật khẩu vẫn ra hai hash khác nhau
- Xuất nội dung file .htpasswd đúng định dạng tên đăng nhập, dấu hai chấm rồi chuỗi hash, chép một nút là xong
- Khối cấu hình mẫu cho Apache với AuthType Basic và cho Nginx với auth_basic, sửa được đường dẫn file và dòng chữ hiện trong hộp đăng nhập
- Cảnh báo tự động khi tên đăng nhập trùng nhau, chứa dấu hai chấm, chứa khoảng trắng hoặc khi mật khẩu vượt giới hạn của thuật toán đang chọn
Vì sao nên tạo .htpasswd ở đây thay vì chạy lệnh trên máy chủ
Lệnh htpasswd đi kèm Apache là cách chuẩn để tạo file này, nhưng nó không phải lúc nào cũng có sẵn. Máy chủ chỉ cài Nginx thường không có gói apache2-utils, và bạn phải cài thêm một gói của Apache chỉ để chạy đúng một lệnh. Máy Windows thì không có sẵn lệnh này. Nhiều dịch vụ lưu trữ chia sẻ cũng không cho truy cập dòng lệnh. Thêm nữa, khi tạo tài khoản cho vài người cùng lúc, chạy lệnh từng lần rồi ghép tay các dòng lại rất dễ sót hoặc nhầm. Công cụ này gom cả việc chọn thuật toán, sinh salt, băm hàng loạt và ghép file vào một chỗ, đồng thời nói rõ thuật toán nào Nginx đọc được và thuật toán nào chỉ Apache hiểu, thứ mà lệnh htpasswd không nhắc bạn. Điểm cần nhấn mạnh là mật khẩu được băm ngay trên trình duyệt của bạn, không có yêu cầu mạng nào rời khỏi máy. Dù vậy vẫn nên dùng mật khẩu mới tạo cho mục đích này chứ đừng đưa mật khẩu đang dùng thật vào bất kỳ trang web nào.
Lợi ích khi sử dụng
- Không cần cài apache2-utils lên máy chủ chỉ để chạy một lệnh
- Tạo hàng loạt tài khoản trong một lần thay vì gõ lệnh từng người rồi ghép tay
- Biết ngay thuật toán vừa chọn có chạy được trên Nginx hay không trước khi lên máy chủ mới phát hiện
- Có sẵn khối cấu hình cho cả hai loại máy chủ nên không phải nhớ cú pháp
- Được nhắc đặt file ngoài thư mục web và phân quyền đúng, hai lỗi khiến bảo vệ trở nên vô nghĩa
Cách tạo file .htpasswd và gắn vào máy chủ
- 1Chọn thuật toán băm. Nếu máy chủ là Apache 2.4 trở lên thì chọn bcrypt, nếu là Nginx trên hệ điều hành cũ thì chọn MD5-crypt kiểu Apache cho chắc chắn.
- 2Với bcrypt, kéo thanh trượt cost factor. Mức 10 đến 12 hợp với xác thực cơ bản vì trình duyệt gửi lại thông tin đăng nhập ở mỗi yêu cầu.
- 3Điền tên đăng nhập và mật khẩu vào bảng, bấm Thêm người dùng nếu cần nhiều dòng, rồi bấm Tạo hash cho tất cả.
- 4Đọc phần cảnh báo nếu có, chẳng hạn tên trùng nhau hoặc mật khẩu vượt giới hạn của thuật toán đang chọn.
- 5Chép nội dung file .htpasswd rồi lưu lên máy chủ ở một thư mục nằm ngoài thư mục web, ví dụ /etc/nginx/htpasswd/.
- 6Sửa đường dẫn file và dòng chữ hiện trong hộp đăng nhập, chép khối cấu hình tương ứng vào file cấu hình của Apache hoặc Nginx, kiểm tra cú pháp rồi nạp lại máy chủ.
Bốn thuật toán và thuật toán nào còn dùng được
Bcrypt là lựa chọn duy nhất trong bốn thuật toán ở đây còn thực sự chống được máy dò mật khẩu hiện nay. Nó có salt ngẫu nhiên 16 byte và cost factor cho bạn tự chọn mức chậm, nghĩa là có thể nâng theo đà tăng tốc của phần cứng. Apache 2.4 đọc được bcrypt, tương ứng lệnh htpasswd với tùy chọn viết hoa B. MD5-crypt kiểu Apache, nhận ra qua tiền tố đô la apr1 đô la, có salt 8 ký tự và một nghìn vòng lặp MD5. Một nghìn vòng là quá ít so với phần cứng ngày nay nên nó yếu, nhưng đây là định dạng chắc chắn chạy được trên cả Apache lẫn Nginx vì Nginx tự cài sẵn thuật toán này bên trong. SHA-1 dạng ngoặc nhọn SHA chỉ băm một lần, không salt, không lặp, nên card đồ họa phổ thông thử được hàng tỷ mật khẩu mỗi giây và hai người cùng mật khẩu sẽ có hash giống hệt nhau, nhìn vào file là biết. Chỉ dùng nó khi phải tương thích một hệ thống cũ đọc đúng định dạng RFC 2307. Crypt truyền thống dựa trên DES tệ nhất: nó chỉ lấy 8 ký tự đầu của mật khẩu và salt vỏn vẹn 12 bit, nên mật khẩu bạn đặt dài bao nhiêu cũng chỉ có 8 ký tự đầu là thực sự có tác dụng.
Apache và Nginx đọc được những định dạng nào
Đây là chỗ hay khiến người ta mất thời gian nhất, vì hai máy chủ không hỗ trợ giống nhau. Apache 2.4 đọc được cả bốn định dạng trên nền Unix, và bcrypt là khuyến nghị chính thức. Nginx thì khác: nó tự cài sẵn hai định dạng là MD5-crypt kiểu Apache và SHA-1 dạng ngoặc nhọn SHA, nên hai cái này chắc chắn chạy ở bất kỳ đâu Nginx chạy được. Riêng crypt truyền thống và bcrypt thì Nginx gọi ra hàm crypt của hệ điều hành, tức có chạy được hay không phụ thuộc vào thư viện hệ thống. Các bản Linux mới dùng libxcrypt hỗ trợ bcrypt nên không sao, nhưng hệ điều hành cũ hoặc bản dựng tối giản có thể không nhận, và biểu hiện là đăng nhập luôn bị từ chối dù mật khẩu đúng, không có thông báo lỗi nào rõ ràng trong nhật ký. Cách kiểm tra an toàn là tạo trước một tài khoản thử, nạp cấu hình rồi thử đăng nhập bằng trình duyệt ẩn danh trước khi giao cho người dùng thật. Nếu bcrypt không chạy, hãy chuyển sang MD5-crypt kiểu Apache, đó vẫn là lựa chọn tốt nhất trong số các định dạng chắc chắn tương thích.
Đặt file ở đâu và phân quyền thế nào
Sai lầm nghiêm trọng nhất và cũng phổ biến nhất là đặt file .htpasswd ngay trong thư mục web. Nếu file nằm ở dưới thư mục gốc của site, bất kỳ ai cũng có thể tải nó về chỉ bằng cách gõ đường dẫn vào trình duyệt, và thế là họ có toàn bộ danh sách hash để mang về dò ngoại tuyến thoải mái, không bị giới hạn tốc độ nào. Với Apache, tệ hơn nữa là quy ước đặt tên bắt đầu bằng dấu chấm chỉ giấu file khỏi lệnh liệt kê thư mục chứ không chặn máy chủ phục vụ nó. Hãy đặt file ở một thư mục hoàn toàn nằm ngoài thư mục web, ví dụ /etc/nginx/htpasswd/ hoặc /etc/apache2/auth/, hoặc ít nhất là một cấp trên thư mục gốc của site. Về quyền, nguyên tắc là chỉ tiến trình máy chủ web đọc được và không ai khác. Trên Nginx thường đặt chủ sở hữu là root và nhóm là www-data rồi cho quyền 640, tức chủ đọc ghi, nhóm chỉ đọc, người khác không có quyền gì. Trên Apache thay www-data bằng người dùng mà máy chủ đang chạy. Sau khi đặt xong, hãy thử tải trực tiếp đường dẫn file qua trình duyệt để chắc chắn máy chủ trả về lỗi không tìm thấy chứ không phải nội dung file.
Xác thực cơ bản làm được gì và không làm được gì
Cơ chế đứng sau file .htpasswd gọi là xác thực cơ bản, một phần của giao thức web từ rất sớm. Cách hoạt động đơn giản: máy chủ trả về mã lỗi yêu cầu xác thực, trình duyệt hiện hộp thoại đăng nhập, rồi từ đó về sau trình duyệt gửi kèm tên đăng nhập và mật khẩu trong tiêu đề của mọi yêu cầu. Điểm cốt tử là chúng chỉ được mã hóa base64, tức không phải mã hóa gì cả mà chỉ là một cách viết lại đọc ngược được trong một giây. Vì vậy quy tắc bắt buộc là chỉ dùng khi site đã chạy hoàn toàn qua HTTPS, nếu không thì bất kỳ ai đứng giữa đường truyền cũng đọc được mật khẩu ở dạng gần như văn bản thuần. Điểm hạn chế thứ hai là không có cơ chế đăng xuất: trình duyệt giữ thông tin đăng nhập cho tới khi đóng hết cửa sổ, nên bạn không thể cho người dùng thoát ra bằng một nút bấm. Thứ ba là giao diện hộp đăng nhập do trình duyệt vẽ, không tùy biến được. Vì vậy cơ chế này hợp với trang quản trị nội bộ, môi trường thử nghiệm, thư mục tài liệu hay lớp bảo vệ phụ đặt trước một trang đăng nhập khác, chứ không thay được hệ thống tài khoản của một ứng dụng thật.
Định dạng file và những lỗi khiến máy chủ không đọc được
File .htpasswd chỉ là văn bản thuần, mỗi dòng một tài khoản theo dạng tên đăng nhập, dấu hai chấm, rồi chuỗi hash, không có khoảng trắng nào quanh dấu hai chấm. Từ cấu trúc tối giản đó sinh ra vài lỗi hay gặp. Tên đăng nhập không được chứa dấu hai chấm vì máy chủ cắt dòng ở dấu hai chấm đầu tiên, và cũng không nên chứa khoảng trắng vì nhiều bản dựng sẽ đọc sai. Nếu có hai dòng trùng tên đăng nhập, máy chủ chỉ dùng dòng đầu tiên và bỏ qua dòng sau, nên khi đổi mật khẩu cho ai đó mà bạn nối thêm dòng mới vào cuối file thì mật khẩu cũ vẫn tiếp tục có hiệu lực, một lỗi rất khó nhận ra. Hãy sửa đúng dòng cũ thay vì thêm dòng mới. Ký tự xuống dòng phải theo kiểu Unix, nghĩa là nếu bạn soạn file trên Windows bằng trình soạn thảo mặc định rồi tải lên, ký tự xuống dòng kiểu Windows có thể dính vào cuối chuỗi hash và làm việc so khớp luôn thất bại. File cũng nên kết thúc bằng một dấu xuống dòng. Cuối cùng, đừng để lọt dòng trống ở giữa file và đừng ghi chú thích, định dạng này không hỗ trợ dòng chú thích.
Câu hỏi thường gặp (FAQ)
Nên chọn thuật toán nào cho Nginx?
Nếu chắc chắn máy chủ chạy Linux đời mới dùng libxcrypt thì bcrypt là tốt nhất. Nếu không chắc, hãy chọn MD5-crypt kiểu Apache dạng $apr1$ vì Nginx tự cài sẵn thuật toán này bên trong nên chắc chắn chạy được ở mọi nơi. Cách an toàn là tạo một tài khoản thử bằng bcrypt, nạp cấu hình rồi thử đăng nhập, nếu bị từ chối thì chuyển sang $apr1$.
Mật khẩu tôi nhập có bị gửi đi đâu không?
Không. Toàn bộ việc băm chạy bằng JavaScript ngay trong trình duyệt, trang này không gọi máy chủ nào và không ghi gì vào localStorage. Tải lại trang là bảng trống trở lại. Dù vậy vẫn nên tạo mật khẩu mới cho mục đích này thay vì gõ mật khẩu bạn đang dùng thật vào bất kỳ công cụ web nào, kể cả trang này.
Đặt file .htpasswd ở đâu trên máy chủ?
Ở một thư mục nằm hoàn toàn ngoài thư mục web, ví dụ /etc/nginx/htpasswd/ hoặc /etc/apache2/auth/. Nếu đặt trong thư mục web thì ai cũng tải về được toàn bộ danh sách hash chỉ bằng một địa chỉ trong trình duyệt. Dấu chấm ở đầu tên file chỉ giấu nó khỏi lệnh liệt kê thư mục chứ không chặn máy chủ phục vụ file đó.
File nên đặt quyền bao nhiêu?
Thường là 640 với chủ sở hữu root và nhóm là người dùng chạy máy chủ web, trên Nginx thường là www-data. Nghĩa là chủ đọc ghi, nhóm chỉ đọc, người khác không có quyền gì. Sau khi đặt xong hãy thử gõ thẳng đường dẫn file vào trình duyệt để chắc chắn máy chủ trả về lỗi không tìm thấy chứ không phải nội dung file.
Vì sao đổi mật khẩu rồi mà mật khẩu cũ vẫn đăng nhập được?
Gần như chắc chắn là bạn nối thêm một dòng mới vào cuối file thay vì sửa dòng cũ. Khi có hai dòng cùng tên đăng nhập, máy chủ chỉ dùng dòng đầu tiên và bỏ qua dòng phía sau. Hãy mở file ra xóa dòng cũ đi rồi mới thêm dòng mới, hoặc sửa trực tiếp phần hash trên dòng cũ.
Xác thực cơ bản có an toàn không?
Chỉ an toàn khi site chạy hoàn toàn qua HTTPS. Trình duyệt gửi tên đăng nhập và mật khẩu ở mỗi yêu cầu, chỉ mã hóa base64, tức đọc ngược lại được ngay. Trên kết nối không mã hóa thì bất kỳ ai trên đường truyền cũng lấy được mật khẩu. Ngoài ra cơ chế này không có nút đăng xuất và không tùy biến được giao diện hộp đăng nhập.
Vì sao crypt chỉ nhận 8 ký tự đầu của mật khẩu?
Vì thuật toán này dựng trên DES với khóa 56 bit, lấy từ 7 bit của mỗi ký tự trong 8 ký tự đầu. Đó là giới hạn nằm ngay trong thiết kế từ những năm bảy mươi, không phải lỗi cài đặt. Nghĩa là bạn đặt mật khẩu dài hai mươi ký tự thì người khác vẫn đăng nhập được chỉ bằng tám ký tự đầu. Đừng dùng thuật toán này trừ khi bắt buộc.
Cost factor của bcrypt nên đặt bao nhiêu cho .htpasswd?
Mức 10 đến 12 là hợp lý. Cần lưu ý xác thực cơ bản khác với trang đăng nhập thông thường: trình duyệt gửi lại thông tin đăng nhập ở mỗi yêu cầu, kể cả yêu cầu lấy ảnh và tệp tĩnh, nên máy chủ phải băm liên tục. Cost quá cao sẽ làm CPU tăng vọt khi có nhiều người truy cập cùng lúc.
Nginx có đọc file .htaccess không?
Không. Nginx hoàn toàn không có cơ chế file cấu hình theo thư mục như .htaccess của Apache. Mọi khai báo auth_basic và auth_basic_user_file phải nằm trong file cấu hình của site, trong khối server hoặc khối location. Sau khi sửa, chạy nginx -t để kiểm tra cú pháp rồi nginx -s reload để nạp lại.
Vì sao hai người đặt cùng mật khẩu lại ra hai chuỗi hash khác nhau?
Vì bcrypt, MD5-crypt và crypt đều sinh salt ngẫu nhiên riêng cho từng dòng. Đó là điều cần thiết để kẻ lấy được file không nhìn ra ai đang dùng chung mật khẩu và không thể dùng bảng tra tính sẵn. Riêng SHA-1 dạng {SHA} thì không có salt nên hai người cùng mật khẩu sẽ ra hash giống hệt nhau, đây là một lý do nữa để tránh nó.
Tôi soạn file trên Windows rồi tải lên mà đăng nhập không được?
Rất có thể do ký tự xuống dòng kiểu Windows dính vào cuối chuỗi hash làm việc so khớp thất bại. Hãy dùng trình soạn thảo cho phép chọn kiểu xuống dòng Unix, hoặc tải file lên rồi chạy lệnh chuyển đổi trên máy chủ. Ngoài ra kiểm tra xem có khoảng trắng thừa quanh dấu hai chấm hay dòng trống ở giữa file không.
Có giới hạn số tài khoản trong một file không?
Về kỹ thuật thì không, nhưng cả Apache và Nginx đều đọc tuần tự từ đầu file cho tới khi tìm thấy tên đăng nhập khớp, và việc đó lặp lại ở mỗi yêu cầu. Với vài chục tài khoản thì không đáng kể. Nếu bạn cần hàng trăm hay hàng nghìn tài khoản thì đây không còn là công cụ phù hợp, nên chuyển sang hệ thống xác thực có cơ sở dữ liệu thật.
Từ khóa liên quan
- htpasswd generator
- tạo file htpasswd online
- htpasswd cho nginx
- auth_basic nginx cấu hình
- AuthType Basic apache
- apr1 md5 apache là gì
- đặt mật khẩu cho thư mục web
- bảo vệ trang quản trị bằng mật khẩu
- htpasswd bcrypt
- định dạng file htpasswd
- đặt file htpasswd ở đâu
- phân quyền file htpasswd
- xác thực cơ bản http là gì
- nginx không đọc htaccess
- tạo nhiều tài khoản htpasswd
- đổi mật khẩu htpasswd
- sha1 rfc 2307 htpasswd
- crypt des chỉ 8 ký tự
