Làm cách nào để kết nối với máy chủ sftp trong python?

Trường hợp sử dụng điển hình cho ứng dụng Python được nối mạng có thể liên quan đến nhu cầu sao chép tệp từ xa xuống máy tính đang chạy tập lệnh hoặc tạo tệp mới và chuyển tệp đó đến máy chủ từ xa. Thông thường, điều này được thực hiện thông qua việc sử dụng SFTP [Giao thức truyền tệp an toàn]. Trong phần thứ hai của loạt bài gồm ba phần về lập trình mạng bằng Python, chúng ta sẽ xem xét các cách để làm việc với Python, SFTP, SSH và ổ cắm

Bạn có thể đọc phần đầu tiên của loạt bài này bằng cách truy cập hướng dẫn của chúng tôi. Python và các hoạt động mạng cơ bản

Trước khi bắt đầu mã hóa, điều quan trọng là phải nhấn mạnh chính xác SFTP đảm bảo rằng việc truyền tệp được an toàn như thế nào. Một điều quan trọng mà quy trình SFTP thực hiện là xác thực cái được gọi là Dấu vân tay SSH của Máy chủ SFTP từ xa. Dấu vân tay SSH là duy nhất cho một máy chủ SFTP nhất định. Lưu ý rằng, Khóa công khai của máy chủ SFTP KHÔNG giống với Khóa công khai SSH được lưu trữ trên máy chủ từ xa được sử dụng để xác thực mà không cần mật khẩu

Việc xác minh Dấu vân tay SSH là rất quan trọng để đảm bảo rằng Máy chủ SFTP thực sự là Máy chủ SFTP mà người dùng từ xa nghĩ rằng đó là. Nếu một lần thử kết nối SFTP báo cáo rằng Dấu vân tay SSH đã thay đổi, điều đó có thể có nghĩa là một trong số những điều sau

  • Người vận hành Máy chủ SFTP đã nâng cấp hoặc thực hiện một số thay đổi về cấu hình đối với Máy chủ SFTP
  • Người dùng độc hại đang cố mạo danh máy chủ từ xa, rất có thể nhằm mục đích thu thập thông tin đăng nhập. Đây là một ví dụ về cuộc tấn công “Man in the Middle”

Trong cả hai trường hợp, điều bắt buộc là bất kỳ mã nào tự động hóa quy trình SFTP phải xác minh rằng Dấu vân tay SSH thực sự hợp lệ và nếu nó không hợp lệ, phải dừng ngay lập tức mọi nỗ lực kết nối cho đến khi xác minh đúng danh tính của máy chủ. SFTP, giống như tất cả các giao thức khác trong bộ SSH, hoạt động trên Cổng TCP 22 theo mặc định. Tất cả các ví dụ trong bài viết này sẽ tuân theo quy ước này. Nếu cần một cổng khác, thì cổng đó phải được chỉ định thay cho Cổng TCP 22

Lấy dấu vân tay SSH ban đầu bằng SFTP

Cách dễ nhất để lấy bản sao Dấu vân tay SSH của Máy chủ SFTP là kết nối với máy khách SFTP có sẵn miễn phí hoặc với các công cụ OpenSSH được cung cấp cho cả Linux và Windows 10 Professional

Linux

Từ một thiết bị đầu cuối, chỉ cần gọi trực tiếp lệnh sftp. Nếu Dấu vân tay SSH không được biết hoặc nếu nó đã bị thay đổi, lệnh sẽ nhắc người dùng thực hiện điều đó. Lệnh sau sẽ truy vấn máy chủ từ xa để truy xuất Dấu vân tay SSH của nó trước khi thử kết nối. Sau khi kết nối được xác nhận theo bên dưới, người dùng sẽ được nhắc nhập mật khẩu cho tài khoản được chỉ định

$ sftp sftp://[email protected]

Nhận dấu vân tay SSH trong Linux, với dấu vân tay và thuật toán được tô sáng

