Repository navigation
Add support for explicit FTPS to server - #214
Cycloctane wants to merge 14 commits into
Conversation
(but not usable)
this connection is already insecure
- add new commands - add FTPS usage and example to server tutorial
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #214 +/- ##
==========================================
+ Coverage 97.95% 98.25% +0.30%
==========================================
Files 6 6
Lines 2098 2123 +25
==========================================
+ Hits 2055 2086 +31
+ Misses 43 37 -6 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
| host, | ||
| port, | ||
| ssl=self.ssl, | ||
| ssl=self.ssl if not self.ssl_explicit else None, # implicit ftps |
There was a problem hiding this comment.
Explicit FTPS allows plaintext
With explicit FTPS enabled, a client can skip AUTH TLS and still send USER and PASS and upload or download files. The server accepts those operations over plaintext control and data connections, exposing credentials and file contents on the wire. Require the intended TLS protection before accepting them; this must be fixed before merging.
How this was verified: A connection that never sent AUTH TLS logged in and transferred a file with neither connection using TLS.
Artifacts
Local FTP request reproduction script
- The authored Python source runs the same credential, upload, and download sequence in both configurations; it shows exactly how the transport checks were performed.
Implicit-TLS FTP request and response capture
- A TLS-configured implicit-mode server accepted login and file transfers without AUTH TLS, with TLS present on both connections.
Explicit-FTPS plaintext request and response capture
- A TLS-configured explicit-mode server accepted the same login and file transfers without AUTH TLS, with plaintext on both connections.
|
|
||
| ssl_context: ssl.SSLContext | None = None | ||
| if args.ftps != "off": | ||
| if not all((args.keyfile, args.certfile)): |
There was a problem hiding this comment.
The CLI rejects --certfile without --keyfile even when that PEM contains both the certificate and private key. SSLContext.load_cert_chain can load the file without a separate keyfile, so this guard unnecessarily prevents a valid FTPS configuration from starting. This is a non-blocking configuration limitation.
| if not all((args.keyfile, args.certfile)): | |
| if not args.certfile: |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Artifacts
Full executed combined-PEM reproduction source
- Captured the complete Python source with the cat command, working directory, and exit code; this is the script used for both runs.
Explicit FTPS without a separate keyfile
- Ran the CLI with only a combined-PEM certfile after Python loaded that PEM successfully; the CLI rejected it before starting a listener.
Explicit FTPS with the combined PEM also supplied as keyfile
- Ran the same CLI with the PEM supplied as both arguments and completed AUTH TLS and a TLS-protected QUIT; the certificate and key work.
This comment has been minimized.
This comment has been minimized.
- fix AUTH TLS typo - send response directly for auth tls instead of queue
| connection.response("504", f"AUTH {rest!r} not implemented") | ||
| else: | ||
| connection.ssl_enabled = True | ||
| await self.write_response(connection.command_connection, "234", "ready for TLS") |
There was a problem hiding this comment.
If a client sends FEAT and AUTH TLS before reading their replies and the queued FEAT response is delayed, this direct 234 write arrives first. The earlier 211 response can then arrive only after TLS negotiation begins, making the upgrade sequence difficult for the client to handle. This is a non-blocking interoperability concern; finish earlier queued replies before sending 234.
Artifacts
Loopback FTP reproduction script
- The executed script sends pipelined commands to a real loopback server and optionally holds the FEAT writer to expose response ordering.
Prior implementation with FEAT held
- The executed prior-implementation run received no reply while FEAT was held, then received plaintext 211 before 234.
Current implementation with held and ordinary FEAT responses
- The executed current-code runs show 234 overtaking held FEAT, while the ordinary path returns plaintext 211 before 234.
What do these changes do?
python3 -m aioftp).Are there changes in behavior for the user?
Users can use
aioftp.Server(ssl=ssl_context, ssl_explicit=True)to set up an explicit FTPS server.Related issue number
Resolves #37
Checklist