Заключительное задание по кибербезопасности | Августовское обновление

алгоритм

Описание окружающей среды

  • Операционная система: Mac
  • Язык программирования: Python3
  • Зависит от библиотек Python: tkinter, pymysql, ssl, socket и т. д. (в основном встроено в Python)
  • В mysql сначала должна быть создана база данных: filetransfer, новая таблица user, содержащая три поля: id, имя пользователя, пароль

создать сертификат

PROJECT_NAME="jiang zheng fool Project"
# Generate the openssl configuration files.
cat > ca_cert.conf << EOF
[ req ]
distinguished_name     = req_distinguished_name
prompt                 = no
[ req_distinguished_name ]
 O                      = $PROJECT_NAME  Certificate Authority
EOF
cat > server_cert.conf << EOF
[ req ]
distinguished_name     = req_distinguished_name
prompt                 = no
[ req_distinguished_name ]
 O                      = $PROJECT_NAME
 CN                     = SERVER
EOF
cat > client_cert.conf << EOF
[ req ]
distinguished_name     = req_distinguished_name
prompt                 = no

[ req_distinguished_name ]
 O                      = $PROJECT_NAME Device Certificate
 CN                     = SERVER
EOF
mkdir ca
mkdir server
mkdir client
mkdir certDER
# private key generation
openssl genrsa -out ca.key 1024
openssl genrsa -out server.key 1024
openssl genrsa -out client.key 1024
# cert requests
openssl req -out ca.req -key ca.key -new -config ./ca_cert.conf
openssl req -out server.req -key server.key -new -config ./server_cert.conf
openssl req -out client.req -key client.key -new -config ./client_cert.conf
# generate the actual certs.
openssl x509 -req -in ca.req -out ca.crt -sha1 -days 5000 -signkey ca.key
openssl x509 -req -in server.req -out server.crt -sha1 -CAcreateserial -days 5000 -CA ca.crt -CAkey ca.key
openssl x509 -req -in client.req -out client.crt -sha1 -CAcreateserial -days 5000 -CA ca.crt -CAkey ca.key
mv ca.crt ca.key ca/
mv server.crt server.key server/
mv client.crt client.key client/

Услуги, предоставляемые протоколом SSL, в основном включают:

1) Аутентифицировать пользователей и серверы, чтобы гарантировать, что данные отправляются на правильный клиент и сервер;

2) Шифрование данных для предотвращения кражи данных на полпути;

3) Поддерживать целостность данных, чтобы гарантировать, что данные не изменятся во время передачи.

Сгенерированный файл сертификата выглядит следующим образом:

image.png

Откройте ca.crt, и вы увидите результат, как показано ниже:

image.png

image.png

Обзор

Проект в основном состоит из двух небольших проектов, включая функцию группового чата и систему передачи файлов. Функция группового чата в основном реализует такие функции, как многогрупповой чат и кешированные сообщения. Система передачи файлов включает загрузку и скачивание файлов.

Функция группового чата

запуск программы

- client.py  客户端1
- client2.py  客户端2
- server.py   服务端

визуализация

image.png

Введение в папку

  • cer — в этой папке хранится корневой сертификат ЦС, а также сертификаты сервера и клиента (созданные с использованием OpenSSL).
    • CA -- корневой сертификат и ключ
    • server -- ключ сервера, подписанный сертификат и подписанный сертификат
    • client -- секретный ключ клиента, подписанный сертификат и подписанный сертификат

Введение в файл

MultiPersonChat

  • client.py клиент 1
  • клиент2.py клиент2
  • server.py сервер

полная функция

  • Онлайн-напоминание, офлайн-напоминание
  • SSL-ссылка
  • шифрование пароля
  • Пользователь не может повторно войти в систему
  • На стороне сервера необходимо кэшировать некоторые последние исторические сообщения, чтобы передать их только что запущенному клиенту.
  • Визуальный интерфейс
  • многопользовательский чат

client.py Класс клиента

