1. 程式人生 > >使用crypt配置Basic Auth登入認證

使用crypt配置Basic Auth登入認證

簡介

Basic Auth用於服務端簡單的登入認證,通常使用伺服器Nginx、Apache本身即可完成。比如我們要限定某個域名或者頁面必須輸入使用者名稱、密碼才能登入,但又不想使用後端開發語言,此時Basic Auth就派上用場了。

Basic Auth 使用htpasswd工具進行生成 http 基本認證的密碼檔案。

流程

首先說一下Basic Auth使用流程。

建立認證檔案

新建一個檔案auth_basic_user_file,例如:

# 建立目錄
sudo mkdir -p  /usr/local/nginx/

# 生成檔案
sudo touch /work/yphp/nginx/nginx-htpasswd

檔名就是nginx-htpasswd

生成密碼

使用htpasswd工具生成密碼檔案:

# 安裝htpasswd
sudo apt-get install apache2-utils

# 生成密碼
sudo htpasswd -c -d /work/yphp/nginx/nginx-htpasswd yujc
New password: 
Re-type new password: 
Adding password for user yujc

# 檢視檔案內容
cat //work/yphp/nginx/nginx-htpasswd
yujc:sBoB9G5lTLvPk

這裡解釋說明一下: htpasswd 是開源 http 伺服器 apache httpd 的一個命令工具,所以本機如果沒有該命令,需要先安裝。

htpasswd 命令最後一個引數是使用者名稱,也就是需要登入的使用者名稱。命令執行後,會要求輸入該使用者登入時的密碼,這裡我輸入了123。最終我們發現會往/work/yphp/nginx/nginx-htpasswd檔案添加了一行內容。

其中冒號前面的就是使用者名稱,後面是加密的密碼。

如果沒有htpasswd工具怎麼辦呢?也可以藉助線上的工具生成:http://tool.oschina.net/htpasswd 。加密方式選擇crypt。點選生成後,把生成結果追加到nginx-htpasswd檔案裡即可:

cat /work/yphp/nginx/nginx-htpasswd
yujc:sBoB9G5lTLvPk
yujc2:RF8ulInobr21M

大家會發現,相同的密碼,每次生成的結果都不一樣,沒關係,只要密碼沒變,最終都能登入的。原因在後面再說明。

Nginx配置Basic Auth

server {
    listen       80;   
    server_name  test.com;

    auth_basic   "登入認證";  
    auth_basic_user_file /work/yphp/nginx/nginx-htpasswd;

    root   /mnt/html/www;
    index  index.html index.php;
}

重啟Nginx服務後,訪問test.com 就會要求輸入使用者名稱、密碼。

備註:一定要注意auth_basic_user_file路徑,如果檔案不存在,會不厭其煩的出現403。

如果只想某一個頁面支援Basic Auth,可以將auth_basic配置到location裡:

location /test {
    auth_basic   "登入認證";  
    auth_basic_user_file /work/yphp/nginx/nginx-htpasswd;
}

htpasswd加密方式

  • MD5:使用MD5加密密碼。在Windows, Netware 和TPF上,這是預設的加密方式。

  • crypt:使用crypt()加密密碼。在除了Windows, Netware和TPF的平臺上,這是預設的。 雖然它在所有平臺上可以為htpasswd所支援, 但是在Windows, Netware和TPF上不能為httpd伺服器所支援。

  • SHA:使用SHA加密密碼。 它是為了方便轉入或移植到使用LDAP Directory Interchange Format (ldif)的Netscape而設計的。

  • plain:不加密,使用純文字的密碼。雖然在所有平臺上 htpasswd 都可以建立這樣的密碼, 但是httpd後臺只在Windows, Netware和TPF上支援純文字的密碼。

通常我們使用crypt加密方式。如果你使用PHP語言,內建的crypt()函式即可實現加密。

crypt

crypt() 返回一個基於標準 UNIX DES 演算法或系統上其他可用的替代演算法的雜湊字串。

這裡以PHP的crypt為例子說明。該函式原型:

string crypt ( string $str [, string $salt ] )

salt 引數是可選的。然而,如果沒有salt的話,crypt()創建出來的會是弱密碼。 php 5.6及之後的版本會在沒有它的情況下丟擲一個 E_NOTICE 級別的錯誤。為了更好的安全性,請確保指定一個足夠強度的鹽值。

我們使用該函式生成密碼的hash值,使用不同的salt值:

php > echo crypt("123", "123456");
12IbR.gJ8wcpc
php > echo crypt("123", "abcde");
abLEFxdWWYR3c

然後複製到/work/yphp/nginx/nginx-htpasswd

#yujc:sBoB9G5lTLvPk
yujc2:12IbR.gJ8wcpc
yujc3:abLEFxdWWYR3c

輸入123均能登入成功。

注意:測試新使用者需要將已登入使用者註釋掉,無需重啟nginx。