Việc xác nhận thêm dấu vân tay sẽ lưu dấu vân tay đó vào thư mục ~/. ssh/known_hosts, dành riêng cho từng tài khoản người dùng cá nhân trên hệ thống Linux. Trong hình trên, hàm băm của Dấu vân tay SSH, cũng như thuật toán băm được sử dụng [sha-256] và sơ đồ mã hóa hoặc chữ ký được sử dụng [Ed25519] đều được đánh dấu bằng các hình chữ nhật màu đỏ

Code Python cho Linux dưới đây sẽ bị lỗi nếu không trả lời “có” cho câu hỏi muốn tiếp tục kết nối. Điều quan trọng là bản ghi máy chủ được thêm vào ~/. ssh/known_hosts và đó là một phương pháp bảo mật tốt để đảm bảo rằng bất kỳ mã chương trình nào sử dụng SFTP đều bị giới hạn đối với các máy chủ đã được người dùng cuối thêm vào

Mặc dù thông tin này có thể quan trọng trong các ứng dụng khác, nhưng mô-đun Python sẽ được giới thiệu trong các minh họa này quan tâm nhiều hơn đến giá trị của chính dấu vân tay. Chúng có thể được liệt kê bằng cách kết xuất nội dung của ~/. tập tin ssh/known_hosts

Tệp known_hosts mẫu

Trong hình trên, thuật toán mã hóa hoặc chữ ký tương ứng với hàm băm được tô sáng. Lưu ý rằng trong bản phân phối Linux và triển khai OpenSSH cụ thể này, bản thân máy chủ, là giá trị ở phía ngoài cùng bên trái của mỗi dòng, được băm

các cửa sổ

Windows 10 cung cấp triển khai OpenSSH chính thức có thể được sử dụng để truy xuất Dấu vân tay SSH của Máy chủ SFTP từ xa. Trước khi tiếp tục, hãy làm theo hướng dẫn tại Bắt đầu với OpenSSH và xác minh rằng ở mức tối thiểu, Phần bổ trợ Windows “OpenSSH Client” đã được cài đặt trong Windows. Sau khi nó được cài đặt, một tệp tương tự như ~/. tệp ssh/known_hosts có thể được tạo bằng lệnh

C…> ssh-keyscan my-sftp-host-or-ip > known_hosts.txt

các_hosts đã biết. txt sẽ trông giống như sau. Lưu ý cách thức, trong tình huống này, Địa chỉ IP [hoặc tên máy chủ] không được băm

Tập tin known_hosts doppelganger

Như trường hợp của Linux ~/. ssh/known_hosts, thuật toán chữ ký được liệt kê với Dấu vân tay SSH, nhưng trong quá trình triển khai OpenSSH của Windows, các mục máy chủ không được băm

Bây giờ, Dấu vân tay SSH của máy chủ từ xa được đề cập đã được biết, đã đến lúc chuyển sang mã. Các ví dụ về máy chủ ở đây đã sử dụng thuật toán chữ ký ssh-ed25519 và hàm băm SHA-256. Các máy chủ khác có thể sử dụng các sơ đồ mã hóa khác nhau và mã sẽ cần được điều chỉnh cho phù hợp

Đọc. Các khóa học trực tuyến hàng đầu để học Python

Mô-đun Paramiko

Paramiko là một mô-đun Python triển khai SSHv2. Các minh họa trong hướng dẫn Python này sẽ tập trung hoàn toàn vào kết nối SFTP và cách sử dụng SFTP cơ bản. Ví dụ dưới đây được chạy trên Ubuntu 22. 04 LTS với Python phiên bản 3. 10. 4. Trong hệ thống này, lệnh python3 phải được sử dụng rõ ràng để gọi Python 3. Do đó, lệnh pip được liên kết với hệ thống này là pip3. Các hệ thống khác có thể đặt bí danh lệnh python để gọi Python 3. Trong những tình huống này, lệnh sẽ là pip

Để cài đặt mô-đun Paramiko, hãy sử dụng lệnh

$ pip3 install paramiko

Linux

Mã bên dưới kết nối từ Linux và lấy khóa Máy chủ từ ~/. tập tin ssh/known_hosts. Mã xác minh rằng dấu vân tay SSH khớp trước khi cho phép kết nối

# demo-sftp.py

import paramiko
import sys

def main[argv]:
  hostkeys = paramiko.hostkeys.HostKeys [filename="/home/phil/.ssh/known_hosts"]
  # The host fingerprint is stored using the ed25519 algorithm. This was revealed
  # when the host was initially connected to from the sftp program invoked earlier.
  hostFingerprint = hostkeys.lookup ["my-sftp-host-or-ip"]['ssh-ed25519']    


  try:
    # Note that the parameters below represent a low-level Python Socket, and 
    # they must be represented as such.
    tp = paramiko.Transport["my-sftp-host-or-ip", 22]

    # Note that while you *can* connect without checking the hostkey, you really
    # shouldn't. Without checking the hostkey, a malicious actor can steal
    # your credentials by impersonating the server.
    tp.connect [username = "my-username", password="my-password", hostkey=hostFingerprint]
    try:
      sftpClient = paramiko.SFTPClient.from_transport[tp]
      fileCount = 0
      # Proof of concept - List First 10 Files
      for file in sftpClient.listdir[]:
        print [str[file]]
        fileCount = 1 + fileCount
        if 10 == fileCount:
          break
      sftpClient.close[]
    except Exception as err:
      print ["SFTP failed due to [" + str[err] + "]"]

    tp.close[]
  except paramiko.ssh_exception.AuthenticationException as err:
    print ["Can't connect due to authentication error [" + str[err] + "]"]
  except Exception as err:
    print ["Can't connect due to other error [" + str[err] + "]"]

if __name__ == "__main__":
  main[sys.argv[1:]]

Dưới đây là một mẫu của đầu ra

Đầu ra của Liệt kê 3

các cửa sổ

Lệnh tương tự có thể được sử dụng để cài đặt mô-đun Paramiko trong Windows

C…> pip3 install paramiko

Khi Paramiko được cài đặt trong Windows, hãy ghi lại known_hosts. txt đã tạo ở trên. Việc triển khai Windows bên dưới giả định rằng known_hosts. txt nằm trong cùng thư mục với mã Python

Mã tương tự từ trước có thể được điều chỉnh cho Windows

# demo-sftp-windows.py

import paramiko
import sys

def main[argv]:
  hostkeys = paramiko.hostkeys.HostKeys [filename="known_hosts.txt"]
  # The host fingerprint is stored using the ed25519 algorithm. This was revealed
  # when the host was initially connected to from the sftp program invoked earlier.
  hostFingerprint = hostkeys.lookup ["my-sftp-host-or-ip"]['ssh-ed25519']    


  try:
    # Note that the parameters below represent a low-level Python Socket, and 
    # they must be represented as such.
    tp = paramiko.Transport["my-sftp-host-or-ip", 22]

    # Note that while you *can* connect without checking the hostkey, you really
    # shouldn't. Without checking the hostkey, a malicious actor can steal
    # your credentials by impersonating the server.
    tp.connect [username = "my-username", password="my-password", hostkey=hostFingerprint]
    try:
      sftpClient = paramiko.SFTPClient.from_transport[tp]
      fileCount = 0
      # Proof of concept - List First 10 Files
      for file in sftpClient.listdir[]:
        print [str[file]]
        fileCount = 1 + fileCount
        if 10 == fileCount:
          break
      sftpClient.close[]
    except Exception as err:
      print ["SFTP failed due to [" + str[err] + "]"]

    tp.close[]
  except paramiko.ssh_exception.AuthenticationException as err:
    print ["Can't connect due to authentication error [" + str[err] + "]"]
  except Exception as err:
    print ["Can't connect due to other error [" + str[err] + "]"]

if __name__ == "__main__":
  main[sys.argv[1:]]

Tầm quan trọng của việc đóng cửa

Lưu ý cách trong cả hai danh sách mã, cả đối tượng sftpClient và đối tượng tp đều được đóng gần cuối khối nơi chúng được sử dụng. Điều này rất quan trọng vì một số hoạt động cơ bản nhất định có thể bị chặn nếu các đối tượng này không được đóng

Đọc. Công cụ tốt nhất cho nhà phát triển từ xa

Mô phỏng vấn đề bảo mật

Vì hướng dẫn Python này đã tạo ra một “vấn đề lớn” trong việc đảm bảo rằng Dấu vân tay SSH khớp với dấu vân tay được phát hiện ban đầu, nên có thể thú vị khi “mô phỏng” một cuộc tấn công mạo danh máy chủ có thể trông như thế nào. Để làm điều này, chỉ cần mở ~/. ssh/known_hosts và thực hiện thay đổi đối với khóa máy chủ cho hệ thống được kết nối trong Liệt kê 1

Cố tình làm hỏng dấu vân tay SSH trong Linux

Vì bản phân phối Linux này sử dụng hàm băm thay vì máy chủ cho các mục, nên sẽ phải suy ra rằng vì đây là mục duy nhất sử dụng thuật toán Ed25519 nên đây là mục cần được sửa đổi. Nhà phát triển có trách nhiệm đảm bảo rằng nếu loại thử nghiệm này là cần thiết, thì bản ghi máy chủ phù hợp sẽ được sửa đổi. Trong khi ví dụ trên tập trung vào một chữ cái, bất kỳ ký tự nào trên dòng đó ở bên phải của “ssh-ed25519” đều có thể được thay đổi

Bây giờ chạy lại mã trong Liệt kê 1 sẽ báo lỗi này

Lỗi chính xác do Dấu vân tay SSH không khớp

Nếu Dấu vân tay SSH đã bị thay đổi ở phía máy chủ do người dùng độc hại đang cố mạo danh máy chủ SSH, thì đây sẽ là một lỗi rất đáng hoan nghênh vì thông tin xác thực bảo mật không được truyền đi trong trường hợp Dấu vân tay SSH không khớp

Để khắc phục sự cố đã tạo ở trên, chỉ cần gọi lệnh ban đầu được sử dụng để khám phá Dấu vân tay SSH và làm theo hướng dẫn mà nó cung cấp

Sửa lỗi không khớp SSH Fingerprint được tạo có chủ ý

Khi các mục vi phạm bị xóa, chỉ cần thử lại SFTP vào máy chủ ban đầu theo các bước ban đầu ở trên để thêm lại Dấu vân tay SSH vào ~/. tập tin ssh/known_hosts

Vấn đề bảo mật tương tự có thể được mô phỏng trong Windows bằng cách thực hiện một thay đổi tương tự đối với known_hosts. txt đã tạo ở trên

Cố tình làm hỏng dấu vân tay SSH trong Windows

Và lỗi tương tự xuất hiện trong phiên bản Windows của mã. Để khắc phục sự cố trên, chỉ cần tạo lại known_hosts. txt theo các bước trên

Các tác vụ SFTP phổ biến với Python

Mô-đun Paramiko cung cấp bộ công cụ rất phong phú và mạnh mẽ cho các tác vụ SFTP đơn giản cũng như rất phức tạp. Phần này sẽ nêu bật một số tác vụ SFTP cơ bản và phổ biến hơn

Tải tệp lên bằng SFTP và Python

Phương thức put tải tệp lên Máy chủ SFTP, trong ngữ cảnh của kết nối SFTP mở hiện có

# demo-sftp-upload.py

import paramiko
import sys

def main[argv]:
	hostkeys = paramiko.hostkeys.HostKeys [filename="/home/phil/.ssh/known_hosts"]
	# The host fingerprint is stored using the ed25519 algorithm. This was revealed
	# when the host was initially connected to from the sftp program invoked earlier.
	hostFingerprint = hostkeys.lookup ["my-sftp-host-or-ip"]['ssh-ed25519']


	try:
		# Note that the parameters below represent a low-level Python Socket, and 
		# they must be represented as such.
		tp = paramiko.Transport["my-sftp-host-or-ip", 22]

		# Note that while you *can* connect without checking the hostkey, you really
		# shouldn't. Without checking the hostkey, a malicious actor can steal
		# your credentials by impersonating the server.
		tp.connect [username = "my-username", password="my-password", hostkey=hostFingerprint]

		# Use a dictionary object to create a list of files to upload, along with their remote paths.
		# Note that the first entry attempts to upload to a directory without write permissions.
		filesToUpload = {"./Wiring Up Close - Annotated.jpeg":"./no-upload-allowed/Wiring Up Close - Annotated.jpeg",
			"./lipsum.txt":"./lipsum.txt", 
			"./3 Separate LEDs - Full Diagram - Cropped.jpeg":"./3 Separate LEDs - Full Diagram - Cropped.jpeg"}


		sftpClient = paramiko.SFTPClient.from_transport[tp]
		for key, value in filesToUpload.items[]:
			try:
				sftpClient.put[key, value]
				print ["[" + key + "] successfully uploaded to [" + value + "]"]
			except PermissionError as err:
				print ["SFTP Operation Failed on [" + key + 
					"] due to a permissions error on the remote server [" + str[err] + "]"]
			except Exception as err:
				print ["SFTP failed due to other error [" + str[err] + "]"]

		# Make sure to close all created objects.
		sftpClient.close[]

		tp.close[]
	except paramiko.ssh_exception.AuthenticationException as err:
		print ["Can't connect due to authentication error [" + str[err] + "]"]
	except Exception as err:
		print ["Can't connect due to other error [" + str[err] + "]"]

if __name__ == "__main__":
	main[sys.argv[1:]]





Listing 3 - Uploading Files

Lưu ý cách chỉ định toàn bộ thư mục cho mỗi tệp được tải lên, cả cục bộ và từ xa. Điều này là do phương thức put có thể gây ra lỗi nếu chỉ có thư mục từ xa được chỉ định

Thư mục “không được phép tải lên” trên máy chủ SFTP từ xa được định cấu hình rõ ràng là không thể ghi, nhằm mục đích minh họa điều gì sẽ xảy ra khi cố gắng tải lên một thư mục như vậy

Đúng như dự đoán, một lần cố gắng tải lên thư mục không thể ghi đã dẫn đến lỗi quyền. Các tải lên khác đã thành công

Tải xuống tệp trong SFTP bằng Python

Phương thức get tải xuống các tệp từ Máy chủ SFTP, trong ngữ cảnh của kết nối SFTP mở hiện có

# demo-sftp-download.py

import os
import paramiko
import sys

def main[argv]:
 hostkeys = paramiko.hostkeys.HostKeys [filename="known_hosts.txt"]
 # The host fingerprint is stored using the ed25519 algorithm. This was revealed
 # when the host was initially connected to from the sftp program invoked earlier.
 hostFingerprint = hostkeys.lookup ["my-sftp-host-or-ip"]['ssh-ed25519']


 try:
  # Note that the parameters below represent a low-level Python Socket, and 
  # they must be represented as such.
  tp = paramiko.Transport["my-sftp-host-or-ip", 22]

  # Note that while you *can* connect without checking the hostkey, you really
  # shouldn't. Without checking the hostkey, a malicious actor can steal
  # your credentials by impersonating the server.
  tp.connect [username = "my-username", password="my-password", hostkey=hostFingerprint]

  # Use a dictionary object to create a list of files to download, along with their remote paths.
  # Note that the first entry attempts to download from a directory with no files.
  
  # Note that while this dictionary shows the local path as the key and the remote path
  # as the value, the get method expects the remote path as its first parameter, so the 
  # call to that will look "backwards."
  filesToDownload = {"./Wiring Up Close - Annotated.jpeg":"./no-upload-allowed/Wiring Up Close - Annotated.jpeg",
   "./lipsum.txt":"./lipsum.txt", 
   "./3 Separate LEDs - Full Diagram - Cropped.jpeg":"./3 Separate LEDs - Full Diagram - Cropped.jpeg"}
  sftpClient = paramiko.SFTPClient.from_transport[tp]
  for key, value in filesToDownload.items[]:
   # Note how the remote file to download is specified first. The path to which it will be saved
   # locally is the second parameter.
   try:
    sftpClient.get [value, key]
    print ["[" + value + "] successfully downloaded to [" + key + "]"]
   except FileNotFoundError as err:
    print ["File download failed because [" + value + "] did not exist on the remote server."]
    # Note that the get method may leave a zero-length file in the local path.
    # This should be deleted.
    if os.path.exists[key]:
     os.remove[key]
   except Exception as err:
    print ["File download failed for [" + value + "] due to other error [" + str[err] + "]"]
  
  # Make sure to close all created objects.
  sftpClient.close[]
  tp.close[]
 except paramiko.ssh_exception.AuthenticationException as err:
  print ["Can't connect due to authentication error [" + str[err] + "]"]
 except Exception as err:
  print ["Can't connect due to other error [" + str[err] + "]"]

