Điều đó giúp giao thức TCP được ứng dụng chủ yếu trong những hoạt động cần độ chính xác cao như truyền tải trang web, truyền tải email, hay kho lưu trữ điện toán đám mây,… Ngược lại, gia
GIỚI THIỆU VỀ SOCKET PROGRAMING 1.1 Tổng quan về Socket và mục đích của Socket Programing
Tổng quan
a Socket và lập trình Socket
Socket là một điểm cuối của liên kết giao tiếp hai chiều (two-way communication) giữa hai chương trình đang chạy trên mạng Nó hoạt động như một cánh cửa kết nối, cung cấp khả năng truyền dữ liệu giữa các chương trình với nhau.
Lập trình Socket là một mô hình lập trình cho phép giao tiếp giữa các tiến trình
(ứng dụng/chương trình) qua mạng Nó cho phép các tiến trình chạy trên các máy tính khác nhau trao đổi dữ liệu. b Cấu trúc
Socket được định nghĩa trong hệ điều hành bằng một cấu trúc, được xem như điểm nối để hai chương trình giao tiếp với nhau Cấu trúc của socket có 5 trường, gồm:
− Family: xác định họ địa chỉ socket sử dụng (IPv4, IPv6, ).
− Type: xác định loại socket.
− Protocol: chỉ định giao thức cụ thể được sử dụng (thường được gán = 0).
− Local Socket Address và Remote Socket Address: là địa chỉ socket của máy cục bộ và máy từ xa.
Hình 1.1: Cấu trúc Socket c Phân loại
Có 4 loại Socket phổ biến:
Socket Hướng kết nối (Stream Socket) hoạt động dựa trên giao thức TCP, thiết lập kết nối giữa máy chủ và máy khách Ưu điểm của loại này là đảm bảo truyền dữ liệu chính xác, đúng thứ tự và nhanh chóng nhờ khả năng xác nhận trả về giữa các thông điệp Tuy nhiên, nhược điểm là mỗi kết nối chỉ có một IP, do đó một máy phải chờ chấp nhận kết nối từ máy kia.
− Datagram Socket: còn được gọi là Socket không hướng kết nối Hình thức hoạt động với sự hỗ trợ của giao thức UDP (User Datagram Protocol). Datagram Socket hoạt động tốt ngay cả khi không thiết lập kết nối giữa hai máy với nhau Các ưu điểm có thể kể đến của Datagram Socket là quá trình kết nối, truyền tải thông tin đơn giản, không yêu cầu quá nhiều thao tác Thời gian truyền tải dữ liệu được đánh giá là cực nhanh chóng Thế nhưng, nó có một nhược điểm lớn là quá trình truyền thông tin chưa đảm bảo độ tin cậy, dữ liệu bên nhận có thể không đúng với thứ tự bên truyền hoặc bị lặp.
− WebSocket: Là công cụ hỗ trợ kết nối qua lại giữa Client và Server trên
Internet Quy trình diễn ra nhanh chóng, hiệu quả nhờ việc sử dụng TCP Socket. Không chỉ dùng riêng cho ứng dụng web, loại này có thể áp dụng với bất kỳ mục đích trao đổi thông tin khác Điều cần đảm bảo là hoạt động diễn ra trên không gian mạng Loại này có ưu điểm như sau: Tăng cường tốc độ truyền tải thông tin giữa hai chiều Khi phát hiện lỗi rất dễ phát hiện và xử lý Cách sử dụng đơn giản, không cần cài đặt thêm các phần mềm bổ sung khác Không cần dùng nhiều phương pháp hỗ trợ kết nối khác nhau Trong khi đó, nhược điểm là nó chưa thể chạy trên tất cả trình duyệt Điều tương tự xảy ra đối với dịch vụ có phạm vi yêu cầu, khó hỗ trợ hoàn toàn.
− Unix Socket: Đây là điểm giao tiếp, hỗ trợ trao đổi giữa các ứng dụng khác nhau ngay trên cùng máy tính Mọi hoạt động được ghi nhận diễn ra ở nhân của hệ điều hành Nhờ vậy, tốc độ kết nối, truyền tải nhanh, nhẹ, đảm bảo hiệu quả Đặc điểm của Unix Socket là tránh được các bước kiểm tra hoặc
Routing Vì thế, quá trình truyền tin dễ dàng hơn nhiều Ngoài ra, một số ưu điểm khác với dạng Endpoint này là: Có thể tăng cường tốc độ truy cập MySQL lên đến 30 – 50%, giảm độ trễ, từ 60ms chỉ còn 5ms Tăng cường PostgreSQL lên hơn 30% Tăng Redis lên 50% Nhược điểm tồn tại trong trường hợp ứng dụng nằm trên những máy chủ khác nhau Hệ thống sẽ không thể kết nối bằngUnix Socket Ngoài ra, vấn đề phân quyền giữa các tập tin đôi khi vẫn xảy ra.Ngoài ra, còn có một số loại Socket khác như Raw Socket, Sequenced PacketSocket, …
Mục đích của lập trình Socket
Socket được sử dụng phổ biến giúp tiết kiệm thời gian và nâng cao hiệu suất công việc Ưu điểm vượt trội của socket là hỗ trợ trên hầu hết các hệ điều hành, bao gồm cả MS
Windows, Linux,… Ngoài ra, nó cũng được sử dụng với nhiều ngôn ngữ lập trình, gồm C, C++, Java, Python, v.v… Vì thế nó tương thích với hầu hết mọi đối tượng người dùng với những cấu hình máy khác nhau.
Mục đích của lập trình Socket là tạo ra một cơ chế để ứng dụng có thể giao tiếp với nhau qua mạng Điều này có vai trò quan trọng trong việc phát triển ứng dụng mạng và giao tiếp giữa các thiết bị trên cùng một mạng Dưới đây là một số mục đích cụ thể của việc sử dụng lập trình Socket.
Giao tiếp giữa các quy trình (process): Socket cho phép các quy trình trên các máy tính khác nhau giao tiếp với nhau Điều này rất hữu ích trong việc xây dựng ứng dụng phân tán, nơi các phần của ứng dụng chạy trên các máy tính riêng biệt và cần trao đổi dữ liệu.
Truyền dữ liệu: Socket cho phép các ứng dụng gửi và nhận dữ liệu qua mạng. Điều này cho phép truyền dữ liệu từ một máy tính đến một máy tính khác, hoặc giữa các tiến trình trên cùng một máy tính Các gói tin này có thể chứa thông tin về tin nhắn, tệp tin, hoặc bất kỳ dữ liệu nào khác.
Tạo liên kết giữa client và server: Khi một ứng dụng client muốn gửi dữ liệu đến server, nó sẽ tạo một socket và kết nối đến socket trên server thông qua một cổng.
Cổng là một số nguyên dương được sử dụng để xác định dịch vụ hoặc ứng dụng cụ thể trên server.
Hỗ trợ nhiều ngôn ngữ và hệ điều hành: Socket programming không chỉ hỗ trợ các ứng dụng viết bằng ngôn ngữ lập trình khác nhau (như Python, Java, C++, v.v.) giúp tận dụng sức mạnh của mỗi ngôn ngữ và tối ưu hóa hiệu suất mà còn hoạt động trên nhiều hệ điều hành khác nhau (như Windows, Linux, macOS, v.v.) giúp việc lập trình tích hợp dễ dàng và tăng tính linh hoạt và tương thích với nhiều môi trường khác nhau.
Cơ chế hoạt động của Socket trong trao đổi dữ liệu qua Internet
Hiện nay, dữ liệu được truyền qua Internet thông qua hai giao thức chủ yếu là
TCP và UDP Đối với giao thức TCP sử dụng Stream Socket, dữ liệu được truyền bảo đảm tính toàn vẹn, độ tin cậy và thứ tự các gói tin Điều đó giúp giao thức TCP được ứng dụng chủ yếu trong những hoạt động cần độ chính xác cao như truyền tải trang web, truyền tải email, hay kho lưu trữ điện toán đám mây,… Ngược lại, giao thức UDP sử dụng Datagram Socket cung cấp một đường truyền nhanh chóng, nhưng dữ liệu không thể đáng tin cậy và khó đảm bảo tính toàn vẹn như TCP Dễ thấy rằng, với những đặc tính như vậy, giao thức UDP được dùng trong những hoạt động mạng yêu cầu tính thời gian thực, hoặc không yêu cầu độ chính xác của dữ liệu quá cao Một số ứng dụng của giao thức UDP có thể kể đến là truyền phát trực tiếp, trò chơi trực tuyến, hay các ứng dụng liên lạc thời gian thực như Zoom, Skype có thể chấp nhận một số mất mát dữ liệu mà không ảnh hưởng đến chất lượng cuộc gọi.
Datagram Socket là loại socket trong lập trình mạng dùng để giao tiếp qua giao thức UDP Đặc điểm của Datagram Socket là truyền dữ liệu dưới dạng các gói tin độc lập, không cần thiết lập kết nối trước.
− Tạo và gán địa chỉ cho socket: Đầu tiên, ứng dụng muốn truyền dữ liệu cần phải tạo một Datagram Socket để thiết lập điểm kết nối trên máy tính Sau đó cần gán một địa chỉ IP và một số cổng cho socket, để socket có thể xác định được nơi nó sẽ lắng nghe hoặc gửi dữ liệu.
− Gửi dữ liệu: Khi cần gửi dữ liệu, ứng dụng sử dụng hàm gửi (sendto) để gửi dữ liệu qua socket Dữ liệu được gói vào các gói tin và gửi đi mà không cần thiết lập kết nối trước.
− Nhận dữ liệu: Để nhận dữ liệu, ứng dụng sử dụng hàm nhận (recvfrom) để lắng nghe các gói tin đến qua socket Khi một gói tin đến, ứng dụng nhận được dữ liệu cùng với địa chỉ và cổng của nguồn gửi.
− Xử lý dữ liệu: Sau khi nhận dữ liệu, ứng dụng có thể xử lý nó theo yêu cầu của mình, ví dụ: hiển thị dữ liệu lên giao diện người dùng, lưu trữ vào cơ sở dữ liệu, hoặc truyền tiếp cho các phần khác của ứng dụng.
Stream Socket được sử dụng trong giao thức TCP, cụ thể là trong quá trình bắt tay ba bước Quá trình này giúp thiết lập một kênh truyền an toàn giữa hai thực thể trong mạng Internet Đúng như tên gọi của nó, quy trình này có 3 bước cơ bản như sau:
Hình 1.2: Quá trình bắt tay 3 bước
− Đầu tiên, máy chủ A sẽ khởi tạo kết nối bằng cách gửi gói tin SYN đến máy chủ đích Gói tin này chứa một số ngẫu nhiên(ví dụ:100) đánh dấu sự bắt đầu của số thứ tự cho dữ liệu mà máy chủ A sẽ truyền.
− Máy chủ đích nhận gói SYN của A, sau đó nó sẽ phản hồi lại bằng gói SYN là số thứ tự của của máy A tăng thêm 1 (VD: 101), và gói ACK là số của máy server (VD: 200) (số xác nhận)
− Máy chủ A sau khi nhận được 2 gói ACK và SYN từ máy chủ đích, cũng sẽ gửi lại một gói ACK xác nhận là số thứ tự của máy đích tăng thêm 1 (VD:
Sau khi 3 bước được thực hiện, kênh truyền giữa 2 máy đã được tạo và nó sẽ duy trì liên tục cho đến khi kết thúc quá trình truyền tin Để đi vào chi tiết hơn, hình sau sẽ nói về cơ chế hoạt động của Stream Socket:
Hình 1.3: Cơ chế hoạt động của Stream Socket
− Bước 1: Client sẽ tạo ra 1 socket có chức năng gửi yêu cầu kết nối với Server. Còn Server sẽ tạo 1 socket có chức năng lắng nghe và chấp nhận các yêu cầu kết nối từ phía Client Quá trình tạo sẽ sử dụng hàm socket() Sau đó, hai bên sẽ gán địa chỉ đích và Port cho socket của mình.
− Bước 2: Socket ở phía Client sẽ gửi đi gói tin SYN như một yêu cầu kết nối bằng hàm connect(), trong khi socket ở Server sẽ lắng nghe yêu cầu bằng hàm listen() Khi “bắt” được yêu cầu từ phía Client, nó sẽ chấp nhận kết nối bằng hàm accept().
− Bước 3: Server sẽ tạo một socket mới có chức năng truyền và nhận dữ liệu. Sau đó, Client và Server sẽ trao đổi các gói tin ACK và SYN như đã trình bày ở trên qua socket này, tiếp theo chúng mới truyền và nhận dữ liệu của nhau qua hàm send() và recv().
− Bước 4: Sau khi quá trình trao đổi dữ liệu hoàn tất, tài nguyên trên socket sẽ được giải phóng bằng hàm close() Tuy nhiên socket không bị xóa hoàn toàn, mà sẽ có thể tái sử dụng nhằm tối ưu hiệu suất và tài nguyên của Server.
Các ứng dụng của lập trình Socket
Lập trình Socket được sử dụng rộng rãi và có nhiều ứng dụng trong các lĩnh vực khác nhau, bao gồm:
Giao tiếp Client-Server: Lập trình socket được sử dụng rộng rãi để cho phép giao tiếp giữa máy khách và máy chủ trong môi trường mạng Máy chủ lắng nghe các kết nối đến ở các port cụ thể, trong khi máy khách kết nối với máy chủ, cho phép chúng trao đổi dữ liệu và thực hiện nhiều tác vụ khác nhau như truyền tệp, truy cập từ xa và nhắn tin.
Web Server và Client: Máy chủ web, chứa các trang web và phục vụ trang web cho các máy khách (trình duyệt web), sử dụng lập trình socket để xử lý yêu cầu và phản hồi HTTP Tương tự, các máy khách (trình duyệt) sử dụng socket để giao tiếp với các máy chủ khi truy xuất trang web, gửi yêu cầu HTTP và nhận phản hồi. Ứng dụng chat: Lập trình socket cho phép giao tiếp thời gian thực trong các ứng dụng trò chuyện, cho phép người dùng gửi và nhận tin nhắn ngay lập tức
Các máy chủ quản lý kết nối từ nhiều client và phân phối tin nhắn giữa chúng, tạo điều kiện cho trò chuyện một-một hoặc trò chuyện nhóm.
Trò chơi trực tuyến đa người chơi: Lập trình socket rất quan trọng cho việc phát triển các trò chơi trực tuyến đa người chơi, nơi người chơi tương tác thời gian thực với nhau Các máy chủ sử dụng socket để quản lý kết nối của người chơi, xử lý logic trò chơi và đồng bộ hóa trạng thái trò chơi trên nhiều client, cho phép trải nghiệm chơi game có tính cộng tác và cạnh tranh.
Truy cập và điều khiển từ xa: Lập trình socket tạo điều kiện cho việc truy nhập và kiểm soát máy tính và các thiết bị từ xa qua mạng Các ứng dụng desktop từ xa và bộ giả lập terminal sử dụng socket để thiết lập kết nối giữa một thiết bị client và một hệ thống từ xa, cho phép người dùng truy cập và quản lý tài nguyên từ xa Các hệ thống phân tán: Lập trình socket đóng một vai trò quan trọng trong việc xây dựng các hệ thống phân tán, nơi nhiều thành phần độc lập giao tiếp với nhau qua mạng Các ứng dụng phân tán như cơ sở dữ liệu phân tán, hệ thống tệp phân tán và các trang tính phân tán phụ thuộc vào socket để giao tiếp giữa các tiến trình và trao đổi dữ liệu.
IoT: Trong các hệ thống IoT, các thiết bị tương tác với nhau và với các máy chủ tập trung để thu thập dữ liệu, giám sát môi trường và điều khiển các đối tượng vật lý Lập trình socket tạo điều kiện cho việc giao tiếp giữa các thiết bị IoT, cổng thông tin và máy chủ đám mây, cho phép truyền dữ liệu thời gian thực, thu thập dữ liệu cảm biến và quản lý thiết bị.
Giám sát và quản lý mạng: Lập trình socket được sử dụng trong các công cụ giám sát và quản lý mạng để thu thập thống kê, giám sát lưu lượng và quản lý các thiết bị mạng Các công cụ này sử dụng socket để giao tiếp với các thiết bị mạng, thu thập thông tin và thực hiện các nhiệm vụ cấu hình.
Truyền dữ liệu: Các ứng dụng truyền tập tin như FTP và SCP sử dụng lập trình socket để truyền tập tin giữa các client và máy chủ một cách an toàn Socket cho phép truyền dữ liệu đáng tin cậy và hỗ trợ các tính năng như xác minh tính toàn vẹn của tập tin và tiếp tục truyền tập tin bị gián đoạn.
Thu thập dữ liệu: Có thể sử dụng lập trình socket để tạo các kết nối mạng tới các cổng TCP hoặc UDP trên server mục tiêu và xác định trạng thái của các cổng đó (mở, đóng hoặc được lọc) Bằng cách sử dụng socket, người dùng có thể thiết lập các kết nối mạng và gửi các gói tin đến máy chủ để kiểm tra xem cổng có sẵn hay không Điều này cho phép hacker xác định được thông tin ban đầu của mục tiêu để khai thác các lỗ hổng và thực hiện tấn công Bên cạnh đó, lập trình socket cũng giúp người quản trị mạng đánh giá được độ an toàn của hệ thống để đưa ra những giải pháp khắc phục, phòng chống xâm nhập.
Các thư viện và ngôn ngữ hỗ trợ lập trình Socket
1.4.1 Các ngôn ngữ hỗ trợ lập trình Socket
Lập trình Socket chính là cốt lõi của lập trình mạng, do đó hiện nay có rất nhiều các ngôn ngữ lập trình phổ biến hỗ trợ lập trình socket, dưới đây là một số ví dụ:
-C/C++: là ngôn ngữ phổ biến nhất cho lập trình socket Nó cung cấp hiệu suất cao và khả năng kiểm soát chi tiết cho các lập trình viên Các thư viện hỗ trợ lập trình socket gồm: Thư viện Winsock dành cho hệ điều hành Windows; Thư viện Berkeley sockets dành cho hệ điều hành Unix/Linux và thư viện Libevent – thư viện đa nền tảng hỗ trợ nhiều giao thức mạng và các tính năng nâng cao.
-Java: Java là ngôn ngữ lập trình hướng đối tượng phổ biến khác hỗ trợ lập trình socket Java cung cấp khả năng di động cao và dễ sử dụng Các thư viện hỗ trợ lập trình socket gồm: java.net.Socket: Cung cấp các lớp cơ bản để tạo kết nối socket và gửi/nhận dữ liệu; java.nio.channels.SocketChannel: Cung cấp các lớp hiệu suất cao hơn cho lập trình socket không đồng bộ.
-Go: Go là ngôn ngữ lập trình mới nổi được thiết kế cho hiệu suất cao và khả năng đồng bộ Go là lựa chọn tốt cho việc phát triển các ứng dụng socket có yêu cầu cao.
Thư viện hỗ trợ lập trình socket là net, có chức năng cung cấp các lớp cơ bản để tạo kết nối socket và gửi/nhận dữ liệu.
-JavaScript: JavaScript được sử dụng chủ yếu cho phát triển web, nhưng nó cũng có thể được sử dụng để lập trình socket Các thư viện hỗ trợ lập trình socket gồm
WebSocket – cung cấp giao thức cho phép truyền dữ liệu hai chiều giữa máy chủ và trình duyệt web và thư viện Socket.IO – thư viện hỗ trợ lập trình socket thời gian thực cho các ứng dụng web.
-Python: Python là ngôn ngữ lập trình đơn giản và dễ học, phù hợp cho việc phát triển các ứng dụng socket đơn giản Python sử dụng một số thư viện để lập trình socket như: Thư viện socket – cung cấp các lớp cơ bản để tạo kết nối socket và gửi/nhận dữ liệu; Asyncore – thư viện hỗ trợ lập trình socket không đồng bộ;
Twisted – Thư viện mạng mạnh mẽ hỗ trợ nhiều giao thức mạng và các tính năng nâng cao.
1.4.2 Giới thiệu module socket trong Python
Python cung cấp Module Socket giúp chúng ta dễ dàng thực hiện kết nối
Client/Server để giao tiếp với nhau Để có thể sử dụng được, trước tiên ta phải import module Socket vào chương trình:
Socket_family:họ địa chỉ
AF_INET: IPV4 AF_INET6: IPV6 AF_UNIX
Socket Type: cách thiết lập giao thức
SOCK_STREAM dựa trên giao thức TCP, chỉ thực hiện giao tiếp giữa hai tiến trình đã thiết lập kết nối, đảm bảo dữ liệu được truyền đi tin cậy và theo đúng thứ tự Loại socket này còn được gọi là socket hướng kết nối.
+ SOCK_DGRAM: Dựa trên giao thức UDP, không yêu cầu thiết lập kết nối Giao thức này không đảm bảo dữ liệu truyền đến nơi nhận 1 cách tin cậy và toàn vẹn Còn được gọi là socket hướng không kết nối.
Bảng 1.4: Chi tiết tham số của hàm socket()
Một số phương thức được sử dụng trong lập trình Socket với ngôn ngữ Python: self.node.bind(port_and_ip) Phương thức này nhằm gán địa chỉ IP và Port self.node.listen(5) Phương thức này node server sẽ lắng nghe kết nối đến self.connection, addr = self.node.accept() Phương thức này thiết lập chấp nhận kết nối self.connection.send(SMS.encode()) Phương thức này dùng để gửi tin nhắn khi được kết nối self.connection.recv(1024).decode() Phương thức này sử dụng để nhận thông báo từ client trong suốt lúc chương trình hoạt độngBảng 1.5: Một số phương thức dùng để kết nối và trao đổi thông tin.
Python hỗ trợ thu thập thông tin Server, bao gồm: tên miền, host, địa chỉ IP, … với một số Module sau đây: gethostname() Trả về một chuỗi chứa tên máy chủ của máy mà trình thông dịch Python hiện đang thực thi. gethostbyname(hostname)
Dùng để dịch tên máy chủ thành địa chỉ IPv4 Địa chỉ IPv4 được trả về dưới dạng chuỗi và được lưu vào biến addr gethostbyname() không hỗ trợ
Tương tự gethostbyname() nhưng được mở rộng thêm Trả về bộ ba: hostname, aliaslist, ipaddrlist, trong đó hostname là tên máy chủ lưu trữ chính của hostname, aliaslist là danh sách (có thể trống) tên các máy chủ lưu trữ thay thế cho cùng một địa chỉ và ipaddrlist là danh sách địa chỉ IPv4. gethostbyname_ex() không hỗ trợ IPv6. gethostbyaddr(ip_address) Tương tự gethostbyname_ex(), tuy nhiên tham số truyền vào là địa chỉ IPv4. getfqdn(name) Trả về đầy đủ tên miền nếu nó có sẵn. getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)
Chuyển đổi các đối số cơ sở lưu trữ/cổng thành chuỗi gồm năm bộ chứa tất cả các đối số cần thiết để tạo một Socket được kết nối với dịch vụ đó Cơ sở lưu trữ là tên miền còn cổng là tên dịch vụ chuỗi.
Hàm này sẽ trả về 5 bộ với cấu trúc như sau:
(family, type, proto, canonname, sockaddr). connect_ex(IP address, port) Trả về giá trị 0 nếu thao tác thành công, ngược lại sẽ trả về giá trị cho biến errno.
Bảng 1.6: Một số phương thức dùng để thu thập thông tin Server.
ỨNG DỤNG AN TOÀN SOCKET PROGRAMING
Các phương pháp bảo mật
Trong lập trình Socket, việc xác thực người dùng là vô cùng quan trọng Nó giúp đảm bảo chỉ những người dùng đã được ủy quyền mới có thể truy cập vào các tài nguyên mạng Nói cách khác, xác thực người dùng giúp bảo vệ tính toàn vẹn, tính bảo mật và độ tin cậy của hệ thống.
Quá trình xác thực thường diễn ra như sau:
+ Người dùng kết nối đến máy chủ thông qua socket.
+ Máy chủ yêu cầu người dùng cung cấp thông tin xác thực (VD: user và password)
+ Người dùng gửi thông tin xác thực đến máy chủ.
+ Máy chủ kiểm tra thông tin xác thực và quyết định liệu người dùng có được phép truy cập hay không.
+ Nếu thông tin xác thực là hợp lệ, máy chủ sẽ cho phép kết nối tiếp tục và cung cấp các dịch vụ được yêu cầu.
Sau đây là một số phương thức xác thực người dùng trong lập trình Socket: + Xác thực mật khẩu (Password Authentication): Trong phương pháp này,người dùng cần cung cấp một cặp tên đăng nhập và mật khẩu để đăng nhập vào hệ thống Mật khẩu thường được bảo mật bằng cách sử dụng hàm băm.Sau đó, máy chủ sẽ kiểm tra xem cặp username và password này có hợp lệ hay không.
+ Xác thực mã thông báo (Token-based Authentication): Người dùng đăng nhập bằng cách cung cấp một mã thông báo (token) duy nhất Mã thông báo này có thể được tạo ra bằng nhiều cách khác nhau và thường có thời hạn.
+ Xác thực chứng chỉ (Certificate Authentication): Sử dụng chứng chỉ
SSL/TLS để xác thực Mỗi bên đều có một bộ chứng chỉ, máy chủ sẽ xác minh xem chứng chỉ của người dùng có được ký bởi một tổ chức uy tín hay không Trong python, có thể sử dụng module ssl để thực hiện xác thực theo cách này.
Giao thức SSL/TLS là giao thức ở lớp Transport có tầm quan trọng cao nhất đối với sự bảo mật của các trình ứng dụng trên Web.
SSL: Viết tắt của Security Sockets Layer, SSL là một giao thức mật mã giúp truyền thông an toàn qua mạng máy tính SSL được phát triển bởi Netscape, phiên bản đầu tiên được công bố là SSL 2.0 (năm 1995), sau đó là phiên bản 3.0 (năm 1996) Tuy nhiên do có nhiều lỗ hổng nên không được phát triển nữa và ngày nay SSL cũng ít được sử dụng hơn TLS.
TLS: Viết tắt của Transport Layer Security, TLS cũng giống như SSL là một giao thức mật mã giúp truyền thông an toàn TLS 1.0 được IETF phát triển năm
1999, các phiên bản tiếp theo là TLS 1.1, 1.2, 1.3.
Tóm lại, TLS là phiên bản tiếp theo và cải tiến của SSL, được phát triển để cung cấp một môi trường bảo mật mạnh mẽ hơn cho việc truyền dữ liệu qua mạng Trong khi SSL vẫn tồn tại và được sử dụng trong một số trường hợp, TLS là lựa chọn ưu tiên cho các ứng dụng và dịch vụ mới.
Trong giao thức SSL/TLS có 4 giao thức con với những chức năng cụ thể: + SSL Record: Giao thức này được sử dụng để chia nhỏ dữ liệu thành các gói tin nhỏ hơn, mã hóa dữ liệu và thêm các thông tin bảo mật như mã băm, chữ ký số, và khóa phiên trước khi gửi đi
+ SSL Handshake: Giao thức Handshake được sử dụng để thiết lập kết nối an toàn giữa máy khách và máy chủ, xác thực các bên và thỏa thuận các thông tin cần thiết cho việc thiết lập kết nối bảo mật.
Giao thức SSL Change Cipher Spec được sử dụng để truyền đạt cho các bên tham gia rằng dữ liệu được trao đổi sau đó sẽ được mã hóa bằng các thuật toán được thỏa thuận trong giao thức bắt tay (Handshake).
+ SSL Alert: Giao thức này được sử dụng để thông báo về các sự kiện quan trọng và cảnh báo liên quan đến quá trình trao đổi dữ liệu bảo mật như như lỗi xác thực, lỗi băm, hoặc lỗi mã hóa,…
Trong 4 giao thức trên, mỗi giao thức đều có tầm quan trọng riêng, nhưng đặc biệt phải kể đến giao thức SSL Handshake, đây được coi như là cốt lõi của giao thức SSL.
Giao tiếp qua SSL luôn bắt đầu bằng SSL Handshake Handshake SSL là một mật mã bất đối xứng cho phép trình duyệt xác minh máy chủ web, lấy khóa công khai và thiết lập kết nối an toàn trước khi bắt đầu truyền dữ liệu thực tế.
Hình sau minh họa các bước liên quan đến quá trình SSL Handshake:
Bước đầu tiên, máy khách sẽ gửi một thông báo "client hello" Thông báo này bao gồm số phiên bản SSL của máy khách, cài đặt mật mã, dữ liệu theo phiên cụ thể và các thông tin khác cần thiết để máy chủ giao tiếp với máy khách bằng SSL.
− Máy chủ phản hồi bằng một thông báo "server hello" Bao gồm số phiên bản SSL của máy chủ, cài đặt mật mã, dữ liệu theo phiên cụ thể, chứng chỉ SSL có khóa công khai và thông tin khác mà máy khách cần để giao tiếp với máy chủ qua SSL.
-Máy khách xác minh chứng chỉ SSL của máy chủ từ CA (Certificate
Authority) và xác thực máy chủ Nếu xác thực không thành công, thì máy khách từ chối kết nối SSL.)
− Nếu xác thực thành công, Máy khách tạo một session key, mã hóa nó bằng khóa công khai của máy khác và gửi đến máy chủ Nếu máy chủ đã yêu cầu xác thực máy khách (chủ yếu là trong giao tiếp máy chủ với máy chủ), thì máy khách sẽ gửi chứng chỉ của chính mình đến máy chủ.
− Máy chủ giải mã khóa phiên bằng khóa riêng của nó và gửi xác nhận đến máy khách được mã hóa bằng khóa phiên.
THỰC NGHIỆM
Tổng quan mô hình xây dựng bài toán
a) Bài toán xác thực người dùng:
Sử dụng ngôn ngữ Python để xây dựng một ứng dụng giao tiếp đơn giản giữa thực thể là Client và Server Trước khi muốn giao tiếp, Client phải đăng nhập thành công bằng tên tài khoản và mật khẩu được lưu trữ ở dạng băm trong cơ sở dữ liệu Nếu chưa có tài khoản, Client có thể lựa chọn “Đăng ký” để tạo tài khoản mới Tài khoản đó bao gồm tên tài khoản và mật khẩu ở dạng băm, nhằm tăng tính an toàn đối với các cuộc tấn công vào cơ sở dữ liệu. b) Bài toán tăng cường tính bảo mật trong quá trình giao tiếp Client – Server :
Giao tiếp SSL/TLS cho kết nối Client-Server được xây dựng bằng cách kết hợp module socket và ssl Trước khi kết nối, Client xác thực Server bằng chứng chỉ SSL do Server gửi Nếu xác thực thành công, Client tiến hành kết nối và trao đổi thông điệp với Server.
Tiến hành
3.2.1 Xác thực người dùng trong giao tiếp Client-Server:
*)Server: Đầu tiên ta cần import vào các module cần thiết:
-Trong đó module sqlite3 là một thư viện cung cấp chức năng truy cập và thao tác với cơ sở dữ liệu Sqlite
-Module hashlib là một thư viện cung cấp các hàm băm mật mã cho dữ liệu
-Module socket đã được giải thích.
Sau đó, tạo ra một socket, gán địa chỉ và port cho socket đó, rồi chuyển sang chế độ lắng nghe bằng hàm listen() Chấp nhận kết nối đến:
Xây dựng hàm có chức năng “Đăng ký” Khi hàm này được gọi, Client có thể đăng ký tài khoản mới với username và mật khẩu:
Từ dòng 18 đến 24, sau khi nhập mật khẩu, Client sẽ phải xác nhận lại mật khẩu, nếu mật khẩu xác nhận không trùng khớp với mật khẩu đặt ban đầu, người dùng phải nhập lại.
Từ dòng 25 đến 30, khi mật khẩu đã được xác nhận, nó sẽ được băm và thêm vào cơ sở dữ liệu Khi quá trình hoàn tất, một thông điệp báo thành công sẽ được gửi tới Client.
Xây dựng hàm có chức năng “Đăng nhập” Thông tin username và mật khẩu được nhận từ Client:
Từ dòng 40 đến 42, mật khẩu sau khi nhận sẽ được băm, sau đó truy vấn trong cơ sở dữ liệu.
Từ dòng 44 đến 48, kết quả phép truy vấn sẽ được gửi bằng một thông điệp tới Client.
Cuối cùng, gửi thông điệp gồm 2 lựa chọn đến Client:
Nếu phản hồi của Client là ‘1’ (Log in), kênh chat được tạo ra để Client và Server giao tiếp tới khi thông điệp Server nhận được là ‘exit’, kênh chat sẽ kết thúc:
Nếu phản hồi là ‘2’ (Sign up), hàm có chức năng “Đăng ký” được gọi:
Import module socket, sau đó tạo socket và kết nối đến địa chỉ của Server:
Bắt đầu nhận thông điệp từ Server và gửi phản hồi, nếu lựa chọn là ‘1’ (Log in),người dùng có thể nhập username và mật khẩu, nếu đăng nhập thành công, kênh chat sẽ được tạo ra
Nếu lựa chọn là ‘2’ (Sign up), người dùng sẽ nhập username mật khẩu mới cho đến khi hợp lệ và nhận được thông báo ‘Create account successful’
Lúc này, thông tin tài khoản của Client đã được lưu trên cơ sở dữ liệu với mật khẩu đã được mã hóa theo hàm băm:
Khi người dùng chọn ‘1’ (Log in):
3.2.2 Triển khai giao thức SSL/TLS trong giao tiếp Client – Server a) Các hàm, chức năng chính
- Lưu lại username cũ nếu có trước khi thực hiện set username
- Hiện ra form yêu cầu nhập username:
+/ Lưu username vào file config
+/ Nếu đối tượng là client và đã kết nối thì gửi đi username cũ của user đó
- Mở một form để điền thông tin về server, mục đích tuỳ thuộc vào việc đối tượng là server hay client
+/ Đối với server việc set ip và port để mở kết nối
+/ Đối với client thì đây là bước thiết lập thông tin về server muốn kết nối
- Thông tin về ip và port cũng sẽ được lưu lại để thuận tiện cho những lần kết nối sau
- Vẫn sẽ tương tự với các thiết lập trên, điểm khác biệt là khi nhập sẽ ẩn các kí tự nhập vào bằng các dấu *, cũng như không lưu mật khẩu vào file config để đảm bảo không bị lộ mật khẩu khi bị tấn công
- Tạo socket sử dụng giao thức TCP và Ipv4
- Cho phép address có thể được sử dụng ngay lập tức sau khi đóng server (tránh việc gặp lỗi khi dùng lại address đó)
- Truy vấn DNS tới IP để lấy địa chỉ, gán vào add_info
- Trích xuất IP và Port
- Thực hiên lắng nghe kết nối đến địa chỉ được chỉ định (đoạn trong byte có thể sẽ sửa thành addr)
- Tạo một context SSL/TLS mới dùng để yêu cầu xác thực từ client
- Load file cert và key được chỉ định
- Chuyển socket thành SSL/TLS ở chế độ server side
- Khởi chạy một luồng (thread) mới để chạy hàm accept_clients:
+/ Tạo một vòng lặp chỉ kết thúc khi đóng kết nối qua biến is_connected
+/ Chờ và chấp nhận một kết nối từ client Nó trả về một list/tuple chứa 2 giá trị, gán chúng vào conn, addr (conn là socket đại diện cho kết nối thiết lập với server, addr chứa địa chỉ IP và port của client đã kết nối
+/ Gửi thông báo lên bảng chat là có một client đã kết nối tới server
+/ Tạo một luồng mới để chạy hàm nhận tin nhắn (sẽ được đề cập sau) với
2 tham số là conn và addr
+/ Thiết lập daemon thành True, để khi server đóng thì các luồng con đang chạy cũng sẽ dừng lại
+/ Nếu gặp lỗi sẽ trả về thông báo lỗi và dừng việc chấp nhận kết nối
- Bắt đầu chạy luồng chấp nhận kết nối
- Thông báo rằng Server đã chạy
- Cập nhật tên server vào danh sách người dùng
- Nếu gặp lỗi sẽ trả về thông báo lỗi không thể khởi chạy server
- Nếu đối tượng là server và đang chạy
+/ Duyệt các socket trên list lưu các socket client và thực hiện đóng các socket đó
- Đóng kết nối của bản thân
- Set các trạng thái về False và xoá dữ liệu trong list clients
- Cập nhật user list về trống
- Gửi lên bảng chat thông báo răng server đã đóng
- Nếu đối tượng không phải server hay không chạy thì sẽ báo lỗi
* Tạo cert (tự động tạo khi start server)
- Khởi tạo biến đại diện cho đường dẫn tới cert và key
- Lấy thông tin về thư mục hiện tại (là thư mục chứa script
- Tạo đường dẫn tới 2 file cert và key, gán cho 2 biến đường dẫn khởi tạo ban đầu
- Tạo một khoá bí mật với các tham số được chỉ định
+/ Số mũ public (mã hoá công khai)
+/ Sử dụng backend mặc định để tạo key
- Tạo thông tin chủ thể (subject) và đơn vị cấp phát (issuer) cho chứng chỉ như quốc gia, tình thành, tổ chức,…
- Thiết lập một đối tượng cert (chứng chỉ)
+/ subject_name: Thiết lập thông tin chủ thể của cert
+/ issuer_name: Thiết lập đơn vị cấp phát cert
+/ public_key: Thiết lập public key cho cert
+/ serial_number: Thiết lập số serial (ngẫu nhiên)
+/ not_valid_before: Thiết lập thời gian hiệu lực
+/ not_valid_after: Thiết lập thời gian hết hạn cert
+/ add_extension: Thêm extension cho phép truy cập chứng chỉ qua localhost
+/ sign: Ký cert bằng khoá bí mật, sử dụng SHA256 và back_end mặc định
- Mở file key ở chế độ ghi nhị phân:
+/ Ghi private key vào file theo định dạng PEM và mã hoá PKCS8
+/ Không sử dụng thuật toán mã hoá để lưu khoá bí mật
- Mở file và ghi nội dung cho file cert bằng định dạng PEM
- Trả về thông báo lỗi tạo khoá thất bại khi gặp lỗi
+/ Thay đổi trạng thái thành đã kết nối
+/ Tạo socket sử đụng giao thức TCP và Ipv4
+/ Kết nối tới server được chỉ định
- Tạo một context SSL/TLS mới, sử dụng các thiết lập mặc định
- check_hostname = FALSE: Bỏ qua kiểm tra tên miền server (chỉ dùng trong thử nghiệm)
- verify_mode = ssl.CERT_NONE: Bỏ qua việc xác thực chứng chỉ SSL của server (cũng chỉ dùng trong thử nghiệm)
- Tạo luồng nhận tin nhắn sử
- Gửi username tới server và thực hiện cập nhật lại danh sách user
- Gửi tin nhắn lên khung chat là đã kết nối tới server
- Nếu đối tượng là client và đang kết nối:
+/ Gửi trạng thái disconnect tới server
+/ Đóng socket và đổi trạng thái đã kết nối thành FALSE
+/ Thông báo lên khung chat là đã ngắt kết nối
- Là một chức năng cập nhật danh sách các user đang kết nối tới server
- Trước mắt hàm này sẽ toàn bộ dữ liệu của user list cũ trên giao diện
- Nếu đối tượng là server thì sẽ thêm username của bản thên vào user list, nếu không thì tiếp tục
- Lần lượt thêm tất cả user lại vào danh sách được cung cấp bởi tham số user_list
- Đây là một hàm để gửi các cập nhật và danh sách user cho các client đang kết nối
- Hàm này tạo một biến lưu user list và một đoạn tin nhắn cụ thể, những từ khoá trong đoạn tin nhắn sẽ dùng để xác định đây là một tin nhắn cập nhật
- Mã hoá tin nhắn này và gửi đi
- Tuỳ vào các hành động cụ thể thì server sẽ gửi đi các tin nhắn định dạng khác nhau (các thông báo về username mới, một client đã disconnect hay là một user đổi tên), nếu chỉ là đoạn chat thông thường thì sẽ gửi bằng cách thông thường
- Nếu là client thì sẽ gửi tin nhắn được mã hoá tới server, còn ngược lại thì server sẽ gửi tin nhắn tới toàn bộ client
- Trường hợp tin nhắn không thuộc 3 tin nhắn có định dạng để thực hiện các chức năng thì ra in lên bảng chat, còn không thì sẽ chỉ gửi chứ không in ra
- Trước khi nhận tin, chương trình sẽ kiểm tra xem Client có đang kết nối hay không bằng hàm kiểm tra socket và kiểm tra data nhận được
- Về hàm kiểm tra socket thì chương trình sẽ gửi đi một đoạn byte rỗng tới kết nối được chỉ định, nếu gặp lỗi sẽ trả về False và ngược lại
- Sau khi kiểm tra kết nối (giải mã và mã hoá sẽ đề cập sau) thì sẽ đến công đoạn kiểm tra các từ khoá trong tin nhắn để xác định tin nhắn này thuộc dạng nào Nếu chứa “ENCRYPTEDUSERLIST:” thì chương trình sẽ tạo user list dựa trên tin nhắn gửi tới và cập nhật lại user list hiện tại
- Nếu bắt đầu bằng USERNAME:
+/ Lấy thông tin của username đó
+/ Nếu là server thì update và broadcast userlist Ngoài ra còn gửi thêm một tin nhắn bắt đầu bằng SERVERUSERNAME:
+/ Nếu là client thì chỉ đơn giản là update user list thôi
- Nếu bắt đầu bằng SERVERUSERNAME:
+/ Nếu tên server không có trong danh sách user thì thêm vào
- Nếu bắt đầu bằng USERNAMECHANGE:
+/ Lấy ra username cũ và mới
+/ Nếu là server thì sẽ thay thế vị username mới dánh list clients thay username cũ, đồng thời update và broadcast danh sách user mới đi
+/ Lấy tên username đã ngắt kết nối
+/ Nếu là server thì sẽ xoá username đó khỏi list clients, đồng thời đóng kết nối và cập nhật lại user list
- Còn lại nếu không có bất kì từ khoá nào thì sẽ chỉ đơn giản là gửi tin nhắn đi và hiện trên khung chat
- Kiểm tra cái lỗi trả về khi gửi đi mật khẩu để xác thực, client chỉ được phép nhập sai 3 lần Sau 3 lần nhập sai, kết nối sẽ đóng lại
Ngoài việc mã hoá tin nhắn gửi đi qua SSL/TLS, chương trình này còn kết hợp thêm thuật toán mã hoá khác để tăng cường bảo mật
- Tạo khoá dẫn xuất sử dụng hàm tạo khoá dựa trên mật khẩu (PBKDF2), kết hợp hàm băm sha256 (trước đó password được encode sang dạng byte) với
- Salt được dùng để nối vào mật khẩu nhằm chống các cuộc tấn công
- Hàm trả về một khoá có độ dài được chỉ định bởi KEY_SIZE
- Trước hết hàm mã hoá này sẽ kiểm tra tin nhắn có ở dạng byte không, nếu không thì sẽ chuyển nó về dạng byte
- Thiết lập mã hoá: Khởi tạo đối tượng AES sử sụng key và chế độ EAX
- Mã hoá tin nhắn thành bản mã cũng như tạo một tag (mã xác thực)
Mã hóa tin nhắn sử dụng ba thành phần: nonce (số ngẫu nhiên một lần), ciphertext (bản mã) và tag Nonce ngăn chặn các cuộc tấn công phát lại, trong khi ciphertext chứa tin nhắn được mã hóa và tag xác minh tính toàn vẹn của tin nhắn.
- salt sẽ được tạo ngẫu nhiên và có kích thước được chỉ định
- key được tạo bằng hàm dẫn xuất khoá đã đề cập trước đó
- Mã hoá tin nhắn sử dụng key -> gấn cho encrypted_data
- encrypted_data khi đó là một mảng chứa 3 giá trị nonce, ciphertext, tag, tách chúng thành các xâu để nối với nhau cũng nhau nối với salt -> gán vào encrypted_message
- Cuối cùng thì dùng base64 để mã hoá encrypted message -> gán vào encrypted_messagem, đây sẽ là bản mã cuối cùng để truyền đi
- Tham số đầu vào của hàm giải mã có encrypted_data – là mảng gòm 3 giá trị nonce, ciphertext, tag
- Khởi tạo đối tượng AES sử dụng key, nonce lấy từ encrypted_data, chế độ EAX
- Bản rõ sẽ được lấy từ hàm decrypt_and_verify bằng cách cung cấp bản mã và tag, cuối cùng decode để chuyển từ dạng byte sang dạng thường b) Thực nghiệm trên giao diện UI
- Chương trình sẽ không chia thành 2 file để phân biệt client và server như thông thường mà kết hợp thành một ứng dụng duy nhất
- Giao diện chỉnh (gồm thanh menu, khung hiển thị chat, khung nhập tin nhắn, khung hiển thị danh sách user, nút gửi tin nhắn)
- Phía server: sử dụng menu Settings để thiết lập Server name, Server IP/Port, Password chung để kết nối