1.SSH概念
ssh(secure shell),安全外殼協議,由IETF的網絡小組所制定。ssh為建立在應用層基礎上的安全協議。SSH是目前較可靠,專為遠程登錄會話和其他網絡服務提供安全性的協議。利用SSH協議可以有效防止遠程管理過程中的信息泄露問題。SSH最初是UNIX系統上的一個程序,后來迅速擴展到其他操作平台。SSH在正確使用時可以彌補網絡中的漏洞。
1.1 ssh功能
傳統的網絡服務程序,如ftp、pop和telnet在本質上都是不安全的,因為它們在網絡上使用明文傳送口令和數據。而且,這些服務程序的安全驗證方式也是有其弱點的,極易受到中間人攻擊。通過使用SSH,可以把所有傳輸的數據進行加密,這樣中間人攻擊方式就不可能實現了,而且也能防止DNS欺騙和IP欺騙。使用SSH,還有一個額外的好處是傳輸的數據是經過壓縮的,所以可以加快傳輸的速度。SSH有很多功能,它既可以代替TELNET,又可以為ftp、pop甚至為ppp提供一個安全的通道。
1.2 ssh驗證
從客戶端來看,ssh提供兩種級別的安全驗證。
第一種級別(基於口令的安全驗證)
只要你知道自己賬號和口令,就可以登錄到遠程主機。所有傳輸的數據都會被加密,但是不能保證你正在連接的服務器是你想連接的服務器。可能會有別的服務器在冒充真正的服務器,也就是受到“中間人攻擊”。
第二種級別(基於密鑰的安全驗證)
需要依靠密鑰,也就是你必須為自己創建一對密鑰,並把公用密鑰放在需要訪問的服務器上。如果你想要連接到SSH服務器上,客戶端軟件就會向服務器發出請求,請求用你的密鑰進行安全驗證。服務器收到請求之后,先到該服務器上你的主目錄下尋找你的公用密鑰,然后把它和你發送過來的公用密鑰進行比較。如果兩個密鑰一致,服務器就用公鑰密鑰加密“質詢(challenge)”並把它發送給客戶端軟件。客戶端軟件收到“質詢”之后就可以用你的私人密鑰解密再把它發送給服務器。
用這種方式,你必須知道自己密鑰口令,但是,與第一種級別相比,第二種級別不需要再網絡上傳送口令。
第二種級別不僅加密所有傳輸的數據,而且中間人攻擊不能實現,因為他沒有你的私人密鑰。但是整個登陸過程可能需要10秒。
1.3 ssh結構
ssh由客戶端和服務端軟件組成,有兩個不兼容的版本分別是:1.x和2.x。用ssh2.x的客戶程序不能連接到ssh1.x的服務程序上去。openssh2.x同時支持ssh1.x和2.x。
服務端是一個守護進程,它在后台運行並響應來自客戶端的連接請求。服務端一般是sshd進程,提供了對遠程連接的處理,一般包括公共密鑰認證、密鑰交換、對稱密鑰加密和非安全連接。
客戶端包含ssh程序以及像scp(遠程拷貝)、slogin(遠程登錄)、sftp(安全文件傳輸)等其他的應用程序。
他們的工作機制大致是本地的客戶端發送一個連接請求到遠程的服務端,服務端檢查申請的包和IP地址再發送密鑰給SSH的客戶端,本地再將密鑰發回給服務端,自此連接建立。SSH1.x和SSH2.x在連接協議上有一些差異。
一旦建立一個安全傳輸層連接,客戶機就發送一個服務請求。當用戶認證完成之后,會發送第二個服務請求。這樣就允許新定義的協議可以與上述協議共存。
啟動SSH服務器后,sshd運行起來並在默認的22端口進行監聽,當請求到來的時候SSH守護進程會產生一個子進程,該子進程進行這次的連接處理。
1.4 擴展
SSH協議框架中設計了大量可擴展的溶於能力,比如用戶自定義算法、客戶自定義密鑰規則、高層擴展功能性應用協議。這些擴展大多遵循IANA的有關規定,特別是在重要的部分,像命名規則和消息編碼方面。
SSH采用面向連接的TCP協議傳輸,應用22端口,安全系數較高。
2. libssh2
libssh2是一個用C庫實現了SSH2協議的客戶端。
2.1 libssh2特性
libssh2提供用於開發基於SSH的應用的API。一些特性如下:
- 密鑰交換方式:diffie-hellman-group1-sha1, diffie-hellman-group14-sha1, diffie-hellman-group-exchange-sha1, diffie-hellman-group-exchange-sha256;
- 主機密鑰類型:ssh-rsa,ssh-dss;
- 加密器:aes256-ctr, aes192-ctr, aes128-ctr, aes256-cbc (rijndael-cbc@lysator.liu.se), aes192-cbc, aes128-cbc, 3des-cbc, blowfish-cbc, cast128-cbc, arcfour, arcfour128, none;
- 壓縮方式:zlib, zlib@openssh.com, none;
- MAC 哈希:hmac-sha2-256, hmac-sha2-512, hmac-sha1, hmac-sha1-96, hmac-md5, hmac-md5-96, hmac-ripemd160 (hmac-ripemd160@openssh.com), none;
- 認證方式:none, password, public-key, hostbased, keyboard-interactive;
- 通道:shell, exec (incl. SCP wrapper), direct-tcpip, subsystem;
- 全局請求:tcpid-forward;
- 通道請求:x11, pty, exit-signal, keepalive@openssh.com;
- 子系統:sftp(version 3), publickey(version 2);
- SFTP:statvfs@openssh.com, fstatvfs@openssh.com;
- 線程安全:只是不要同時共享句柄;
- 非阻塞:可以使用阻塞和非阻塞兩種;
- sockets:app在socket上調用,調用select()等;
- 加密后端:OpenSSL、libgcrypt,mbedTLS
2.2 libssh2版本
libssh2 1.8.0
改變:
- 添加了一個基礎的dockerised測試套件;
- crypto:添加了mbedTLS的后端支持。
bug修復:
- libgcrypt:修復了OOM上的一個NULL指針解除引用;
- VMS:不能將%zd用於off_t格式;
- VMS:更新了 vms/libssh2_config.h;
- windows:與crypt32.lib鏈接;
- libssh2_channel_open:在通道錯誤消息中修復了speeling錯誤;
- msvc:修復了14個編譯警告;
- tests:HAVE_NETINET_IN_H未正確定義;
- openssl:添加OpenSSL 1.1.0兼容性;
- cmake:添加CLEAR_MEMORY選項,類似於autoconf的選項;
- configure:使用 --with-*選項覆蓋OpenSSL的默認值;
- libssh2_wait_socket:設置err_msg錯誤;
- libssh2_wait_socket:修復用毫秒與api_timeout的比較。
2.3 libssh2應用例子
工作思路是為session以一種工作模式(加密、壓縮和MAC層)啟動一個libssh2例程,然后在經過身份認證后接着進行文件傳輸或其他服務,在完成服務后,關閉session和socket,最后退出libssh2。
2.3.1 在sftp的應用
unsigned long hostaddr; int sock, i, auth_pw = 0; struct sockaddr_in sin; const char *fingerprint; char *userauthlist; LIBSSH2_SESSION * session; int rc; LIBSSH2_SFTP *sftp_session; LIBSSH2_HANDLE *sftp_handle;
步驟:
(1)創建socket並建立連接;
(2)創建一個會話實例;
/* Create a session instance */ session = libssh2_session_init(); if(!session) return -1;
(3)如果設置非阻塞模式,通知libssh2我們正在阻塞
libssh2_session_set_blocking(session, 1);
(4)啟動它。操作包括:交易歡迎橫幅、交換密鑰並設置加密、壓縮和MAC層。
rc = libssh2_session_handshake(session, sock); if(rc) { fprintf(stderr, "Failure establishing SSH session: %d\n", rc); return -1; }
(5)此時尚未通過身份驗證。要做的第一件事是檢查主機密鑰對我們已知的主機的指紋(您的應用程序可能已經硬編碼,可能會轉到文件,可能會將其呈現給用戶,這是你的通話)。
fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
(6)檢查可用的認證方法。
userauthlist = libssh2_userauth_list(session, username, strlen(username)); if(strstr(userauthlist, "password") != NULL) { auth_pw |= 1; } if(strstr(userauthlist, "keyboard-interactive") != NULL) { auth_pw |= 2; } if(strstr(userauthlist, "publickey") != NULL) { auth_pw |= 4; }
(7)根據用戶參數-i、-p、-k選擇認證方式
if (auth_pw & 1) { /* We could authenticate via password */ if (libssh2_userauth_password(session, username, password)) { fprintf(stderr, "Authentication by password failed.\n"); goto shutdown; } } else if (auth_pw & 2) { /* Or via keyboard-interactive */ if (libssh2_userauth_keyboard_interactive(session, username, &kbd_callback) ) { fprintf(stderr, "\tAuthentication by keyboard-interactive failed!\n"); goto shutdown; } else { fprintf(stderr, "\tAuthentication by keyboard-interactive succeeded.\n"); } } else if (auth_pw & 4) { /* Or by public key */ if (libssh2_userauth_publickey_fromfile(session, username, keyfile1, keyfile2, password)) { fprintf(stderr, "\tAuthentication by public key failed!\n"); goto shutdown; } else { fprintf(stderr, "\tAuthentication by public key succeeded.\n"); } } else { fprintf(stderr, "No supported authentication methods found!\n"); goto shutdown; }
(8)通過認證后 ,重啟一個會話
sftp_session = libssh2_sftp_init(session);
if (!sftp_session) { fprintf(stderr, "Unable to init SFTP session\n"); goto shutdown; }
(9)通過SFTP請求一個文件
sftp_handle = libssh2_sftp_open(sftp_session, sftppath, LIBSSH2_FXF_READ, 0);
...
do
{
char mem[1024];
rc = libssh2_sftp_read(sftp_handle, mem, sizeof(mem));
if(rc >0)
{
write(1, mem, rc);
}
else
{
break;
}
}while(1);
(10)關閉sftp句柄和sftp會話
libssh2_sftp_close(sftp_handle);
libssh2_sftp_shutdown(sftp_session);
(11)shutdown:斷開會話連接並釋放會話
libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing"); libssh2_session_free(session);
(12)關閉sock並退出libssh2
close(sock);
libssh2_exit();