=============================== 11.9 简å•çš„å®¢æˆ·ç«¯è®¤è¯ =============================== ---------- 问题 ---------- ä½ æƒ³åœ¨åˆ†å¸ƒå¼ç³»ç»Ÿä¸å®žçŽ°ä¸€ä¸ªç®€å•çš„客户端连接认è¯åŠŸèƒ½ï¼Œåˆä¸æƒ³åƒSSLé‚£æ ·çš„å¤æ‚。 ---------- 解决方案 ---------- å¯ä»¥åˆ©ç”¨ ``hmac`` 模å—实现一个连接æ¡æ‰‹ï¼Œä»Žè€Œå®žçŽ°ä¸€ä¸ªç®€å•è€Œé«˜æ•ˆçš„认è¯è¿‡ç¨‹ã€‚下é¢æ˜¯ä»£ç 示例: .. code-block:: python import hmac import os def client_authenticate(connection, secret_key): ''' Authenticate client to a remote service. connection represents a network connection. secret_key is a key known only to both client/server. ''' message = connection.recv(32) hash = hmac.new(secret_key, message) digest = hash.digest() connection.send(digest) def server_authenticate(connection, secret_key): ''' Request client authentication. ''' message = os.urandom(32) connection.send(message) hash = hmac.new(secret_key, message) digest = hash.digest() response = connection.recv(len(digest)) return hmac.compare_digest(digest,response) 基本原ç†æ˜¯å½“连接建立åŽï¼ŒæœåŠ¡å™¨ç»™å®¢æˆ·ç«¯å‘é€ä¸€ä¸ªéšæœºçš„å—节消æ¯ï¼ˆè¿™é‡Œä¾‹åä¸ä½¿ç”¨äº† ``os.urandom()`` 返回值)。 客户端和æœåŠ¡å™¨åŒæ—¶åˆ©ç”¨hmac和一个åªæœ‰åŒæ–¹çŸ¥é“的密钥æ¥è®¡ç®—å‡ºä¸€ä¸ªåŠ å¯†å“ˆå¸Œå€¼ã€‚ç„¶åŽå®¢æˆ·ç«¯å°†å®ƒè®¡ç®—出的摘è¦å‘é€ç»™æœåŠ¡å™¨ï¼Œ æœåŠ¡å™¨é€šè¿‡æ¯”较这个值和自己计算的是å¦ä¸€è‡´æ¥å†³å®šæŽ¥å—或拒ç»è¿žæŽ¥ã€‚摘è¦çš„比较需è¦ä½¿ç”¨ ``hmac.compare_digest()`` 函数。 使用这个函数å¯ä»¥é¿å…é到时间分æžæ”»å‡»ï¼Œä¸è¦ç”¨ç®€å•çš„比较æ“作符(==)。 ä¸ºäº†ä½¿ç”¨è¿™äº›å‡½æ•°ï¼Œä½ éœ€è¦å°†å®ƒé›†æˆåˆ°å·²æœ‰çš„网络或消æ¯ä»£ç ä¸ã€‚例如,对于sockets,æœåŠ¡å™¨ä»£ç 应该类似下é¢ï¼š .. code-block:: python from socket import socket, AF_INET, SOCK_STREAM secret_key = b'peekaboo' def echo_handler(client_sock): if not server_authenticate(client_sock, secret_key): client_sock.close() return while True: msg = client_sock.recv(8192) if not msg: break client_sock.sendall(msg) def echo_server(address): s = socket(AF_INET, SOCK_STREAM) s.bind(address) s.listen(5) while True: c,a = s.accept() echo_handler(c) echo_server(('', 18000)) Within a client, you would do this: from socket import socket, AF_INET, SOCK_STREAM secret_key = b'peekaboo' s = socket(AF_INET, SOCK_STREAM) s.connect(('localhost', 18000)) client_authenticate(s, secret_key) s.send(b'Hello World') resp = s.recv(1024) ---------- 讨论 ---------- ``hmac`` 认è¯çš„一个常è§ä½¿ç”¨åœºæ™¯æ˜¯å†…部消æ¯é€šä¿¡ç³»ç»Ÿå’Œè¿›ç¨‹é—´é€šä¿¡ã€‚ ä¾‹å¦‚ï¼Œå¦‚æžœä½ ç¼–å†™çš„ç³»ç»Ÿæ¶‰åŠåˆ°ä¸€ä¸ªé›†ç¾¤ä¸å¤šä¸ªå¤„ç†å™¨ä¹‹é—´çš„通信, ä½ å¯ä»¥ä½¿ç”¨æœ¬èŠ‚方案æ¥ç¡®ä¿åªæœ‰è¢«å…许的进程之间æ‰èƒ½å½¼æ¤é€šä¿¡ã€‚ 事实上,基于 ``hmac`` 的认è¯è¢« ``multiprocessing`` 模å—使用æ¥å®žçŽ°å进程直接的通信。 还有一点需è¦å¼ºè°ƒçš„是连接认è¯å’ŒåŠ 密是两ç 事。 认è¯æˆåŠŸä¹‹åŽçš„通信消æ¯æ˜¯ä»¥æ˜Žæ–‡å½¢å¼å‘é€çš„,任何人åªè¦æƒ³ç›‘å¬è¿™ä¸ªè¿žæŽ¥çº¿è·¯éƒ½èƒ½çœ‹åˆ°æ¶ˆæ¯ï¼ˆå°½ç®¡åŒæ–¹çš„密钥ä¸ä¼šè¢«ä¼ 输)。 hmac认è¯ç®—法基于哈希函数如MD5å’ŒSHA-1,关于这个在IETF RFC 2104ä¸æœ‰è¯¦ç»†ä»‹ç»ã€‚