Merged
Conversation
I'm going to add another, and IMO it's easier to read inline too.
I'm putting this in its own commit to simplify testing across backports. Also, I'm taking a "belt-and-suspenders" approach, and I'm going to test that either of the two fixes passes the tests.
Taking a "belt-and-suspenders" approach to a STARTTLS stripping attack: This handles `STARTTLS` as a special-case: if the `STARTTLS` handler did not run, for _whatever_ reason, an exception _must_ be raised and the connection dropped. _No_ command should ever receive a tagged `OK` prior to completely sending the command. But `STARTTLS` is security-sensitive enough to warrant this special-case handler.
Taking a "belt-and-suspenders" approach: This is a potential problem for any command which registers a response handler: a malicious server can easily guess what the next tag will be, and send an `OK` response _before_ the client the response handler is attached. `STARTTLS` is an extreme example of this issue: if the `STARTTLS` handler does not run, then `#starttls` will not start the TLS session, and the connection is not secured, _but no error is raised._ We should _also_ attach the response handler before sending the `CRLF`, but that is neither necessary (the response handler will added before the `synchronize` mutex is unlocked) nor sufficient (the fake `OK` can be sent _much_ earlier). On the other hand, it _is_ okay for the server to send an error tagged response (`NO` or `BAD`), before sending the command has completed.
As far as I can tell, this really isn't necessary: there is no race condition because the `synchronize` mutex will not be released before the response handler is added. Nor is it sufficient to protect against any invalid responses sent previously. But the code _reads_ more cleanly (to me) when it is written so the critical window that requires that mutex is as short as possible.
nevans
added a commit
that referenced
this pull request
Apr 23, 2026
🔒 Fix STARTTLS stripping vulnerability (backports #664)
nevans
added a commit
that referenced
this pull request
Apr 23, 2026
nevans
added a commit
that referenced
this pull request
Apr 23, 2026
…ort #664] Taking a "belt-and-suspenders" approach to a STARTTLS stripping attack: This handles `STARTTLS` as a special-case: if the `STARTTLS` handler did not run, for _whatever_ reason, an exception _must_ be raised and the connection dropped. _No_ command should ever receive a tagged `OK` prior to completely sending the command. But `STARTTLS` is security-sensitive enough to warrant this special-case handler.
nevans
added a commit
that referenced
this pull request
Apr 23, 2026
…664] Taking a "belt-and-suspenders" approach: This is a potential problem for any command which registers a response handler: a malicious server can easily guess what the next tag will be, and send an `OK` response _before_ the client the response handler is attached. `STARTTLS` is an extreme example of this issue: if the `STARTTLS` handler does not run, then `#starttls` will not start the TLS session, and the connection is not secured, _but no error is raised._ We should _also_ attach the response handler before sending the `CRLF`, but that is neither necessary (the response handler will added before the `synchronize` mutex is unlocked) nor sufficient (the fake `OK` can be sent _much_ earlier). On the other hand, it _is_ okay for the server to send an error tagged response (`NO` or `BAD`), before the sending the command has completed.
nevans
added a commit
that referenced
this pull request
Apr 23, 2026
🔒 Fix STARTTLS stripping vulnerability (backports #664)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Warning
Without this patch, a man-in-the-middle attacker can cause
Net::IMAP#starttlsto return "successfully", without starting TLS.This ensures that
#starttlseither succeeds or raises an exception.The following cases will raise an exception and close the connection:
#starttls, when another error wasn't raised (e.g: the response errors forNOorBAD) but the handler did not run. (Any handler errors are re-raised, since 🥅 Re-raise#starttlserror from receiver thread #395.)OKresponse for that command before the command has been fully sent (before the command's own response handlers are added), an exception is raised and the connection is closed.