Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@jkryl
Copy link

@jkryl jkryl commented Jan 6, 2017

The problem is that the code for upgrading plain connection to TLS connection is not quite correct. All events from plain socket are forwarded to TLS socket. It can happen that when end or close event is emitted on plain socket not all bytes are processed yet in TLS socket, which results in file truncation on the server (the bytes queued in TLS are not flushed to fs-write-stream).

At the time when this code was written there was no easy way how to convert plain sockets to TLS sockets. Now there is (TLSSocket()) so we should use the new way which works correctly and the code is much simpler.

lib/starttls.js Outdated

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a bug, its a missing feature - its never done that, I just incorrectly claimed it did in the docs (sorry). Even when I add the feature, it may or may not make its way all the way back to v4.x, so it would be better to keep doing things this way, it will work across all LTS versions.

lib/starttls.js Outdated

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:-(. I will document the API.

lib/starttls.js Outdated

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: you need to call secureSocket.ssl.verifyError(), and check that there is no error. If you don't do this, your TLS connection may be completely unauthenticated, so will be completely insecure.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reviewing the code! I have fixed this.

lib/starttls.js Outdated

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its not authorized yet. You have connected to a valid certificate, but you don't know if its a certificate for the host you want to connect to! It See https://github.com/sam-github/node/blob/39f39189ee6d4ef3c6d20033721ae515a744267f/doc/api/tls.md#event-secure

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I can do that as I don't know the hostname of client. This is server side of FTP connection, so client is connecting to us. All we can know about the client is perhaps its IP address and even that one does not have to be correct (if client is behind NAT or so). So making sure that the certificate presented by the client is valid is probably the best we can do here. It looks that neither the previous version of the code called checkServerIdentity(). Since it's not a regression I would prefer to leave the code as it is.

Copy link
Contributor

@mk-pmb mk-pmb Jan 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this code even special for the case where clients do present their own certificates, or do we have to assume the client authenticated with username and password? In the latter case, as far as I understand it, there would be no client certificate at all that we could verify.
In that case we should still put a comment that this were the place to put certificate verification if we knew which cert to expect, to help future additions and for better example code.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. By skipping server identity check we can make people to believe that the connection is fully secure while it is not. IMHO it is really a corner case as I doubt that any one runs FTP server configured to validate client certificates but nevertheless we can either:

  1. put a comment into the code explaining how the things are (as you suggest) or
  2. throw exception from FTP server constructor if anyone tries to use requestCert option to avoid false impression of security or
  3. allow user to specify his own checkServerIdentity method in options and throw exception if it is not specified (and requestCert is true)

@mk-pmb: What would be the preferred solution in your opinion? I'm open to other suggestions as well. thanks

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I skimmed the README again to see what behavior people might expect from our config documentation. Took me a while to find the TLS options, we should add some keyword-y hints like "Configure your server certificate and private key here."

The best I could find was allowUnauthorizedTls, that sounds like it could be used to determine the security level required, but I'd prefer something more like requireTlsCert with at least four values:

  • no = no cert required,
  • any = .requestCert, any cert will do (e.g. learning phase for whitelist),
  • ca = .requestCert + .rejectUnauthorized (cert must be CA-signed),
  • custom = .requestCert + fire a validation event where I can plug-in my whitelist of which self-signed user certs I accept.

The validation event should provide:

  • TLS info on an abstraction level near the node API.
    • I wouldn't want to work with that directly, instead I'd want a module that makes validation more admin-friendly. However, this module should work for any node TLS, not just FTP, thus out of project scope here.
    • Our docs should have a (link to a) list of what modules we can recommend for admin-friendly TLS validation. Even if that list might start empty, adding an item might feel easier if the collection already exists. (It assures that yes it is a good idea to collect this kind of item.)
    • A comment near the source line that fires the validation event should remind that the docs offer a list of recommended modules.
  • easy access to some session object where information can be shared between at least these events:
    • TLS validation on control channel
      • USER/PASS on control channel
      • TLS validation on data channel
        • People might want to verify the cert is not just valid for this user but also the same one used on the control channel.
        • I'm undecided whether we should even make this the default.
  • a decider function with one argument that we treat as boolean flag whether to accept the connection.
    • Whichever event handler calls the decider first, wins. If people want more sophisticated decision strategies like priorities, they can hand out their own decider function, count ballots, and provide our decider with the final result.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea, you are right. checkServerIdentity is used only on client side of TLS connection. So my proposal regarding checkServerIdentity was not correct. I looked at what general TLS server implementation in nodejs does and it is surprising: https://github.com/nodejs/node/blob/master/lib/_tls_wrap.js#L814 . It does not bother to verify hostname to match a certificate at all. Valid certificate used by client is sufficient. I got confused by Sam's initial comment about missing checkServerIdentity call:

... you don't know if its a certificate for the host you want to connect to!

Sam thought that we deal with client side of the connection, but that's wrong. So if we want to provide more sophisticated validation of client certificates, it is possible but we should pick a different name for such method than checkServerIdentity (i.e. checkClientIdentity :-) ) and it would get called only if client's certificate was successfully verified. Regardless how it is named implementation of this new option sounds exactly like feature which could be implemented in future as extension of what we have now?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, sorry, I thought it was client side code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should think of what exactly the client's cert shall certify. The remote IP or its hostname, probably not. I could think of a username disguised as a hostname, or email address, my CA software could do both. Those would make sense to verify.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it is not very clear what should be subject to verification. We should keep in mind that when TLS handshake happens on control connection we don't even know client's login credentials. I'm sure it will be much easier to add this feature when there is a need for it, rather than if one has to guess what might be needed without any particular use case. I have updated my PR to include documentation changes clarifying how tlsOptions should be used and inserted a couple of TODOs to source code - hints on possible future improvements. Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when TLS handshake happens on control connection we don't even know client's login credentials

Right, so we'd have to save which name the cert claimed to certify (if we believe it) so we can remember it when a USER or PASS command arrives.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants