insecure_ssl_tls.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. #
  2. # Copyright 2014 Hewlett-Packard Development Company, L.P.
  3. #
  4. # SPDX-License-Identifier: Apache-2.0
  5. import bandit
  6. from bandit.core import issue
  7. from bandit.core import test_properties as test
  8. def get_bad_proto_versions(config):
  9. return config["bad_protocol_versions"]
  10. def gen_config(name):
  11. if name == "ssl_with_bad_version":
  12. return {
  13. "bad_protocol_versions": [
  14. "PROTOCOL_SSLv2",
  15. "SSLv2_METHOD",
  16. "SSLv23_METHOD",
  17. "PROTOCOL_SSLv3", # strict option
  18. "PROTOCOL_TLSv1", # strict option
  19. "SSLv3_METHOD", # strict option
  20. "TLSv1_METHOD",
  21. "PROTOCOL_TLSv1_1",
  22. "TLSv1_1_METHOD",
  23. ]
  24. } # strict option
  25. @test.takes_config
  26. @test.checks("Call")
  27. @test.test_id("B502")
  28. def ssl_with_bad_version(context, config):
  29. """**B502: Test for SSL use with bad version used**
  30. Several highly publicized exploitable flaws have been discovered
  31. in all versions of SSL and early versions of TLS. It is strongly
  32. recommended that use of the following known broken protocol versions be
  33. avoided:
  34. - SSL v2
  35. - SSL v3
  36. - TLS v1
  37. - TLS v1.1
  38. This plugin test scans for calls to Python methods with parameters that
  39. indicate the used broken SSL/TLS protocol versions. Currently, detection
  40. supports methods using Python's native SSL/TLS support and the pyOpenSSL
  41. module. A HIGH severity warning will be reported whenever known broken
  42. protocol versions are detected.
  43. It is worth noting that native support for TLS 1.2 is only available in
  44. more recent Python versions, specifically 2.7.9 and up, and 3.x
  45. A note on 'SSLv23':
  46. Amongst the available SSL/TLS versions provided by Python/pyOpenSSL there
  47. exists the option to use SSLv23. This very poorly named option actually
  48. means "use the highest version of SSL/TLS supported by both the server and
  49. client". This may (and should be) a version well in advance of SSL v2 or
  50. v3. Bandit can scan for the use of SSLv23 if desired, but its detection
  51. does not necessarily indicate a problem.
  52. When using SSLv23 it is important to also provide flags to explicitly
  53. exclude bad versions of SSL/TLS from the protocol versions considered. Both
  54. the Python native and pyOpenSSL modules provide the ``OP_NO_SSLv2`` and
  55. ``OP_NO_SSLv3`` flags for this purpose.
  56. **Config Options:**
  57. .. code-block:: yaml
  58. ssl_with_bad_version:
  59. bad_protocol_versions:
  60. - PROTOCOL_SSLv2
  61. - SSLv2_METHOD
  62. - SSLv23_METHOD
  63. - PROTOCOL_SSLv3 # strict option
  64. - PROTOCOL_TLSv1 # strict option
  65. - SSLv3_METHOD # strict option
  66. - TLSv1_METHOD # strict option
  67. :Example:
  68. .. code-block:: none
  69. >> Issue: ssl.wrap_socket call with insecure SSL/TLS protocol version
  70. identified, security issue.
  71. Severity: High Confidence: High
  72. CWE: CWE-327 (https://cwe.mitre.org/data/definitions/327.html)
  73. Location: ./examples/ssl-insecure-version.py:13
  74. 12 # strict tests
  75. 13 ssl.wrap_socket(ssl_version=ssl.PROTOCOL_SSLv3)
  76. 14 ssl.wrap_socket(ssl_version=ssl.PROTOCOL_TLSv1)
  77. .. seealso::
  78. - :func:`ssl_with_bad_defaults`
  79. - :func:`ssl_with_no_version`
  80. - https://heartbleed.com/
  81. - https://en.wikipedia.org/wiki/POODLE
  82. - https://security.openstack.org/guidelines/dg_move-data-securely.html
  83. - https://cwe.mitre.org/data/definitions/327.html
  84. .. versionadded:: 0.9.0
  85. .. versionchanged:: 1.7.3
  86. CWE information added
  87. .. versionchanged:: 1.7.5
  88. Added TLS 1.1
  89. """
  90. bad_ssl_versions = get_bad_proto_versions(config)
  91. if context.call_function_name_qual == "ssl.wrap_socket":
  92. if context.check_call_arg_value("ssl_version", bad_ssl_versions):
  93. return bandit.Issue(
  94. severity=bandit.HIGH,
  95. confidence=bandit.HIGH,
  96. cwe=issue.Cwe.BROKEN_CRYPTO,
  97. text="ssl.wrap_socket call with insecure SSL/TLS protocol "
  98. "version identified, security issue.",
  99. lineno=context.get_lineno_for_call_arg("ssl_version"),
  100. )
  101. elif context.call_function_name_qual == "pyOpenSSL.SSL.Context":
  102. if context.check_call_arg_value("method", bad_ssl_versions):
  103. return bandit.Issue(
  104. severity=bandit.HIGH,
  105. confidence=bandit.HIGH,
  106. cwe=issue.Cwe.BROKEN_CRYPTO,
  107. text="SSL.Context call with insecure SSL/TLS protocol "
  108. "version identified, security issue.",
  109. lineno=context.get_lineno_for_call_arg("method"),
  110. )
  111. elif (
  112. context.call_function_name_qual != "ssl.wrap_socket"
  113. and context.call_function_name_qual != "pyOpenSSL.SSL.Context"
  114. ):
  115. if context.check_call_arg_value(
  116. "method", bad_ssl_versions
  117. ) or context.check_call_arg_value("ssl_version", bad_ssl_versions):
  118. lineno = context.get_lineno_for_call_arg(
  119. "method"
  120. ) or context.get_lineno_for_call_arg("ssl_version")
  121. return bandit.Issue(
  122. severity=bandit.MEDIUM,
  123. confidence=bandit.MEDIUM,
  124. cwe=issue.Cwe.BROKEN_CRYPTO,
  125. text="Function call with insecure SSL/TLS protocol "
  126. "identified, possible security issue.",
  127. lineno=lineno,
  128. )
  129. @test.takes_config("ssl_with_bad_version")
  130. @test.checks("FunctionDef")
  131. @test.test_id("B503")
  132. def ssl_with_bad_defaults(context, config):
  133. """**B503: Test for SSL use with bad defaults specified**
  134. This plugin is part of a family of tests that detect the use of known bad
  135. versions of SSL/TLS, please see :doc:`../plugins/ssl_with_bad_version` for
  136. a complete discussion. Specifically, this plugin test scans for Python
  137. methods with default parameter values that specify the use of broken
  138. SSL/TLS protocol versions. Currently, detection supports methods using
  139. Python's native SSL/TLS support and the pyOpenSSL module. A MEDIUM severity
  140. warning will be reported whenever known broken protocol versions are
  141. detected.
  142. **Config Options:**
  143. This test shares the configuration provided for the standard
  144. :doc:`../plugins/ssl_with_bad_version` test, please refer to its
  145. documentation.
  146. :Example:
  147. .. code-block:: none
  148. >> Issue: Function definition identified with insecure SSL/TLS protocol
  149. version by default, possible security issue.
  150. Severity: Medium Confidence: Medium
  151. CWE: CWE-327 (https://cwe.mitre.org/data/definitions/327.html)
  152. Location: ./examples/ssl-insecure-version.py:28
  153. 27
  154. 28 def open_ssl_socket(version=SSL.SSLv2_METHOD):
  155. 29 pass
  156. .. seealso::
  157. - :func:`ssl_with_bad_version`
  158. - :func:`ssl_with_no_version`
  159. - https://heartbleed.com/
  160. - https://en.wikipedia.org/wiki/POODLE
  161. - https://security.openstack.org/guidelines/dg_move-data-securely.html
  162. .. versionadded:: 0.9.0
  163. .. versionchanged:: 1.7.3
  164. CWE information added
  165. .. versionchanged:: 1.7.5
  166. Added TLS 1.1
  167. """
  168. bad_ssl_versions = get_bad_proto_versions(config)
  169. for default in context.function_def_defaults_qual:
  170. val = default.split(".")[-1]
  171. if val in bad_ssl_versions:
  172. return bandit.Issue(
  173. severity=bandit.MEDIUM,
  174. confidence=bandit.MEDIUM,
  175. cwe=issue.Cwe.BROKEN_CRYPTO,
  176. text="Function definition identified with insecure SSL/TLS "
  177. "protocol version by default, possible security "
  178. "issue.",
  179. )
  180. @test.checks("Call")
  181. @test.test_id("B504")
  182. def ssl_with_no_version(context):
  183. """**B504: Test for SSL use with no version specified**
  184. This plugin is part of a family of tests that detect the use of known bad
  185. versions of SSL/TLS, please see :doc:`../plugins/ssl_with_bad_version` for
  186. a complete discussion. Specifically, This plugin test scans for specific
  187. methods in Python's native SSL/TLS support and the pyOpenSSL module that
  188. configure the version of SSL/TLS protocol to use. These methods are known
  189. to provide default value that maximize compatibility, but permit use of the
  190. aforementioned broken protocol versions. A LOW severity warning will be
  191. reported whenever this is detected.
  192. **Config Options:**
  193. This test shares the configuration provided for the standard
  194. :doc:`../plugins/ssl_with_bad_version` test, please refer to its
  195. documentation.
  196. :Example:
  197. .. code-block:: none
  198. >> Issue: ssl.wrap_socket call with no SSL/TLS protocol version
  199. specified, the default SSLv23 could be insecure, possible security
  200. issue.
  201. Severity: Low Confidence: Medium
  202. CWE: CWE-327 (https://cwe.mitre.org/data/definitions/327.html)
  203. Location: ./examples/ssl-insecure-version.py:23
  204. 22
  205. 23 ssl.wrap_socket()
  206. 24
  207. .. seealso::
  208. - :func:`ssl_with_bad_version`
  209. - :func:`ssl_with_bad_defaults`
  210. - https://heartbleed.com/
  211. - https://en.wikipedia.org/wiki/POODLE
  212. - https://security.openstack.org/guidelines/dg_move-data-securely.html
  213. .. versionadded:: 0.9.0
  214. .. versionchanged:: 1.7.3
  215. CWE information added
  216. """
  217. if context.call_function_name_qual == "ssl.wrap_socket":
  218. if context.check_call_arg_value("ssl_version") is None:
  219. # check_call_arg_value() returns False if the argument is found
  220. # but does not match the supplied value (or the default None).
  221. # It returns None if the arg_name passed doesn't exist. This
  222. # tests for that (ssl_version is not specified).
  223. return bandit.Issue(
  224. severity=bandit.LOW,
  225. confidence=bandit.MEDIUM,
  226. cwe=issue.Cwe.BROKEN_CRYPTO,
  227. text="ssl.wrap_socket call with no SSL/TLS protocol version "
  228. "specified, the default SSLv23 could be insecure, "
  229. "possible security issue.",
  230. lineno=context.get_lineno_for_call_arg("ssl_version"),
  231. )