摘要:一般http中存在請求資訊明文傳輸,容易被竊聽擷取;資料的完整性未校驗,容易被篡改;沒有驗證對方身份,存在冒充危險。面對這些問題,怎麼破?
一、為什麼要用https
一般http中存在如下問題:請求資訊明文傳輸,容易被竊聽擷取;資料的完整性未校驗,容易被篡改;沒有驗證對方身份,存在冒充危險。
HTTPS又有哪些優勢?
1、使用HTTPS可認證使用者和伺服器,確保資料傳送到正確的客戶機和伺服器。
2、HTTPS是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網路協議,要比http協議安全,可防止資料在傳輸過程中不被竊取、改變,確保資料的完整性。
3、HTTPS是現行架構下最安全的解決方案,雖然不是絕對安全,但它大幅增加了中間人攻擊的成本。
二、https中的證照是什麼
CA:證照授權中心( certificate authority)類似於國家出入境管理處一樣,給別人頒發護照;也類似於國家工商管理局一樣,給公司企業頒發營業執照。它有兩大主要性質:
- CA本身是受信任的,國際認可的;
- 給他受信任的申請物件頒發證照。 和辦理護照一樣,要確定你的合法身份,你不能是犯罪分子或造反派。當然,你需要被收保護費,同時,CA可以隨時吊銷你的證照。
CA 的證照 ca.crt 和 SSL Server的證照 server.crt 是什麼關係呢?
- SSL Server 自己生成一個 私鑰/公鑰對,server.key/server.pub 。server.pub 生成一個請求檔案 server.req。 請求檔案中包含有 server 的一些資訊,如域名/申請者/公鑰等。
- server 將請求檔案 server.req 遞交給 CA,CA驗明正身後,將用 ca.key和請求檔案加密生成 server.crt由於 ca.key 和 ca.crt 是一對, 於是 ca.crt 可以解密 server.crt。
- 在實際應用中:如果 SSL Client 想要校驗 SSL server.那麼 SSL server 必須要將他的證照 server.crt 傳給 client.然後 client 用 ca.crt 去校驗 server.crt 的合法性。如果是一個釣魚網站,那麼CA是不會給他頒發合法server.crt證照的,這樣client 用ca.crt去校驗,就會失敗。
三、生成證照與金鑰
shell指令碼
#!/bin/bash PROJECT_NAME="https 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 = EOF cat > client_cert.conf << EOF [ req ] distinguished_name = req_distinguished_name prompt = no [ req_distinguished_name ] O = $PROJECT_NAME Device Certificate CN = EOF mkdir ca mkdir server mkdir client # 生成私鑰 openssl genrsa -out ca.key 1024 openssl genrsa -out server.key 1024 openssl genrsa -out client.key 1024 # 根據私鑰建立證照請求檔案,需要輸入一些證照的元資訊:郵箱、域名等 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 # 結合私鑰和請求檔案,建立自簽署證照 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/ rm *.conf rm *.req rm *.srl
一些命令的解釋
openssl genrsa [-out filename] [-passout arg] [-des] [-des3] [-idea] [numbits] 選項說明: -out filename:將生成的私鑰儲存至filename檔案,若未指定輸出檔案,則為標準輸出。 -numbits:指定要生成的私鑰的長度,預設為1024。該項必須為命令列的最後一項引數。 -des|-des3|-idea:指定加密私鑰檔案用的演算法,這樣每次使用私鑰檔案都將輸入密碼,太麻煩所以很少使用。 -passout args:加密私鑰檔案時,傳遞密碼的格式,如果要加密私鑰檔案時單未指定該項,則提示輸入密碼。傳遞密碼的args的格式 openssl req -out ca.req -key ca.key -new -config ./ca_cert.conf 主要命令選項: -new :說明生成證照請求檔案 -key :指定已有的祕鑰檔案生成祕鑰請求,只與生成證照請求選項-new 配合。 -out :指定生成的證照請求或者自簽名證照名稱 openssl x509 -req -in ca.req -out ca.crt -sha1 -days 5000 -signkey ca.key openssl x509命令具以下的一些功能,例如輸出證照資訊,簽署證照請求檔案、生成自簽名證照、轉換證照格式等。 -in filename:指定證照輸入檔案,若同時指定了"-req"選項,則表示輸入檔案為證照請求檔案,再使用"-signkey"提供自簽署時使用的私鑰。 -out filename:指定輸出檔案 -days: 指定證照的有效時間長短。預設為30天
四、安裝flask
需要安裝python 的 openssl 的類庫,使用pip 安裝
pip install pyOpenSSL
五、https單向認證握手流程
python實現
server端:
from flask import Flask app = Flask(__name__) @app.route('/login') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run(host="0.0.0.0", port=8091, ssl_context=('server.crt', 'server.key'))
客戶端:
import urllib.request import ssl if __name__ == '__main__': CA_FILE = "ca.crt" context = ssl.SSLContext(ssl.PROTOCOL_TLS) context.check_hostname = False context.load_verify_locations(CA_FILE) context.verify_mode = ssl.CERT_REQUIRED try: request = urllib.request.Request('https://127.0.0.1:8091/login') res = urllib.request.urlopen(request, context=context) print(res.code) print(res.read().decode("utf-8")) except Exception as ex: print("Found Error in auth phase:%s" % str(ex))
六、https雙向認證握手流程
python實現
客戶端:
from flask import Flask, request, Response import json app = Flask(__name__) @app.route("/login") def hello(): return "Hello World!" @app.route('/login1', methods=['POST']) def login(): username = request.form.get("username") password = request.form.get("password") login_config = { "name": "pwd1" } if username in login_config.keys(): if password == login_config[username]: return Response(json.dumps(True), status=200, mimetype='application/json') return Response(json.dumps(False), status=200, mimetype='application/json') if __name__ == "__main__": app.run(host="0.0.0.0", port=8091, ssl_context=('server/server.crt', 'server/server.key'))
客戶端:
import urllib.request import ssl if __name__ == '__main__': CA_FILE = "ca.crt" context = ssl.SSLContext(ssl.PROTOCOL_TLS) context.check_hostname = False context.load_verify_locations(CA_FILE) context.verify_mode = ssl.CERT_REQUIRED dict = { "username": "name", "password": "pwd1", } data = urllib.parse.urlencode(dict).encode('utf-8') try: request = urllib.request.Request('https://127.0.0.1:8091/login') res = urllib.request.urlopen(request, context=context) print(res.code) print(res.read().decode("utf-8")) except Exception as ex: print("Found Error in auth phase:%s" % str(ex)) try: request = urllib.request.Request('https://127.0.0.1:8091/login1', data=data, method='POST') res = urllib.request.urlopen(request, context=context) print(res.code) print(res.read().decode("utf-8")) except Exception as ex: print("Found Error in auth phase:%s" % str(ex))
參考連結
openssl 命令(1): openssl req 命令詳解
openssl 命令(3): openssl x509命令詳解
本文分享自華為雲社群《https如何使用python+flask來實現》,原文作者:SNHer 。