保持交易與客戶資料隱密性
OpenSSL heartbleed漏洞源碼分析
最近IT界最火的事情莫過於openssl的heartbleed漏洞,關於該漏洞帶來的可怕後果本文就不再贅述了。作為一名程序員,如果我們僅僅只是看看或是抱著無所謂的態度去了解一下這個漏洞,那麼我們實在太枉為程序員了,所以我們來仔細瞧瞧:
發現一個漏洞,大家第一時間想到的可能就是如何快速的修復這個漏洞,但當我們了解這個漏洞是怎麼回事的時候,一切都變得不是那麼可怕,正如openssl的heartbleed漏洞。
通過本文您可以了解最近十分火爆的openssl的heartbleed漏洞到底是怎麼回事,從根本上去了解這個漏洞,當您了解了這個漏洞的本質之後,您就會選擇更淡然的方式去修復而不至於慌亂。或者說您已經修復了該漏洞,但是很好奇這到底是一個什麼樣的漏洞,為什麼漏洞的名字會取得這麼嚴重,“心臟出血”這是一個多麼可怕的情形?
另外當你了解這個漏洞後,你就會更加的理智去面對網上的各種流言,你就會更加的有自己的判斷力,如此簡單的一個漏洞,為什麼一直都沒曝光?為什麼現在才曝光?我們的私密信息到底洩露了多少?
網上可能針對該漏洞有很多很多非常詳盡的分析,但為什麼還要寫本文?本文旨在用最簡潔易懂的語言,描述清楚該漏洞是什麼?看完前面的示例,再看後面具體的源碼的時候,就會比較易懂。
技術博客不在於技術有多麼新和多麼深奧,關鍵在於如何以簡單易懂的方式讓更多的人了解這是怎麼回事,正如openssl的heartbleed漏洞一樣,雖然網上已經有了很多分析的非常不錯的文章,而我更願以一種簡單的方式去讓更多的人了解這個漏洞的本質。
heartbleed漏洞介紹
什麼是heartbleed漏洞?大家從不同的渠道可能都已經有所了解了,但是本文還是需要再簡要的描述一下,以幫助大家更好的理解這個漏洞。
當使用基於openssl通信的雙方建立安全連接後,客戶端需要不斷的發送心跳信息到服務器,以確保服務器是可用的。
基本的流程是:客戶端發送一段固定長度的字符串到服務器,服務器接收後,返回該固定長度的字符串。比如客戶端發送"hello,world"字符串到服務器,服務器接受後,原樣返回"hello,world"字符串,這樣客戶端就會認為openssl服務器是可用的。
我們假設客戶端發送的心跳信息結構體定義為:
struct hb {
int type;
int length;
unsigned char *data;
};
其中type為心跳的類型,length為data的大小,其中關於data字段的內容結構為:
type字段佔一個字節,payload字段佔兩個字節,其餘的為payload的具體內容,詳情如下所示:
字節序號 備註
0 type
1-2 data中具體的內容的大小為payload
3-len 具體的內容pl
當服務器收到消息後,會對該消息進行解析,也就是對data中的字符串進行解析,通過解析第0位得到type,第1-2位得到payload,接著申請(1+2+payload)大小的內存,然後再將相應的數據拷貝到該新申請的內存中。
以下舉個簡單的示例來說明該問題,假如客戶端發送的data數據為"006abcdef",那麼服務器端解析可以得到type=0, payload=06, pl='abcdef',申請(1+2+6 =9)大小的內存,然後再將type, payload, pl寫到新申請的內存中。
如果大家都是老實人,那麼上述流程不會出現任何問題。可是世界上總是存在著那麼多不“安分”的人,他們會非常的不誠實,比如客戶端發送的字符串“abcdef”明明只有6個,而我非得把payload設置為500,如果服務器傻不拉幾的不做任何邊界檢查,直接申請(1+2+500)大小內存,而且更過分的是還把"abcdef********"所有的內容拷貝到新申請的內存處,並發回給客戶端。
這樣那些不安分的人就獲得了服務器上很多非常敏感的信息,這些信息可能包括銀行帳號信息,電子交易信息等等諸多安全信息。
至此,您可能對heartbleed漏洞有了一些初步的了解了,接下來我們看看它的源碼長什麼樣子的。
heartbleed漏洞源碼分析
上面我們已經簡單的描述了heartbleed漏洞,您應該已經有了一個初步的了解了,接下來我們具體的來看一看openssl的heartbleed漏洞是什麼樣的。
我們直接來看一下他們最近修復提交的代碼和之前代碼的區別在什麼地方就可以了。
--- a/ssl/d1_both.c
+++ b/ssl/d1_both.c
@@ -1459,26 +1459,36 @@ dtls1_process_heartbeat(SSL *s)
unsigned int payload;
unsigned int padding = 16; /* Use minimum padding */
- /* Read type and payload length first */
- hbtype = *p++;
- n2s(p, payload);
- pl = p;
-
if (s->msg_callback)
s->msg_callback(0, s->version, TLS1_RT_HEARTBEAT,
&s->s3->rrec.data[0], s->s3->rrec.length,
s, s->msg_callback_arg);
+ /* Read type and payload length first */
+ if (1 + 2 + 16 > s->s3->rrec.length)
+ return 0; /* silently discard */
+ hbtype = *p++;
+ n2s(p, payload);
+ if (1 + 2 + payload + 16 > s->s3->rrec.length)
+ return 0; /* silently discard per RFC 6520 sec. 4 */
+ pl = p;
+
if (hbtype == TLS1_HB_REQUEST)
{
unsigned char *buffer, *bp;
+ unsigned int write_length = 1 /* heartbeat type */ +
+ 2 /* heartbeat length */ +
+ payload + padding;
int r;
+ if (write_length > SSL3_RT_MAX_PLAIN_LENGTH)
+ return 0;
+
/* Allocate memory for the response, size is 1 byte
* message type, plus 2 bytes payload length, plus
* payload, plus padding
*/
- buffer = OPENSSL_malloc(1 + 2 + payload + padding);
+ buffer = OPENSSL_malloc(write_length);
bp = buffer;
/* Enter response type, length and copy payload */
@@ -1489,11 +1499,11 @@ dtls1_process_heartbeat(SSL *s)
/* Random padding */
RAND_pseudo_bytes(bp, padding);
- r = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);
+ r = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, write_length);
if (r >= 0 && s->msg_callback)
s->msg_callback(1, s->version, TLS1_RT_HEARTBEAT,
- buffer, 3 + payload + padding,
+ buffer, write_length,
s, s->msg_callback_arg);
OPENSSL_free(buffer);
從上面的差異我們可以看到,服務器處理心跳原來的方式是首先直接解析type和payload,什麼都不做任何的檢查。
unsigned int payload;
unsigned int padding = 16; /* Use minimum padding */
- /* Read type and payload length first */
- hbtype = *p++;
- n2s(p, payload);
- pl = p;
-
接下來我們再看看修復之後他們是如何處理的:
我們之前介紹的示例代碼和openssl的代碼的data數據格式有一點差別就是openssl的data進行了16字節的數據對齊,其他格式一致。示例代碼是為了讓大家更好的理解原理,所以很多細節的東西就沒有添加,避免由於復雜度過高而不易理解。
接下來我們來看一下openssl添加的兩個最重要的判斷條件:
if (1 + 2 + 16 > s->s3->rrec.length)
return 0; /* silently discard */
這個判斷的目的是為了避免data的length為0這一特殊情況的處理;
if (1 + 2 + payload + 16 > s->s3->rrec.length)
return 0; /* silently discard per RFC 6520 sec. 4 */
從這個判斷條件我們可以看出,對payload的大小做了檢查,如果超出了length就表示你可能是惡意攻擊,直接返回0。
結論
從openssl的heartbleed漏洞我們可以看出,儘管已經被大家廣泛使用的openssl技術,且應用於很多金融領域,但是這裡面依然存在著很多致命的漏洞。從上面的分析,您或許可以得出為什麼這次的漏洞會叫heartbleed漏洞了,確實太heart bleed了。
文章轉載:http:/http://my.oschina.net/gschen/blog/221796