имя метода конкретная функция
send_login_info Отправить имя пользователя и пароль вошедшего в систему пользователя на сервер для проверки и вернуть результат проверки
send_register_info Отправить зарегистрированное имя пользователя и пароль на сервер и вернуть результат регистрации
recv_data Клиент получает данные от сервера
close Закройте сокет для соединения между клиентом и сервером

client.py Класс LoginPanel

имя метода конкретная функция
set_panel_position Установите положение и размер интерфейса входа в систему на экране
config_for_reg_panel Установите другие конфигурации для интерфейса входа в систему
set_title Поместите заголовок интерфейса
set_form Разместите форму входа
set_btn Разместите кнопки регистрации и входа
show Вызовите метод экземпляра, чтобы сделать общий макет интерфейса входа в систему
close Реализовать закрытие интерфейса
get_input Получить имя пользователя и пароль, введенные пользователем
login_func Функция кнопки входа инкапсулирована в интерфейс входа
reg_func Он встроен в кнопку регистрации интерфейса входа в систему, чтобы реализовать переход от интерфейса входа в интерфейс регистрации.

server.py

имя метода Функции
encrypt_psw Зашифровать пароль пользователя с помощью алгоритма MD5.
check_user Убедитесь, что имя пользователя и пароль, введенные пользователем при входе в систему, верны
add_user Проверьте имя пользователя, которое нужно зарегистрировать, чтобы определить, есть ли повторяющиеся имена пользователей.
update_online_list Обновить список онлайн-пользователей клиента
online_notice Отправить уведомление о новом клиенте онлайн всем онлайн-клиентам
offline_notice Отправлять уведомление пользователя в автономном режиме всем онлайн-пользователям
handle_login Обработка запросов на вход
handle_reg Обработка запросов на регистрацию клиентов
handle_msg Транслировать то, что клиент хочет отправить
handle Основной фрейм, на котором работает сервер

Код ключа следующий, отвечающий за распределение запросов:

def handle(new_socket, addr):
    """
    服务器运行的主框架
    :param new_socket: 本次连接的客户端套接字
    :param addr: 本次连接客户端的ip和port
    """
    try:
        while True:
            req_type = new_socket.recv(1).decode("utf-8")  # 获取请求类型
            print(req_type)
            if req_type:  # 如果不为真,则说明客户端已断开
                if req_type == "1":  # 登录请求
                    print("开始处理登录请求")
                    handle_login(new_socket)
                elif req_type == "2":  # 注册请求
                    print("开始处理注册请求")
                    handle_reg(new_socket)
                elif req_type == "3":  # 发送消息
                    print("开始处理发送消息请求")
                    handle_msg(new_socket)
            else:
                break
    except Exception as ret:
        print(str(addr) + " 连接异常,准备断开: " + str(ret))
    finally:
        try:
            # 客户端断开后执行的操作
            new_socket.close()
            online_socket.remove(new_socket)
            offline_notice(new_socket)
            socket2user.pop(new_socket)
            time.sleep(4)
            update_online_list()
        except Exception as ret:
            print(str(addr) + "连接关闭异常")


Введение в функцию кэширования

Содержимое каждого чата и пользователя хранится в массиве через структуру, а затем распространяется пользователю, когда пользователь входит в систему.

Функция передачи файлов безопасности

Метод запуска

  • Запустите сервер:
python server_ssl.py   
python server_no_ssl.py
  • Запустите клиент:
python main.py

Папка Описание

  • cer — в этой папке хранится корневой сертификат ЦС, а также сертификаты сервера и клиента (созданные с использованием OpenSSL).
    • CA -- корневой сертификат и ключ
    • server -- ключ сервера, подписанный сертификат и подписанный сертификат
    • client -- секретный ключ клиента, подписанный сертификат и подписанный сертификат
  • ClientCache — в этом каталоге хранятся данные списка загрузки, которые запрашивают обновления с сервера.
  • ClientDownload -- путь загрузки клиента
  • ServerRec -- путь загрузки сервера

описание файла

  • файл запуска клиента main.py
  • client_login.py интерфейс входа клиента
  • client_mian.py основной интерфейс клиента
  • вид основного интерфейса клиента view.py
  • client_socket_no_ssl.py Клиент не шифрует объекты связи
  • client_socket_ssl.py объект зашифрованной связи клиента
  • Сервер server_no_ssl.py не шифрует код связи
  • Шифрование сервера server_ssl.py не шифрует код связи
  • result.txt используется для записи списка загрузки сервера
  • Serverlog.txt Журнал сервера

блок-схема

Серверный процесс выглядит следующим образом:image.png

Схема работы клиента выглядит следующим образом:image.png

Пользовательский транспортный протокол

Пользовательский заголовок добавляется к каждому обмену данными между сервером и клиентом.Клиент активно запрашивает данные с сервера, а сервер возвращает пассивно, поэтому заголовки пакетов у них будут разными. Эта система использует структурную структуру Python для реализации передачи двоичного потока заголовка.

Содержимое заголовка клиента следующее: (1024 байта)

  • Команда включает в себя: Скачать, Загрузить, Обновить, Войти и Зарегистрироваться.
  • имя_файла — это имя файла, загружаемого командой загрузки, и путь к локально загруженному файлу в режиме загрузки.
  • png это размер файла
  • время - время запроса данных
  • Пользователь и пароль — это имя пользователя и пароль, которые будут проверяться каждый раз при запросе данных, имитируя режим Cookie.
header = {
            'Command': 'Download',
            'fileName': filename,
            'fileSize': '',
            'time': time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
            'user': self.username,
            'password': self.password,
        }

Использование структуры в python выглядит следующим образом

header_hex = bytes(json.dumps(header).encode('utf-8'))
fhead = struct.pack('1024s', header_hex)
self.ssock.send(fhead)

Содержимое заголовка сервера выглядит следующим образом: (128 байт)

Поскольку сервер пассивно отвечает клиенту, содержимого заголовка не должно быть много, поэтому используется 128 байт.

  • Обратная связь указывает на команду, на которую следует реагировать.
  • Stat показывает статус ответа (например, зарегистрирован, вошел в систему и т. д.)
  • Размер файла - это размер файла
  • Пользователь является текущим пользователем
header = {
                'Feedback': 'Login',
                'stat': 'Success',
                'fileSize': os.stat(listResult).st_size,
                'user': username
             }

ключевой код

Ответственный за распределение запросов:

    def conn_thread(self,connection):
        while True:
            try:
                connection.settimeout(60)
                fileinfo_size = struct.calcsize('1024s')
                buf = connection.recv(fileinfo_size)
                if buf:  # 如果不加这个if,第一个文件传输完成后会自动走到下一句
                    header_json = str(struct.unpack('1024s', buf)[0], encoding='utf-8').strip('\00')
                    #print(header_json)
                    header = json.loads(header_json)
                    Command = header['Command']

                    if Command == 'Upload':
                        fileName = header['fileName']
                        fileSize = header['fileSize']
                        time = header['time']
                        user = header['user']
                        filenewname = os.path.join('ServerRec/', fileName)
                        print('Upload: file new name is %s, filesize is %s' % (filenewname, fileSize))
                        recvd_size = 0  # 定义接收了的文件大小
                        file = open(filenewname, 'wb')
                        print('start receiving...')
                        while not recvd_size == fileSize:
                            if fileSize - recvd_size > 1024:
                                rdata = connection.recv(1024)
                                recvd_size += len(rdata)
                            else:
                                rdata = connection.recv(fileSize - recvd_size)
                                recvd_size = fileSize
                            file.write(rdata)
                        file.close()
                        print('receive done')

                        fileSize = float(fileSize)
                        if fileSize<1024.0:
                            fileSize = "%s bytes"%(int(fileSize))
                        elif fileSize/1024.0 <= 1024.0:
                            fileSize = "%.2f Kb"%(fileSize/1024.0)
                        elif fileSize/1024.0/1024.0 <= 1024.0:
                            fileSize = "%.2f Mb"%(fileSize/1024.0/1024.0)
                        else:
                            fileSize = "%.2f Gb"%(fileSize/1024.0/1024.0/1024.0)

                        uploadmsg = '{"文件名": "%s", "上传者": "%s", "上传时间": "%s", "大小": "%s"}\n'%\
                                    (fileName,user,time,fileSize)
                        with open('result.txt', 'a', encoding='utf-8') as list:
                            list.write(uploadmsg)

                        uploadlog = '\n%s upload a file "%s" at %s' % \
                                        (user, fileName, time)
                        with open('Serverlog.txt', 'a', encoding='utf-8') as list:
                            list.write(uploadlog)
                        #connection.close()

                    elif Command == 'Login':
                        # 查询数据表数据
                        username = header['user']
                        password = header['password']
                        time = header['time']
                        sql = "select * from user where username = '%s' and password = '%s'"%(username,password)
                        cursor.execute(sql)
                        data = cursor.fetchone()
                        if data:
                            listResult = './result.txt'
                            # 定义文件头信息,包含文件名和文件大小
                            header = {
                                'Feedback': 'Login',
                                'stat': 'Success',
                                'fileSize': os.stat(listResult).st_size,
                                'user': username
                            }
                            header_hex = bytes(json.dumps(header).encode('utf-8'))
                            fhead = struct.pack('128s', header_hex)
                            connection.send(fhead)

                            fo = open(listResult, 'rb')
                            while True:
                                filedata = fo.read(1024)
                                if not filedata:
                                    break
                                connection.send(filedata)
                            fo.close()
                            print('%s login successfully')

                            loginlog = '\n%s try to login at "%s" , Stat: Success ' % \
                                        (username, time)
                            with open('Serverlog.txt', 'a', encoding='utf-8') as list:
                                list.write(loginlog)
                            #connection.close()
                        else:
                            header = {
                                'Feedback': 'Login',
                                'stat': 'Fail',
                                'fileSize': '',
                                'user': username
                            }
                            header_hex = bytes(json.dumps(header).encode('utf-8'))
                            fhead = struct.pack('128s', header_hex)
                            connection.send(fhead)
                            loginlog = '\n%s try to login at "%s" , Stat: Fail ' % \
                                       (username, time)
                            with open('Serverlog.txt', 'a', encoding='utf-8') as list:
                                list.write(loginlog)

                    elif Command == 'Download':
                        # 查询数据表数据
                        username = header['user']
                        password = header['password']
                        time = header['time']
                        sql = "select * from user where username = '%s' and password = '%s'" % (username, password)
                        cursor.execute(sql)
                        data = cursor.fetchone()
                        filename = header['fileName']
                        if data:

                            filepath = os.path.join('./ServerREc/', filename)
                            # 定义文件头信息,包含文件名和文件大小
                            header = {
                                'Feedback': 'Download',
                                'stat': 'Success',
                                'fileSize': os.stat(filepath).st_size,
                                'user': username
                            }
                            header_hex = bytes(json.dumps(header).encode('utf-8'))
                            fhead = struct.pack('128s', header_hex)
                            connection.send(fhead)

                            fo = open(filepath, 'rb')
                            while True:
                                filedata = fo.read(1024)
                                if not filedata:
                                    break
                                connection.send(filedata)
                            fo.close()
                            print('send file over...')
                            downloadlog = '\n%s download a file "%s" at %s' % \
                                          (username, filename, time)
                            with open('Serverlog.txt', 'a', encoding='utf-8') as list:
                                list.write(downloadlog)
                            # connection.close()
                        else:
                            header = {
                                'Feedback': 'Download',
                                'stat': 'LoginFail',
                                'fileSize': '',
                                'user': username
                            }
                            header_hex = bytes(json.dumps(header).encode('utf-8'))
                            fhead = struct.pack('128s', header_hex)
                            connection.send(fhead)

                    elif Command == 'Update':
                        # 查询数据表数据
                        username = header['user']
                        password = header['password']
                        sql = "select * from user where username = '%s' and password = '%s'" % (username, password)
                        cursor.execute(sql)
                        data = cursor.fetchone()
                        if data:
                            listResult = './result.txt'
                            # 定义文件头信息,包含文件名和文件大小
                            header = {
                                'Feedback': 'Update',
                                'stat': 'Success',
                                'fileSize': os.stat(listResult).st_size,
                                'user': username
                            }
                            header_hex = bytes(json.dumps(header).encode('utf-8'))
                            fhead = struct.pack('128s', header_hex)
                            connection.send(fhead)

                            fo = open(listResult, 'rb')
                            while True:
                                filedata = fo.read(1024)
                                if not filedata:
                                    break
                                connection.send(filedata)
                            fo.close()
                            #print('send list over...')
                            # connection.close()
                        else:
                            header = {
                                'Feedback': 'Login',
                                'stat': 'Fail',
                                'fileSize': '',
                                'user': username
                            }
                            header_hex = bytes(json.dumps(header).encode('utf-8'))
                            fhead = struct.pack('128s', header_hex)
                            connection.send(fhead)

                    elif Command == 'Register':
                        # 查询数据表数据
                        username = header['user']
                        password = header['password']
                        time = header['time']
                        sql = "select * from user where username = '%s'" % (username)
                        cursor.execute(sql)
                        data = cursor.fetchone()
                        if data:
                            # 定义文件头信息,包含文件名和文件大小
                            header = {
                                'Feedback': 'Register',
                                'stat': 'Exist',
                                'fileSize': '',
                                'user': username
                            }
                            header_hex = bytes(json.dumps(header).encode('utf-8'))
                            fhead = struct.pack('128s', header_hex)
                            connection.send(fhead)
                            loginlog = '\n%s try to register at "%s" , Stat: Fail ' % \
                                       (username, time)
                            with open('Serverlog.txt', 'a', encoding='utf-8') as list:
                                list.write(loginlog)
                        else:
                            sql = "insert into user values ('','%s','%s')"%(username,password)
                            cursor.execute(sql)
                            db.commit()
                            # 定义文件头信息,包含文件名和文件大小
                            header = {
                                'Feedback': 'Register',
                                'stat': 'Success',
                                'fileSize': '',
                                'user': username
                            }
                            header_hex = bytes(json.dumps(header).encode('utf-8'))
                            fhead = struct.pack('128s', header_hex)
                            connection.send(fhead)
                            loginlog = '\n%s try to register at "%s" , Stat: Success ' % \
                                       (username, time)
                            with open('Serverlog.txt', 'a', encoding='utf-8') as list:
                                list.write(loginlog)

            except socket.timeout:
                connection.close()
                break
            except ConnectionResetError:
                connection.close()
                break

визуализация

Отображение списка файлов выглядит следующим образом: image.png

Рендеринг загрузки файла выглядит следующим образом: image.png

столкнуться с проблемами

  • нет графического интерфейса: использование набора инструментов для программирования Python с графическим интерфейсом (Tkinter).
  • Сертификат не будет создан:команда openssl
  • hostname match error:context.wrap_socket(self.sock, server_hostname='SERVER', server_side=False)Имя_хоста_сервера в имени_хоста_сервера должно соответствовать CN в server.conf.

Экспериментальный урожай и опыт

Этот эксперимент многому меня научил и дал более глубокое понимание зашифрованной передачи SSL и протокола TCP. Раньше я читал знания о протоколе TCP в книге, но на этот раз я проверил трехстороннее рукопожатие TCP, номер ACK и т. д., перехватив пакеты самостоятельно, что сделало меня более заинтересованным в знаниях о компьютерных сетях. Кроме того, в этом эксперименте я сам разработал протокол, подал заявку на получение SSL-сертификата, реализовал графический интерфейс и т. д. Я никогда раньше не пробовал его. углубился. Проблемы, возникшие в этом эксперименте, в основном связаны с дизайном графического интерфейса и применением сертификата SSL. Так как я мало что знал об этом заранее, было сложно разобраться во время разработки.Позднее, после большого чтения материалов, просмотра блогов и изучения чужих кодов, я, наконец, решил трудности одну за другой и успешно реализовал все функции. Этот эксперимент развил мою способность мыслить и решать проблемы, а также расширил мой собственный опыт, что очень обогащало меня.