為什麼驗證的時候我們並沒有告訴nginx的salt是多少,但是還能驗證?為什麼同一密碼不同salt產生的hash都能驗證?

我們看下面的例子:

$ php -a
Interactive mode enabled

php > echo crypt("123", "12IbR.gJ8wcpc");
12IbR.gJ8wcpc

php > echo crypt("123", "abLEFxdWWYR3c");
abLEFxdWWYR3c

php > echo crypt("123", "12test");
12IbR.gJ8wcpc

大家應該發現了什麼。我們把加密後的hash當做salt再次使用crypt函式,生成的hash竟然與傳入的salt相同。然後我們把salt前2位保持不變,後面的改成其他的,再使用crypt函式,生成的hash沒有變化。這說明crypt函式只與salt的前幾位有關係:只要前幾位不變,生成的hash是一樣的。

我們既沒有指定使用的演算法,也沒有指定鹽值,crypt是怎麼知道使用什麼演算法和鹽值的呢?其實crypt是根據$salt引數來判斷使用哪種雜湊演算法。也就是說,$salt本身就包含了演算法的型別以及雜湊時實際用到鹽值。

實際使用的演算法判斷有下面這幾種情況:

  • 標準DES(Standard DES) 在沒有匹配到其他演算法的情況下,則使用標準DES演算法,此時取前兩個字元為鹽值(不足兩個字元則返回*0)。鹽值的字元必須是./0-9A-Za-z裡的字元。
php > echo crypt("123", "t123");
t1ZzgDe4z3qWE
php > echo crypt("123", "123");
12IbR.gJ8wcpc
php > echo crypt("123", "t");
*0
  • 擴充套件DES(Extended DES) 以下劃線_開頭,後面緊接著4位元組的迭代次數和4位元組的鹽值。也就是取前9位,後面是什麼值無所謂。
php > echo crypt("123", "_12345678");
_12345678VaI36zUn7Jk
php > echo crypt("123", "_12345678t");
_12345678VaI36zUn7Jk
php > echo crypt("123", "_testtest");
_testtest4v7fH1Er0Ng
php > echo crypt("123", "_testtest1");
_testtest4v7fH1Er0Ng
  • MD5 以$1$開頭,然後是12個字元以內的鹽值。只要前12位相同,生成的hash相同。
php > echo crypt("123", '$1$');
$1$$GmbL3iXOMZR57QuGDLv.L1
php > echo crypt("123", '$1$2');
$1$2$WOzAAwhejT62wplMg6rEE1
php > echo crypt("123", '$1$23');
$1$23$0ZjnChzzaj90xZQJQKHFS1
php > echo crypt("123", '$1$123456789');
$1$12345678$tRy4cXc3kmcfRZVj4iFXr/
php > echo crypt("123", '$1$1234567890');
$1$12345678$tRy4cXc3kmcfRZVj4iFXr/

注意:PHP裡雙引號裡面的字串如果包含$會被認為是變數。

  • lowfish 以$2a$$2x$或者$2y$開頭,然後是用於cost引數的兩位數字,緊接著一個$字元,最後是22位./0-9A-Za-z範圍裡的字元。
php > echo crypt("123", '$2a$07$usesomesillystringforsalt$');
$2a$07$usesomesillystringforeN7/2NBfGxbAuv02IPrTFBImFJd5PJ1m
php > echo crypt("123", '$2a$07$usesomesillystringforsalt$1');
$2a$07$usesomesillystringforeN7/2NBfGxbAuv02IPrTFBImFJd5PJ1m

使用這個演算法的時候,$str最長支援72個字元,超過會被截掉。

  • SHA256 以$5$開頭,然後是16個字元的鹽值,鹽值之前還可以使用rounds=$的格式表明雜湊的迴圈次數(N)。
php > echo crypt("123", '$5$rounds=5000$usesomesillystringforsalt$');
$5$rounds=5000$usesomesillystri$BYJncGl82VuZ6T61c4wSpXT.xoDSuz9aF4JyE9F08U4

rounds的預設值為5000,範圍是1000到999,999,999,如果N不在這個範圍裡,會被擷取到最接近的範圍裡。

  • SHA512 以$6$開頭,然後是16個字元的鹽值,鹽值之前還可以使用rounds=$的格式表明雜湊的迴圈次數(N)。
php > echo crypt("123", '$6$rounds=5000$usesomesillystringforsalt$');
$6$rounds=5000$usesomesillystri$YPNvueKNHmPrzbloaqIomo1gPrVo8aLnqwrKlhlfThu2wzo73efrh/FCR4CAUf/GFe7gF6vuLWTMyFNb7jfnT1

rounds的預設值為5000,範圍是1000到999,999,999,如果N不在這個範圍裡,會被擷取到最接近的範圍裡。

PHP裡我們使用crypt函式,salt直接傳第一個引數的base64_encode即可:

$hashed_password = crypt ( 'mypassword', base64_encode('mypassword') ); 

參考