=============================== 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䏿œ‰è¯¦ç»†ä»‹ç»ã€‚