Apache Thrift - 安全注意事項



使用 Apache Thrift 構建分散式系統時,務必關注安全,以保護您的資料並確保服務間的通訊安全和私密。

本教程將涵蓋關鍵的安全方面,例如如何驗證使用者、控制訪問、加密資料以及遵循最佳實踐以確保一切安全。

身份驗證

身份驗證確保與 Thrift 服務互動的實體(客戶端和伺服器)確實是它們聲稱的身份。這是保護通訊和敏感資料的重要步驟。

以下是不同型別的身份驗證:

  • 基本身份驗證
  • 基於令牌的身份驗證
  • 雙向 TLS (mTLS)

基本身份驗證

基本身份驗證要求使用者提供使用者名稱和密碼才能訪問服務。雖然它簡單易於實現,但它本身並不安全,因為憑據通常以明文形式傳送。

基於令牌的身份驗證

在這種方法中,客戶端在登入後會收到一個令牌,例如 JSON Web 令牌 (JWT)。然後使用此令牌訪問服務。

令牌可以包含過期時間和範圍,與基本身份驗證相比,這種方法更安全、更靈活。

雙向 TLS (mTLS)

雙向 TLS 透過要求客戶端和伺服器都向彼此呈現證書來增強安全性。這種雙向身份驗證過程確保雙方都得到驗證,為通訊提供高水平的安全性。

實現基於令牌的身份驗證

基於令牌的身份驗證透過使用令牌(例如 JWT(JSON Web 令牌))來驗證使用者或系統的身份來增強安全性。

使用 JWT 的示例

以下是關於如何在 Thrift 中實現基於令牌的身份驗證的分步指南:

生成令牌:您生成一個包含使用者資訊和過期時間的令牌。此令牌使用金鑰簽名以防止篡改:

import jwt
import datetime

def generate_token(secret_key):
   payload = {
      'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1),  # Token expires in 1 hour
      'iat': datetime.datetime.utcnow(),  # Issued at current time
      'sub': 'user_id'  # Subject of the token, e.g., user ID
   }
   return jwt.encode(payload, secret_key, algorithm='HS256')  # Encode the token with HS256 algorithm

身份驗證請求:當請求到達時,您檢查請求標頭中提供的令牌。如果令牌有效且未過期,則允許請求;否則,請求將被拒絕:

from thrift.protocol import TBinaryProtocol
from thrift.transport import TTransport
from flask import Flask, request, jsonify

app = Flask(__name__)
secret_key = 'your_secret_key'  # Secret key used for encoding and decoding tokens

def decode_token(token):
   try:
      payload = jwt.decode(token, secret_key, algorithms=['HS256'])  # Decode token using the secret key
      return payload
   except jwt.ExpiredSignatureError:
      return None  # Return None if the token has expired

@app.route('/some_endpoint', methods=['GET'])
def some_endpoint():
   token = request.headers.get('Authorization')  # Get the token from request headers
   if decode_token(token):
      return jsonify({'message': 'Authenticated'}), 200  # Return success message if token is valid
   else:
      return jsonify({'message': 'Unauthorized'}), 401  # Return error message if token is invalid or expired

授權

授權是關於確定使用者或服務在經過身份驗證後可以執行哪些操作。它確保個人或系統只能訪問或修改他們被允許訪問或修改的資源,這取決於他們的角色或屬性。

基於角色的訪問控制

基於角色的訪問控制 (RBAC) 根據使用者在組織中的角色為使用者分配許可權。每個角色都有一組與其關聯的特定許可權,使用者被分配到這些角色。

此方法透過將許可權分組到角色並將這些角色分配給使用者來簡化許可權管理。

  • 定義角色和許可權:您定義不同的角色(例如,管理員、使用者)並指定每個角色可以執行的操作(例如,讀取、寫入、刪除):
  • roles_permissions = {
       'admin': ['read', 'write', 'delete'],
       'user': ['read']
    }
    
  • 檢查許可權:在允許操作之前,您需要檢查使用者的角色是否具有所需的許可權:
  • def check_permission(role, permission):
       if permission in roles_permissions.get(role, []):
          return True
       return False
    
    @app.route('/delete_resource', methods=['POST'])
    def delete_resource():
       role = get_user_role()  # Assume this function retrieves the user's role
       if check_permission(role, 'delete'):
          # Perform delete operation
          return jsonify({'message': 'Resource deleted'}), 200
       else:
          return jsonify({'message': 'Forbidden'}), 403
    

基於屬性的訪問控制

基於屬性的訪問控制 (ABAC) 根據各種屬性授予或限制訪問許可權,例如使用者的角色、資源的屬性或當前的環境條件。

此方法透過考慮多個因素,與 RBAC 相比,提供了更精確的控制。

  • 定義屬性和策略:建立根據屬性(例如使用者角色或資源所有者)確定訪問許可權的規則:
  • def can_access(user_role, resource_owner):
       return user_role == 'admin' or (user_role == 'user' and resource_owner == 'user')
    
  • 執行策略:在您的應用程式中實現檢查以確保遵循策略:
  • @app.route('/access_resource', methods=['GET'])
    def access_resource():
       user_role = get_user_role()
       resource_owner = get_resource_owner()
       if can_access(user_role, resource_owner):
          # Access resource
          return jsonify({'message': 'Resource accessed'}), 200
       else:
          return jsonify({'message': 'Forbidden'}), 403
    

加密

加密是保護資料的重要流程,使未經授權的使用者無法讀取資料。它在資料透過網路傳輸時和儲存在磁碟上時都保護資料。

資料傳輸加密

傳輸加密確保在客戶端和伺服器之間傳送的資料受到保護,防止竊聽或篡改。這是透過在資料透過網路移動時對其進行加密來實現的。

使用 TLS 進行安全通訊:TLS(傳輸層安全)是一種在傳輸過程中加密資料的協議,確保客戶端和伺服器之間的安全通訊:

在 Thrift 伺服器上啟用 TLS:您需要透過提供伺服器的證書和金鑰來配置 Thrift 伺服器以使用 TLS。此設定會在資料從客戶端傳送到伺服器時對其進行加密:

from thrift.server import TServer
from thrift.transport import TSSLTransport

handler = MyHandler()
processor = MyService.Processor(handler)

# Setup TLS
server_transport = TSSLTransport.TSSLServerSocket('localhost', 9090, 'server_cert.pem', 'server_key.pem')
transport_factory = TTransport.TBufferedTransportFactory()
protocol_factory = TBinaryProtocol.TBinaryProtocolFactory()

server = TServer.TSimpleServer(processor, server_transport, transport_factory, protocol_factory)
server.serve()

在 Thrift 客戶端上啟用 TLS:同樣,配置 Thrift 客戶端以使用 TLS,以確保從伺服器接收的資料已加密且安全:

from thrift.transport import TSSLTransport

# Setup TLS
transport = TSSLTransport.TSSLSocket('localhost', 9090, validate=False, ca_certs='ca_cert.pem')
protocol = TBinaryProtocol.TBinaryProtocol(transport)

資料儲存加密

儲存加密保護儲存在磁碟上的資料。即使有人獲得了對您的儲存的物理訪問許可權,加密的資料仍然安全且無法訪問,除非擁有正確的解密金鑰。

使用 AES 加密的示例

  • 加密資料:使用高階加密標準 (AES) 在儲存資料之前對其進行加密。這涉及使用金鑰將資料轉換為不可讀的格式:
  • from Crypto.Cipher import AES
    from Crypto.Util.Padding import pad
    
    def encrypt_data(data, key):
       cipher = AES.new(key, AES.MODE_CBC)
       ciphertext = cipher.encrypt(pad(data, AES.block_size))
       return cipher.iv + ciphertext
    

    這裡,cipher.iv 是有助於加密的初始化向量,ciphertext 是加密後的資料。

  • 解密資料:要讀取加密的資料,您需要使用加密期間使用的相同金鑰和初始化向量對其進行解密:
  • from Crypto.Cipher import AES
    from Crypto.Util.Padding import unpad
    
    def decrypt_data(encrypted_data, key):
       iv = encrypted_data[:AES.block_size]
       ciphertext = encrypted_data[AES.block_size:]
       cipher = AES.new(key, AES.MODE_CBC, iv=iv)
       return unpad(cipher.decrypt(ciphertext), AES.block_size)
    

    此函式從加密資料中提取初始化向量,解密密文並刪除加密期間新增的填充。

廣告
© . All rights reserved.