if __name__ == "__main__":
 main[sys.argv[1:]]




Listing 4 - Downloading Files

Điểm nổi bật chính của danh sách này là mặc dù từ điển được sử dụng ở đây chứa các giá trị giống như từ điển trong danh sách trước, nhưng chính giá trị, trái ngược với khóa, đang thúc đẩy hoạt động tải xuống. Một thay đổi nhỏ khác là trong một số trường hợp, một tệp có độ dài bằng 0 được tạo khi không thể tải xuống. Đó là một thực hành tốt để xóa các tập tin như vậy

Danh sách trên cho đầu ra sau, lưu ý danh sách thư mục trước và sau

Đầu ra của Liệt kê 4, hiển thị các tệp đã tải xuống

Như trường hợp tải tệp lên, thông báo lỗi hiển thị khi cố tải xuống tệp không tồn tại

Xóa tệp bằng SFTP và Python

Phương thức xóa sẽ xóa các tệp trên máy chủ từ xa với giả định rằng tài khoản được sử dụng để đăng nhập vào máy chủ có đủ quyền để thực hiện việc đó

________số 8

Lưu ý rằng cần có các ngoại lệ bổ sung để khắc phục hai lý do phổ biến khiến thao tác xóa có thể không thành công

Các cân nhắc khác về SFTP và Python

Nếu mục đích của ứng dụng Python hỗ trợ SFTP là thực hiện một số loại thao tác trên một tập hợp con các tệp sẽ được tải xuống, thì tốt nhất là tải từng tệp riêng lẻ vào thư mục tạm thời của máy tính cục bộ. Hầu như không bao giờ là một ý kiến ​​hay khi “sao chép” toàn bộ nội dung của một trang web từ xa trước khi thực hiện các thao tác bổ sung

Phần mềm độc hại thường sử dụng SFTP để đánh cắp các tệp của máy tính cục bộ và một số phần mềm quét vi-rút thường tìm kiếm nhiều hoạt động SFTP tuần tự xảy ra bên ngoài phạm vi hoạt động của ứng dụng khách SFTP truyền thống như FileZilla hoặc WinSCP. Trong những tình huống như vậy, phần mềm quét vi-rút này được biết là chỉ chặn việc thực thi ứng dụng Python hỗ trợ SFTP. Có thể cần phải tạo một ngoại lệ để cho phép các ứng dụng Python hỗ trợ SFTP hoạt động

Trong phần tiếp theo của hướng dẫn lập trình mạng Python này, chúng ta sẽ xem xét các cách làm việc với Python và HTTPS ở phía máy khách

Làm cách nào để lấy tệp từ máy chủ SFTP bằng Python?

get[], và thuộc tính print, bạn cũng có thể sử dụng các hàm khác như. .
pysftp. Sự liên quan. đặt[]. Để tải tệp cục bộ lên đường dẫn từ xa, hãy sử dụng dòng. “sftp. đặt [localFilePath, remoteFilePath]”
Pysftp. gỡ bỏ. Bạn có thể muốn xóa một tệp khỏi máy chủ từ xa của mình. Để làm điều này, sử dụng dòng sau. sftp

Làm cách nào để kết nối với máy chủ SFTP bằng khóa riêng trong Python?

Để kết nối bằng tệp chính, bạn sẽ muốn chuyển đường dẫn đến tệp chính khi tạo kết nối. Để làm điều này, bạn sẽ đặt tham số "private_key" thành đường dẫn đến tệp . Khi pySFTP bắt đầu kết nối, nó sẽ cố gắng sử dụng tệp bạn đã chuyển vào.

Chủ Đề