mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Merge pull request #564 from taus-semmle/python-insecure-ssl-version
Python: Check for insecure versions of SSL and TLS.
This commit is contained in:
53
python/ql/src/Security/CWE-327/InsecureDefaultProtocol.qhelp
Normal file
53
python/ql/src/Security/CWE-327/InsecureDefaultProtocol.qhelp
Normal file
@@ -0,0 +1,53 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p> The <code>ssl</code> library defaults to an insecure version of
|
||||
SSL/TLS when no specific protocol version is specified. This may leave
|
||||
the connection vulnerable to attack.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
Ensure that a modern, strong protocol is used. All versions of SSL,
|
||||
and TLS 1.0 are known to be vulnerable to attacks. Using TLS 1.1 or
|
||||
above is strongly recommended. If no explicit
|
||||
<code>ssl_version</code> is specified, the default
|
||||
<code>PROTOCOL_TLS</code> is chosen. This protocol is insecure and
|
||||
should not be used.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<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_1</code> as a keyword argument.
|
||||
</p>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Transport_Layer_Security"> Transport Layer Security</a>.</li>
|
||||
<li>Python 3 documentation: <a href="https://docs.python.org/3/library/ssl.html#ssl.SSLContext"> class ssl.SSLContext</a>.</li>
|
||||
<li>Python 3 documentation: <a href="https://docs.python.org/3/library/ssl.html#ssl.wrap_socket"> ssl.wrap_socket</a>.</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
39
python/ql/src/Security/CWE-327/InsecureDefaultProtocol.ql
Normal file
39
python/ql/src/Security/CWE-327/InsecureDefaultProtocol.ql
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* @name Default version of SSL/TLS may be insecure
|
||||
* @description Leaving the SSL/TLS version unspecified may result in an insecure
|
||||
* default protocol being used.
|
||||
* @id py/insecure-default-protocol
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @tags security
|
||||
* external/cwe/cwe-327
|
||||
*/
|
||||
|
||||
import python
|
||||
|
||||
FunctionObject ssl_wrap_socket() {
|
||||
result = any(ModuleObject ssl | ssl.getName() = "ssl").getAttribute("wrap_socket")
|
||||
}
|
||||
|
||||
ClassObject ssl_Context_class() {
|
||||
result = any(ModuleObject ssl | ssl.getName() = "ssl").getAttribute("SSLContext")
|
||||
}
|
||||
|
||||
CallNode unsafe_call(string method_name) {
|
||||
result = ssl_wrap_socket().getACall() and
|
||||
method_name = "deprecated method ssl.wrap_socket"
|
||||
or
|
||||
result = ssl_Context_class().getACall() and
|
||||
method_name = "ssl.SSLContext"
|
||||
}
|
||||
|
||||
from CallNode call, string method_name
|
||||
where
|
||||
call = unsafe_call(method_name) and
|
||||
not exists(call.getArgByName("ssl_version"))
|
||||
select call, "Call to " + method_name + " does not specify a protocol, which may result in an insecure default being used."
|
||||
|
||||
|
||||
|
||||
|
||||
50
python/ql/src/Security/CWE-327/InsecureProtocol.qhelp
Normal file
50
python/ql/src/Security/CWE-327/InsecureProtocol.qhelp
Normal file
@@ -0,0 +1,50 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
Using a broken or weak cryptographic protocol may make a connection
|
||||
vulnerable to interference from an attacker.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
Ensure that a modern, strong protocol is used. All versions of SSL,
|
||||
and TLS 1.0 are known to be vulnerable to attacks. Using TLS 1.1 or
|
||||
above is strongly recommended.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>
|
||||
The following code shows a variety of ways of setting up a
|
||||
connection using SSL or TLS. They are all insecure because of the
|
||||
version specified.
|
||||
</p>
|
||||
|
||||
<sample src="examples/insecure_protocol.py" />
|
||||
|
||||
<p>
|
||||
All cases should be updated to use a secure protocol, such as
|
||||
<code>PROTOCOL_TLSv1_1</code>.
|
||||
</p>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Transport_Layer_Security"> Transport Layer Security</a>.</li>
|
||||
<li>Python 3 documentation: <a href="https://docs.python.org/3/library/ssl.html#ssl.SSLContext"> class ssl.SSLContext</a>.</li>
|
||||
<li>Python 3 documentation: <a href="https://docs.python.org/3/library/ssl.html#ssl.wrap_socket"> ssl.wrap_socket</a>.</li>
|
||||
<li>pyOpenSSL documentation: <a href="https://pyopenssl.org/en/stable/api/ssl.html"> An interface to the SSL-specific parts of OpenSSL</a>.</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
93
python/ql/src/Security/CWE-327/InsecureProtocol.ql
Normal file
93
python/ql/src/Security/CWE-327/InsecureProtocol.ql
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* @name Use of insecure SSL/TLS version
|
||||
* @description Using an insecure SSL/TLS version may leave the connection vulnerable to attacks.
|
||||
* @id py/insecure-protocol
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @tags security
|
||||
* external/cwe/cwe-327
|
||||
*/
|
||||
|
||||
import python
|
||||
|
||||
FunctionObject ssl_wrap_socket() {
|
||||
result = the_ssl_module().getAttribute("wrap_socket")
|
||||
}
|
||||
|
||||
ClassObject ssl_Context_class() {
|
||||
result = the_ssl_module().getAttribute("SSLContext")
|
||||
}
|
||||
|
||||
string insecure_version_name() {
|
||||
// For `pyOpenSSL.SSL`
|
||||
result = "SSLv2_METHOD" or
|
||||
result = "SSLv23_METHOD" or
|
||||
result = "SSLv3_METHOD" or
|
||||
result = "TLSv1_METHOD" or
|
||||
// For the `ssl` module
|
||||
result = "PROTOCOL_SSLv2" or
|
||||
result = "PROTOCOL_SSLv3" or
|
||||
result = "PROTOCOL_SSLv23" or
|
||||
result = "PROTOCOL_TLS" or
|
||||
result = "PROTOCOL_TLSv1"
|
||||
}
|
||||
|
||||
private ModuleObject the_ssl_module() {
|
||||
result = any(ModuleObject m | m.getName() = "ssl")
|
||||
}
|
||||
|
||||
private ModuleObject the_pyOpenSSL_module() {
|
||||
result = any(ModuleObject m | m.getName() = "pyOpenSSL.SSL")
|
||||
}
|
||||
|
||||
/* A syntactic check for cases where points-to analysis cannot infer the presence of
|
||||
* a protocol constant, e.g. if it has been removed in later versions of the `ssl`
|
||||
* library.
|
||||
*/
|
||||
predicate probable_insecure_ssl_constant(CallNode call, string insecure_version) {
|
||||
exists(ControlFlowNode arg | arg = call.getArgByName("ssl_version") |
|
||||
arg.(AttrNode).getObject(insecure_version).refersTo(the_ssl_module())
|
||||
or
|
||||
arg.(NameNode).getId() = insecure_version and
|
||||
exists(Import imp |
|
||||
imp.getAnImportedModuleName() = "ssl" and
|
||||
imp.getAName().getAsname().(Name).getId() = insecure_version
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate unsafe_ssl_wrap_socket_call(CallNode call, string method_name, string insecure_version) {
|
||||
(
|
||||
call = ssl_wrap_socket().getACall() and
|
||||
method_name = "deprecated method ssl.wrap_socket"
|
||||
or
|
||||
call = ssl_Context_class().getACall() and
|
||||
method_name = "ssl.SSLContext"
|
||||
)
|
||||
and
|
||||
insecure_version = insecure_version_name()
|
||||
and
|
||||
(
|
||||
call.getArgByName("ssl_version").refersTo(the_ssl_module().getAttribute(insecure_version))
|
||||
or
|
||||
probable_insecure_ssl_constant(call, insecure_version)
|
||||
)
|
||||
}
|
||||
|
||||
ClassObject the_pyOpenSSL_Context_class() {
|
||||
result = any(ModuleObject m | m.getName() = "pyOpenSSL.SSL").getAttribute("Context")
|
||||
}
|
||||
|
||||
predicate unsafe_pyOpenSSL_Context_call(CallNode call, string insecure_version) {
|
||||
call = the_pyOpenSSL_Context_class().getACall() and
|
||||
insecure_version = insecure_version_name() and
|
||||
call.getArg(0).refersTo(the_pyOpenSSL_module().getAttribute(insecure_version))
|
||||
}
|
||||
|
||||
from CallNode call, string method_name, string insecure_version
|
||||
where
|
||||
unsafe_ssl_wrap_socket_call(call, method_name, insecure_version)
|
||||
or
|
||||
unsafe_pyOpenSSL_Context_call(call, insecure_version) and method_name = "pyOpenSSL.SSL.Context"
|
||||
select call, "Insecure SSL/TLS protocol version " + insecure_version + " specified in call to " + method_name + "."
|
||||
@@ -0,0 +1,8 @@
|
||||
import ssl
|
||||
import socket
|
||||
|
||||
# Using the deprecated ssl.wrap_socket method
|
||||
ssl.wrap_socket(socket.socket())
|
||||
|
||||
# Using SSLContext
|
||||
context = ssl.SSLContext()
|
||||
16
python/ql/src/Security/CWE-327/examples/insecure_protocol.py
Normal file
16
python/ql/src/Security/CWE-327/examples/insecure_protocol.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import ssl
|
||||
import socket
|
||||
|
||||
# Using the deprecated ssl.wrap_socket method
|
||||
ssl.wrap_socket(socket.socket(), ssl_version=ssl.PROTOCOL_SSLv2)
|
||||
|
||||
# Using SSLContext
|
||||
context = ssl.SSLContext(ssl_version=ssl.PROTOCOL_SSLv3)
|
||||
|
||||
# Using pyOpenSSL
|
||||
|
||||
from pyOpenSSL import SSL
|
||||
|
||||
context = SSL.Context(SSL.TLSv1_METHOD)
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
| InsecureProtocol.py:41:1:41:17 | ControlFlowNode for Attribute() | Call to deprecated method ssl.wrap_socket does not specify a protocol, which may result in an insecure default being used. |
|
||||
| InsecureProtocol.py:42:11:42:22 | ControlFlowNode for SSLContext() | Call to ssl.SSLContext does not specify a protocol, which may result in an insecure default being used. |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE-327/InsecureDefaultProtocol.ql
|
||||
@@ -0,0 +1,13 @@
|
||||
| InsecureProtocol.py:6:1:6:47 | ControlFlowNode for Attribute() | Insecure SSL/TLS protocol version PROTOCOL_SSLv2 specified in call to deprecated method ssl.wrap_socket. |
|
||||
| InsecureProtocol.py:7:1:7:47 | ControlFlowNode for Attribute() | Insecure SSL/TLS protocol version PROTOCOL_SSLv3 specified in call to deprecated method ssl.wrap_socket. |
|
||||
| InsecureProtocol.py:8:1:8:47 | ControlFlowNode for Attribute() | Insecure SSL/TLS protocol version PROTOCOL_TLSv1 specified in call to deprecated method ssl.wrap_socket. |
|
||||
| InsecureProtocol.py:10:1:10:42 | ControlFlowNode for SSLContext() | Insecure SSL/TLS protocol version PROTOCOL_SSLv2 specified in call to ssl.SSLContext. |
|
||||
| InsecureProtocol.py:11:1:11:42 | ControlFlowNode for SSLContext() | Insecure SSL/TLS protocol version PROTOCOL_SSLv3 specified in call to ssl.SSLContext. |
|
||||
| InsecureProtocol.py:12:1:12:42 | ControlFlowNode for SSLContext() | Insecure SSL/TLS protocol version PROTOCOL_TLSv1 specified in call to ssl.SSLContext. |
|
||||
| InsecureProtocol.py:14:1:14:29 | ControlFlowNode for Attribute() | Insecure SSL/TLS protocol version SSLv2_METHOD specified in call to pyOpenSSL.SSL.Context. |
|
||||
| InsecureProtocol.py:15:1:15:30 | ControlFlowNode for Attribute() | Insecure SSL/TLS protocol version SSLv23_METHOD specified in call to pyOpenSSL.SSL.Context. |
|
||||
| InsecureProtocol.py:16:1:16:29 | ControlFlowNode for Attribute() | Insecure SSL/TLS protocol version SSLv3_METHOD specified in call to pyOpenSSL.SSL.Context. |
|
||||
| InsecureProtocol.py:17:1:17:29 | ControlFlowNode for Attribute() | Insecure SSL/TLS protocol version TLSv1_METHOD specified in call to pyOpenSSL.SSL.Context. |
|
||||
| InsecureProtocol.py:32:1:32:19 | ControlFlowNode for Attribute() | Insecure SSL/TLS protocol version SSLv2_METHOD specified in call to pyOpenSSL.SSL.Context. |
|
||||
| InsecureProtocol.py:48:1:48:43 | ControlFlowNode for Attribute() | Insecure SSL/TLS protocol version PROTOCOL_SSLv2 specified in call to deprecated method ssl.wrap_socket. |
|
||||
| InsecureProtocol.py:49:1:49:38 | ControlFlowNode for SSLContext() | Insecure SSL/TLS protocol version PROTOCOL_SSLv2 specified in call to ssl.SSLContext. |
|
||||
@@ -0,0 +1,50 @@
|
||||
import ssl
|
||||
from pyOpenSSL import SSL
|
||||
from ssl import SSLContext
|
||||
|
||||
# true positives
|
||||
ssl.wrap_socket(ssl_version=ssl.PROTOCOL_SSLv2)
|
||||
ssl.wrap_socket(ssl_version=ssl.PROTOCOL_SSLv3)
|
||||
ssl.wrap_socket(ssl_version=ssl.PROTOCOL_TLSv1)
|
||||
|
||||
SSLContext(ssl_version=ssl.PROTOCOL_SSLv2)
|
||||
SSLContext(ssl_version=ssl.PROTOCOL_SSLv3)
|
||||
SSLContext(ssl_version=ssl.PROTOCOL_TLSv1)
|
||||
|
||||
SSL.Context(SSL.SSLv2_METHOD)
|
||||
SSL.Context(SSL.SSLv23_METHOD)
|
||||
SSL.Context(SSL.SSLv3_METHOD)
|
||||
SSL.Context(SSL.TLSv1_METHOD)
|
||||
|
||||
# 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)
|
||||
|
||||
# true positive using flow
|
||||
|
||||
METHOD = SSL.SSLv2_METHOD
|
||||
SSL.Context(METHOD)
|
||||
|
||||
# secure versions
|
||||
|
||||
ssl.wrap_socket(ssl_version=ssl.PROTOCOL_TLSv1_1)
|
||||
SSLContext(ssl_version=ssl.PROTOCOL_TLSv1_1)
|
||||
SSL.Context(SSL.TLSv1_1_METHOD)
|
||||
|
||||
# possibly insecure default
|
||||
ssl.wrap_socket()
|
||||
context = SSLContext()
|
||||
|
||||
# importing the protocol constant directly
|
||||
|
||||
from ssl import PROTOCOL_SSLv2
|
||||
|
||||
ssl.wrap_socket(ssl_version=PROTOCOL_SSLv2)
|
||||
SSLContext(ssl_version=PROTOCOL_SSLv2)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE-327/InsecureProtocol.ql
|
||||
@@ -1 +1,2 @@
|
||||
semmle-extractor-options: -p ../lib/ --max-import-depth=3
|
||||
optimize: true
|
||||
|
||||
10
python/ql/test/query-tests/Security/lib/pyOpenSSL/SSL.py
Normal file
10
python/ql/test/query-tests/Security/lib/pyOpenSSL/SSL.py
Normal file
@@ -0,0 +1,10 @@
|
||||
SSLv2_METHOD = 1
|
||||
SSLv3_METHOD = 2
|
||||
SSLv23_METHOD = 3
|
||||
TLSv1_METHOD = 4
|
||||
TLSv1_1_METHOD = 5
|
||||
TLSv1_2_METHOD = 6
|
||||
|
||||
class Context(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
Reference in New Issue
Block a user