-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[$150] httpclient SSL doesn't verify certificate chain #782
Comments
Here is a patch to fix this, as well as to disable insecure versions of SSL: diff --git c/lib/pure/httpclient.nim w/lib/pure/httpclient.nim
index 2b16177..0dd868c 100644
--- c/lib/pure/httpclient.nim
+++ w/lib/pure/httpclient.nim
@@ -275,7 +275,7 @@ when not defined(ssl):
type SSLContext = ref object
let defaultSSLContext: SSLContext = nil
else:
- let defaultSSLContext = newContext(verifyMode = CVerifyNone)
+ let defaultSSLContext = newContext()
proc newProxy*(url: string, auth = ""): Proxy =
## Constructs a new ``TProxy`` object.
diff --git c/lib/pure/net.nim w/lib/pure/net.nim
index 8afc6c5..27fef9b 100644
--- c/lib/pure/net.nim
+++ w/lib/pure/net.nim
@@ -167,7 +167,7 @@ when defined(ssl):
if SSL_CTX_check_private_key(ctx) != 1:
raiseSSLError("Verification of private key file failed.")
- proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer,
+ proc newContext*(protVersion = protTLSv1, verifyMode = CVerifyPeer,
certFile = "", keyFile = ""): SSLContext =
## Creates an SSL context.
##
diff --git c/lib/pure/smtp.nim w/lib/pure/smtp.nim
index 26f0c95..001b59a 100644
--- c/lib/pure/smtp.nim
+++ w/lib/pure/smtp.nim
@@ -82,7 +82,7 @@ when not defined(ssl):
type PSSLContext = ref object
let defaultSSLContext: PSSLContext = nil
else:
- let defaultSSLContext = newContext(verifyMode = CVerifyNone)
+ let defaultSSLContext = newContext()
proc connect*(address: string, port = Port(25),
ssl = false, debug = false,
diff --git c/lib/pure/sockets.nim w/lib/pure/sockets.nim
index 99cdc00..32e38cb 100644
--- c/lib/pure/sockets.nim
+++ w/lib/pure/sockets.nim
@@ -284,8 +284,8 @@ when defined(ssl):
if SSL_CTX_check_private_key(ctx) != 1:
raiseSslError("Verification of private key file failed.")
- proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer,
- certFile = "", keyFile = ""): SSLContext =
+ proc newContext*(protVersion = protTLSv1, verifyMode = CVerifyPeer,
+ certFile = "", keyFile = "", certificateBundlePath: string): SSLContext =
## Creates an SSL context.
##
## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1 are
@@ -325,8 +325,12 @@ when defined(ssl):
if newCTX == nil:
raiseSslError()
+ # return code doesn't signify failure, ignore it
discard newCTX.SSLCTXSetMode(SSL_MODE_AUTO_RETRY)
+
newCTX.loadCertificates(certFile, keyFile)
+ if not newCTX.SSL_CTX_load_verify_locations("/home/user/tmp/ca-bundle.crt", nil):
+ raiseSslError("Unable to load certificate bundle")
return SSLContext(newCTX)
proc wrapSocket*(ctx: SSLContext, socket: Socket) = The main issue now is determining how to distribute the certificate bundle. There is no practical way to obtain it from the OS, so the best source is the converted Mozilla certificate bundle, which can be found at this url. Any suggestions on how that should be done? |
I think we should have a copy of this in $nim/dist and document it really well. We should support "copyFile" at compile-time then it can be copied over to where the exe will be put. Then it can be loaded via os.getAppDir() / "ca-bundle.crt". Maybe a bit too much magic though. |
That's way too much magic. Please research how it's done in other programming languages. |
Ok, here is how to do it: newContext only does SSL_CTX_load_verify_locations when certificateBundlePath is not "", so we delegate this problem to the programmer. httpclient then also gets some proc to overwrite the default SSL context. So people who think this sing and dance has anything to do with realworld security can deal with it but people like me who want stuff that works out of the box can happily ignore it. |
That's fine. You already have the ability to specify the SSL context in |
How about adding |
Any MITM that is able to passively listen on unencrypted connections could almost as easily impersonate the signed server and just proxy if certificates are not verified by default. Python just went through this process as well for 2.6.x and switched to default:secure. That definitely seems like the right way to go, as long as it's easy to disable checks. Is there currently any way to easily disable checks (ie |
You have to define a custom SSL context currently. I'm happy to accept PRs to fix this, but I do like @Araq's proposed solution so if that could be implemented it would be ideal. |
What about providing a compile-time warning for cert list, and then a compile-time option? (Are they available in libre/openssl when compiled in). It looks like Debian/Ubuntu/Alpine/etc use |
Looking at |
Both verify peer and hostname check (in https) should be enabled by default. Virtually every other major language has gone in this direction, even when it meant breaking changes. In particular, Rust, Go, Python, and Ruby have all become
All of this should be easy to disable, and should probably check against the system certificates, which is how most languages do it. (Python fallback: "If all three are None, this function can choose to trust the system’s default CA certificates instead." https://docs.python.org/3/library/ssl.html ) |
In the list below, none of the listed locations are user-modifiable (that would be a risk). Of course, this is not maintaining a list of certificates or the nightmare of checking CRLs etc, but simply a list of where the OS's own list is. Grabbing this at runtime seems to make more sense than a compile flag (since you might want the same binary to run on both RHEL and Ubuntu). (If anyone knows where/if these are on Windows, that'd be great too.)
(source: https://bugs.python.org/msg192601 ) |
Some additional context here: |
It's also good to be able to disable certificate verification at runtime (in order to run functional tests and have testbeds), and print out a big warning when doing so. Not having this encourages developers to disable verification in their code and a "verify=false" can easily slip into release builds. |
@FedericoCeratto agreed. A user can write an if statement that checks a user setting somewhere (config file, etc) for that. I don't know about the big warning, since it could affect stderr that the user might be controlling in a specific format, but it seems logical. |
These are high priority bugs; in fact, they're actually language vulnerabilities in the stdlib, like the CVE linked to above for Ruby. #784 perhaps could be merged into this one, or vice-versa, since they'll probably be fixed at the same time. |
@jamiesonbecker I suggest having a "verify: bool" flag on newHttpClient for fine-grained control and an environment variable that disables SSL verification globally. Only the latter is meant for testbeds and prints out the warning. |
@FedericoCeratto Agreed with the verify: bool flag. Not sure about the env variable, but sounds reasonable to me if it wouldn't be set by accident. Something like |
BOUNTIES I'd like to pledge bounties for this work (no matter who does it) in PayPal, Stripe, Amazon or Ebay gift cards, etc. As a general rule, everywhere that a check can be done, it will be done by default, but checks should be easy to disable at the highest level possible (ie httpclient verify=false should make changes to ssl contexts etc). I anticipate that these patches would affect httpclient.nim and net.nim, and possibly openssl.nim.
If you think you grok this and might dig into this a bit, please mention it in this thread, and if no one takes up the challenge, I'll start digging into it next week. |
Perhaps #782 (comment) should be in |
@jamiesonbecker post a bounty using bountysource :) |
Hi, https://good.gsr2demo.pki.goog/ |
import std/httpclient
var client = newHttpClient()
echo client.getContent("https://nim-lang.org") On win10 compiled with Error: execution of an external program failed
|
@xflywind works for me on Windows and on Ubuntu. Which OS do you use? |
I use win10
Is it related to my Internet? I follow the instructions from forum |
Put the cacert.pem next to your .exe please and retry. |
We can close this now, right? |
Waiting for @xflywind's feedback. |
It works. |
confirmed it also work on:
|
No, instead we should implement auto-import for Windows' root certificate store. |
We considered that too and it's not without its problems:
|
instead of innovating here, can we follow what python does?
for eg, on my windows VM (via osx parallels), I can use:
|
I don't try to "innovate", I try to support SSL on Windows with minimal changes to the codebase as it's a bugfix. |
Realistically we only have to support Windows 7 (EOL-ed) to Windows 10 (which most people should be on). There aren't that many differences between them in terms of certificate store APIs. |
I'm not talking about API differences, I'm talking about the fact we don't know what is stored in the certificate store. This might lead to differences in behavior, for example, your web crawler can stop working after a Windows update. Having said that, I have no idea how often the entries in the store actually change. |
Inspiration from nim-lang/Nim#782 (comment)
Inspiration from nim-lang/Nim#782 (comment)
Inspiration from nim-lang/Nim#782 (comment)
Inspiration from nim-lang/Nim#782 (comment)
Inspiration from nim-lang/Nim#782 (comment)
The root certificates are meant to receive updates over time. This is usually used to support new CAs, but it becomes critical if an existing CA is compromised and all clients need to drop it quickly. In such case, "breaking" application is the desired behavior. |
Yet the breaking would be Windows specific. More importantly, it's still unclear how to cache the generated .pem file so the current solution is acceptable. |
Not really, if such a global drop happens, it's very likely that all OS (and applications on such OSes) will be impacted (and we don't provide a |
For example the debian ca-certificates changelog shows removals of obsoleted CAs but also blocking of untrusted ones. The refreshing of CRLs is a decades-old caching problem. Perhaps doing an automated refresh every N days could be acceptable for Nim on Windows, if N is user-configurable. |
Summary ======= Introduce the dedicated `asm` and `emit` operators to the MIR, replacing the opaque handling via `mnkPNode`. This: - fixes expressions used in `asm` and `emit` statements not being subject to the MIR transformation - makes MIR-based dependency scanning possible, without requiring special traversal logic for `mnkPNode` Details ======= Both the `emit` directive and the `asm` statement can include arbitrary expression, but these (including the entities referenced by them) were previously hidden at the MIR level. Dependency scanning by way of iterating over all MIR nodes in a fragment was thus not guaranteed to work when they were present. The new MIR nodes act as output operators without any behaviour of their own, with the string literals and input expression being passed via an argument block. Phrased differently, `emit` and `asm` act and look like calls of effect-free procedures at the MIR level. The arguments are all communicated as being passed-by-value with the call not modifying them. While this is not correct (the emitted code could potentially modify the operands), it's easier to implement it this way. Given the low-level nature of `asm` and `emit`, this behaviour is deemed okay for now. To make generation of the `emit` statement work, `nkPragma` processing in `mirgen` is changed slightly: instead of persisting the whole `nkPragma` node that contains an interesting directive, only the interesting directives are persisted (by first moving them into standalone `nkPragma` nodes).
For example
works, but certificate of pcwebshop is self-signed (at last at the time of bug report).
The text was updated successfully, but these errors were encountered: