-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Python: Port insecure default protocol #5246
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
Python: Port insecure default protocol #5246
Conversation
- use API graphs - update .qlhelp-file - limit to versions below 3.4 - move tests to its own directory to only test on old version
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for including a change note and for updating the query help file 😄
I've made a few text suggestions, but wasn't able to see the output of the Qhelp preview check run as this no longer seems to generate an artifact. I've flagged this to the CodeQL core team in case they have any suggestions.
python/change-notes/2021-02-23-port-insecure-default-protocol.md
Outdated
Show resolved
Hide resolved
Note that <code>ssl.wrap_socket</code> has been deprecated in | ||
Python 3.7. A preferred alternative is to use | ||
<code>ssl.SSLContext</code>, which is supported in Python 2.7.9 and | ||
3.2 and later versions. | ||
3.2 and later versions or the convenience function | ||
<code>ssl.create_default_context</code>, which is supported in Python | ||
3.4 and later versions. | ||
</p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, I can't add a suggestion here - GitHub complains about "deleted lines" which seems like a bug (in the tooltip if not in the functionality).
This is getting a little overlong and complex. I'd suggest breaking out into bullets for clarity and readability.
Note that <code>ssl.wrap_socket</code> has been deprecated in
Python 3.7. The recommended alternatives are:</p>
<ul>
<li><code>ssl.SSLContext</code> - supported in Python 2.7.9,
3.2, and later versions</li>
<li><code>ssl.create_default_context</code> - a convenience function,
is supported in Python 3.4 and later versions.</li>
</ul>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I hope we will be able to see the result of my attempt at adapting this :-)
Co-authored-by: Felicity Chapman <felicitymay@github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a look at our discussion from last time you looked at this query (#3580). Our conclusion back then was that even though TLS 1.2 can be used in an insecure manner, we should only alert on version < TLS 1.2.
So TLS 1.1 should also be alerted on, and from my reading of https://docs.python.org/3.9/library/ssl.html#ssl.SSLContext, TLSv1.1 and TLSv1 are allowed with the default ssl.SSLContext()
call.
However, it seems like newer versions of the API expects bit-fiddling with the options
field -- is that maybe what you've referred to as fluent API
? and maybe that was the change that happened in Python 3.4? In any circumstance, I would really like you to add a comment to the QL code explaining what happened in Python 3.4 😊
ctx = ssl.SSLContext()
ctx.options |= ssl.OP_NO_TLSv1
ctx.options |= ssl.OP_NO_TLSv1_1
Is the plan that we will handle such cases with the py/insecure-protocol
query? and therefore, that this query should only alert on old code that doesn't support this bit-fiddling of the options
field? (since clearly, just spotting the context creation with ssl.SSLContext()
is not enough information to tell if that context is going to be secure or not)
I think it's all starting to come together for me, but I would have really liked for you to explain this stuff (in comment in the code, so we can see it in the future), instead of me doing detective work to figure it out 😄
It took me a long time to understand that the test-file InsecureProtocol
was a direct copy of the old test-file. I added a few comments for cleaning up the test-file, since I thought this was something new you just wrote. I think this might be a great time to clean up the test-file anyway, but now you know why I have been so nit-picky.
# not relevant | ||
wrap_socket(ssl_version=ssl.PROTOCOL_SSLv3) | ||
wrap_socket(ssl_version=ssl.PROTOCOL_TLSv1) | ||
wrap_socket(ssl_version=ssl.PROTOCOL_SSLv2) | ||
|
||
Context(SSL.SSLv3_METHOD) | ||
Context(SSL.TLSv1_METHOD) | ||
Context(SSL.SSLv2_METHOD) | ||
Context(SSL.SSLv23_METHOD) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick:
What are these? If they're just your own verification that API graphs actually work, I don't think they should be in the query, but in some tests of API graphs 😉
# FP for insecure default | ||
ssl.SSLContext(ssl.SSLv23_METHOD) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To my understanding, with Python 2.7.16 this is the same as using SSLContext()
, which is the same as SSLContext(ssl.PROTOCOL_TLS)
. So I think those cases should by under the # possibly insecure default
heading, and it should be explained they actually do the same thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, probably the heading should be something else... but grouping things together that does the same thing seems reasonable 😊
python/change-notes/2021-02-23-port-insecure-default-protocol.md
Outdated
Show resolved
Hide resolved
@@ -0,0 +1,52 @@ | |||
import ssl | |||
from pyOpenSSL import SSL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see any mention of pyOpenSSL
in the insecure default protocol... does that means that default values of pyOpenSSL is always safe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, in that SSL.SSLContext
requires a protocol argument; there is no default.
Co-authored-by: Rasmus Wriedt Larsen <rasmuswriedtlarsen@gmail.com>
I guess my hope that it would be explained by this comment was a bit naive. I will add a comment at the top of the query explaining that it only makes sense for older version of Python. |
👍 This, and the interplay with |
Co-authored-by: Rasmus Wriedt Larsen <rasmuswriedtlarsen@gmail.com>
Co-authored-by: Rasmus Wriedt Larsen <rasmuswriedtlarsen@gmail.com>
python/change-notes/2021-02-23-port-insecure-default-protocol.md
Outdated
Show resolved
Hide resolved
I explained the interplay in the .ql-file rather than in the help as it felt more natural. I did add an example of recommended use, though, that felt natural too. |
@RasmusWL, thanks for digging into this, I have now revised the PR based on your insights. For the record, this is basically that for this query attention should be restricted to the call |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think all the files this PR touches upon (especially the qhelp file, and the example) needs to have all references to SSLContext
removed, maybe except for an example of how to use its' wrap_socket
method in a safe manner. This query doesn't report on it anymore, so let's keep make our documentation match that as well 😉
Also just FYI, the example you added should have this change
-context.minimum_version(ssl.TLSVersion.TLSv1_2)
+context.minimum_version = ssl.TLSVersion.TLSv1_2
python/change-notes/2021-02-23-port-insecure-default-protocol.md
Outdated
Show resolved
Hide resolved
Co-authored-by: Rasmus Wriedt Larsen <rasmuswriedtlarsen@gmail.com>
Ah, I just noted the new procedure for requesting this :-) |
Hello @github/docs-content-codeql : this PR is ready for docs review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part of the qhelp file still needs to be changed so it doesn't talk about how default params of ssl.SSLContext
are bad:
codeql/python/ql/src/Security/CWE-327/InsecureDefaultProtocol.qhelp
Lines 27 to 66 in 3dd34c9
<p> | |
The following code shows two different ways of setting up a connection | |
using SSL or TLS. They are both potentially insecure because the | |
default version is used. | |
</p> | |
<sample src="examples/insecure_default_protocol.py" /> | |
<p> | |
Both of the cases above should be updated to use a secure protocol | |
instead, for instance by specifying | |
<code>ssl_version=PROTOCOL_TLSv1_2</code> as a keyword argument. | |
</p> | |
<p> | |
The latter example can also be made secure by modifying the created | |
context before it is used to create a connection and so will not be | |
flagged by this query. However, if a connection is created before | |
the context has been secured (e.g. by setting the value of <code>minimum_version</code>), | |
then the code should be flagged by the query <code>py/insecure-protocol</code>. | |
</p> | |
<p> | |
Note that <code>ssl.wrap_socket</code> has been deprecated in | |
Python 3.7. The recommended alternatives are: | |
</p> | |
<ul> | |
<li><code>ssl.SSLContext</code> - supported in Python 2.7.9, | |
3.2, and later versions</li> | |
<li><code>ssl.create_default_context</code> - a convenience function, | |
supported in Python 3.4 and later versions.</li> | |
</ul> | |
<p> | |
Note also that, even using these alternatives, it is recommended to | |
ensure that a safe protocol is being used. The following code illustrates | |
how to use either flags (available since Python 3.2) or the `minimum_version` | |
field (favored since Python 3.7) to restrict the protocols accepted when | |
creating a connection. | |
</p> | |
<sample src="examples/secure_default_protocol.py" /> | |
</example> |
☝️ is basically what I tried to say in the non-example part of #5246 (review)
I'm not sure whether any of the references are no longer needed?
I think both examples are still valid and I like how this enabled a natural link to |
@adityasharad - somehow this comment: #5246 (comment) wasn't picked up by our workflow. I'm not sure what the problem is but can see that the team name doesn't show up in bold as it would in a normal comment. (Update: Shati pointed out that this might be because the author of the comment is not a member of the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple of small text suggestions, a question, and a possible bug in the qhelp -> markdown conversion. Otherwise, the text looks generally clear.
python/change-notes/2021-02-23-port-insecure-default-protocol.md
Outdated
Show resolved
Hide resolved
The latter example can also be made secure by modifying the created | ||
context before it is used to create a connection and so will not be | ||
flagged by this query. However, if a connection is created before |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not clear to me from this text whether we mean the second example is never flagged by the query because it might be used in a secure way, or the second example is not flagged by the query if it is used in a secure way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is the first option. This query does not use data flow, so it now only flags cases that are immediately insecure. The other query referred to in the next sentence, py/insecure-protocol
, uses data flow to detect if a context is made secure before it is used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have attempted a clarification now.
<li><code>ssl.create_default_context</code> - a convenience function, | ||
supported in Python 3.4 and later versions.</li> | ||
</ul> | ||
<p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a bug in the markdown generated by the Qhelp test here. Note the missing blank line after the bullet list that means that this paragraph is run into the second bullet. I don't think this is an error in the qhelp file.
Note that `ssl.wrap_socket` has been deprecated in Python 3.7. The recommended alternatives are:
* `ssl.SSLContext` - supported in Python 2.7.9, 3.2, and later versions
* `ssl.create_default_context` - a convenience function, supported in Python 3.4 and later versions.
Note also that, even using these alternatives, it is recommended to ensure that a safe protocol is being used. The following code illustrates how to use either flags (available since Python 3.2) or the \`minimum_version\` field (favored since Python 3.7) to restrict the protocols accepted when creating a connection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can try inserting a blank line in the help file and see what happens..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. That doesn't appear to have made any difference 😞
I'm not sure where we should report this bug in the CodeQL CLI, but it doesn't look as if it should block this PR.
@felicitymay it was the colon immediately after the team name. Adding a space seems to fix it, will update the workflow. |
Co-authored-by: Felicity Chapman <felicitymay@github.com>
Co-authored-by: Felicity Chapman <felicitymay@github.com>
Co-authored-by: Felicity Chapman <felicitymay@github.com>
Co-authored-by: Felicity Chapman <felicitymay@github.com>
hopefully this will generate better markdown
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for all the changes. I spotted one more small thing in the text but otherwise it looks okay to me 😄
Co-authored-by: Felicity Chapman <felicitymay@github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One minor comment on the query itself, otherwise LGTM.
…ff/codeql into python-port-insecure-default-protocol
Reading over the text and examples again, it does look ok 👍 |
🤔 removing the request for review from Taus didn't allow me to merge this PR still 😂 |
The world has moved on, so this query is out of date in that its premise (that default constructors are insecure) has been invalidated (by the addition of fluent APIs to change defaults after construction). Therefor the QL help has been updated and the query has been limited to flag problems only when the version is older than the introduction of the fluent API.
Hence, this PR does the following: