Indeed ssl.create_default_context() can be used again (as it's purpose) after initializing it:

import ssl, smtplib
>>> smtp = smtplib.SMTP("mail.python.org", port=587)
>>> context = ssl.create_default_context()
>>> smtp.starttls(context=context)

(220, b'2.0.0 Ready to start TLS')

↳ https://docs.python.org/3/library/ssl.html#best-defaults

Answer from l'L'l on Stack Overflow
🌐
Python
docs.python.org › 3 › library › ssl.html
ssl — TLS/SSL wrapper for socket objects — Python 3.14.4 ...
This context enables VERIFY_X509_STRICT by default, which may reject pre-RFC 5280 or malformed certificates that the underlying OpenSSL implementation otherwise would accept. While disabling this is not recommended, you can do so using: ctx ...
Discussions

tls - Is adding a default context to Python's Urllib necessary security-wise? And is it enough? - Information Security Stack Exchange
However, a badly configured client (like older versions of Python) that don't do HTTPS certificate validation can lead to security problems. If anyone has full control over the network connection between your client and the server they might be intercepting and reading everything that is being transmitted. This is why you read that ssl.create_default_context... More on security.stackexchange.com
🌐 security.stackexchange.com
March 2, 2021
Confusing behavior of ssl.create_default_context()
For instance, suppose that I anticipate two certificates with different purposes will be involved in my connection: a client certificate with CLIENT_AUTH and server certificate with SERVER_AUTH. How should I invoke ssl.create_default_context() if I am the client? More on github.com
🌐 github.com
3
September 20, 2022
python default context object ssl - Stack Overflow
I'm trying to establish a connection using ssl in python (I'm using python 3.6.1). I'm very new to ssl and so I read the ssl documentation and I saw that there is a function called create_default_context that return a new SSLContext object with default setting and I did'nt fully understand ... More on stackoverflow.com
🌐 stackoverflow.com
May 18, 2017
Self signed Certificate with Python SSL socket - Stack Overflow
I use a self signed certificate that I generated with the following command: sudo make-ssl-cert generate-default-snakeoil And copied it to my home directory. If I run the following with this on s... More on stackoverflow.com
🌐 stackoverflow.com
🌐
Runebook.dev
runebook.dev › en › docs › python › library › ssl › ssl.create_default_context
The Default Context: A Guide to Using and Customizing Python's ssl.create_default_context()
You can modify the context's minimum and maximum protocol versions after creation. ... import ssl # --- Alternative Method: Enforcing Modern TLS --- # Get the secure default context first context = ssl.create_default_context() # Enforce a minimum of TLS 1.2 (or even TLS 1.3 if needed) # ssl.TLSVersion.TLSv1_2 is a reliable minimum for modern security context.minimum_version = ssl.TLSVersion.TLSv1_2 # You can also set a maximum version if you know the server is incompatible with the newest # context.maximum_version = ssl.TLSVersion.TLSv1_2 print(f"Context minimum TLS version set to: {context.minimum_version.name}") # Use the modified 'context'
🌐
ProgramCreek
programcreek.com › python › example › 71559 › ssl.create_default_context
Python Examples of ssl.create_default_context
def _certifi_ssl_context(self): if (sys.version_info.major == 2 and sys.hexversion >= 0x02070900 or sys.version_info.major == 3 and sys.hexversion >= 0x03040300): where = certifi.where() self._log(DEBUG1, 'certifi %s: %s', certifi.__version__, where) return ssl.create_default_context( purpose=ssl.Purpose.SERVER_AUTH, cafile=where) else: return None # # XXX USE OF cloud_ssl_context() IS DEPRECATED! # # If your operating system certificate store is out of date you can # install certifi (https://pypi.python.org/pypi/certifi) and its CA # bundle will be used for SSL server certificate verification when # ssl_context is None.
Top answer
1 of 2
2

Any data transmitted over HTTP is plain text and might be intercepted or read by anyone who has control of the devices/networks between the client and the server. HTTP should no longer be used for transmitting sensitive information.

Any request over HTTPS is today normally secure when using modern software and standard configuration. A modern server would refuse insecure connections, and a modern client would also refuse insecure connections.

However, a badly configured client (like older versions of Python) that don't do HTTPS certificate validation can lead to security problems. If anyone has full control over the network connection between your client and the server they might be intercepting and reading everything that is being transmitted. This is why you read that ssl.create_default_context() is recommended since it would configure older versions of Python to do certificate validation and set a recommended set of ciphers.

Not validating a certificate doesn't necessarily mean that your data is unsafe. If your connection ended up in the intended destination at the web server you wanted to reach, the connection is still securely encrypted (unless both the client and the server allows insecure protocols/ciphers). Even if someone like your ISP would try to dump the transmitted data, it would not be readable.

However, without validating the certificate you are vulnerable to man in the middle attacks if anyone in the network between your client and the web server is able to intercept your connection or by other means redirect your connection to a rogue server which can read the contents of your request (including any passwords), and pass it on to the real destination so you won't notice any thing.

  1. If you are using an older version of Python that does not validate SSL certificates by default, and you ALSO have a reason to not trust your ISP or any network between your client and the server, you should be worried. If you are using a newer version of Python it is secure and there is no need to worry. If you are using an older version that does not validate certificates by default, but you do trust the path between your client and the server, it should probably be OK was well.

  2. If you were using an old version of Python that does not validate SSL certificates by default, ssl.create_default_context() should be enough to validate certificates and enable a decent set of protocols and ciphers. Using a newer version of Python will automatically validate SSL certificates.

From release version 2.7.9/3.4.3, Python will validate certificates by default. This affects all relevant stdlib modules (urllib/urllib2, http, httplib).

Sources and documentation: https://www.python.org/dev/peps/pep-0476/ https://docs.python.org/2/library/httplib.html#httplib.HTTPSConnection

2 of 2
0

Would using urllib.request only on addresses starting with https:// automatically secure me, at least from the problems related to HTTP described above?

Yes, at least in Python3 (v3.8.2)

Should I be worried about the passwords I used without the ssl library's context? Are they (and all the data I exchanged) exposed to my ISP? Or did connecting only to https URLs automatically protect me from this particular problem?

No - you don't need to worry about your passwords being exposed on the wire if you use HTTPS (TLSv1.2+) .. at least at time of writing - if you do use http only, then you're credentials are exposed on the wire - see the subsequent screenshot.

If my passwords are indeed unsafe, will using the default context (after changing my passwords) help here?

Change your passwords if you've already used them over http, and switch to HTTPS from now on to ensure urllib.requests uses TLS. If you have sound advice to set the default ssl.create_default_context() then follow this also. In this post, I only reviewed the default behaviour against a web host that wants to ensure the connection is (more) secure. A malicious host might try to force a downgrade, or not force an upgrade to HTTPS.


I built the following test script, and had Wireshark running in the background at the same time as I ran the script. In the subsequent screenshot, you can clearly see the password is being transferred in the clear. Incidentally, infosec.SE processes the http transaction and forces an upgrade to HTTPS.

If, instead, you prefix the url with https:// then urllib.requests will ensure that the exchange occurs via TLS (in this case, TLSv1.2).

import urllib.requests
from urllib.parse import urlencode

data_string = urlencode( {
  'isLogin':  'true'  ,
  'isSignup': 'false'  ,
  'email':    '[email protected]'  ,
  'password': 'Fizz.Buzz.Obscene.Frankfurter'  ,
  } )

data_bytes = data_string.encode()

surl = '://security.stackexchange.com/users/login-or-signup/validation/track'

req = urllib.request.urlopen('http'+surl, data=data_bytes)
        # for python3.9+ you may need to add:  method='POST'
>>> data_string
'isLogin=true&isSignup=false&email=email%40email.com&password=Fizz.Buzz.Obscene.Frankfurter'
>>> req.read()
b'Login-OK'
>>> list( req.info().raw_items() )
[('Connection', 'close'), ('Content-Length', '8'), ('cache-control', 'no-cache'), ('pragma', 'no-cache'), ('content-type', 'text/plain'), ('server', 'Microsoft-IIS/10.0'), ('strict-transport-security', 'max-age=15552000'), ('x-route-name', 'Users/TrackLoginOrSignupValidation'), ('x-frame-options', 'SAMEORIGIN'), ('x-flags', 'AA'), ('x-aspnet-duration-ms', '0'), ('x-request-guid', '6e00e28c-9ced-4503-bef6-53213d971ca9'), ('x-is-crawler', '1'), ('x-providence-cookie', '25839c49-ee0d-05a4-0270-6121c53c7407'), ('content-security-policy', "upgrade-insecure-requests; frame-ancestors 'self' https://stackexchange.com"), ('Accept-Ranges', 'bytes'), ('Date', 'Tue, 02 Mar 2021 22:27:38 GMT'), ('Via', '1.1 varnish'), ('X-Served-By', 'cache-syd10150-SYD'), ('X-Cache', 'MISS'), ('X-Cache-Hits', '0'), ('X-Timer', 'S1614724058.110293,VS0,VE271'), ('Vary', 'Fastly-SSL'), ('X-DNS-Prefetch-Control', 'off'), ('Set-Cookie', 'prov=25839c49-ee0d-05a4-0270-6121c53c7407; domain=.stackexchange.com; expires=Fri, 01-Jan-2055 00:00:00 GMT; path=/; HttpOnly')]

🌐
GitHub
github.com › python › cpython › issues › 96972
Confusing behavior of ssl.create_default_context() · Issue #96972 · python/cpython
September 20, 2022 - Note that it suggests using create_default_context() without any arguments for client use. This is correct. It is also confusing, since the default value of purpose is Purpose.SERVER_AUTH. When I read on, this seems like the wrong option: Changed in version 3.10: The context now uses PROTOCOL_TLS_CLIENT or PROTOCOL_TLS_SERVER protocol instead of generic PROTOCOL_TLS.
Author   piannucci
🌐
Pythontic
pythontic.com › ssl › sslcontext › sslcontext
SSLcontext() method of SSLcontext class in Python | Pythontic.com
In a Python program, an instance of the class ssl.SSLContext acts as a placeholder where the policies and artifacts related to the secure communication of a client or a server can be stored. Creation of an SSLContext instance is generally the first step required in any SSL based server or client.
🌐
Stack Overflow
stackoverflow.com › questions › 44042709 › python-default-context-object-ssl
python default context object ssl - Stack Overflow
May 18, 2017 - So if i set the verify_mode to cert_none i should be able to use the default context object without creating self signed certificate ? ... The verify_mode determines how to treat the certificate of the other side. If you simply want not to load cert file at server side, you can directly use ssl.create_default_context(ssl.Purpose.xxx) and use the context instance to wrap a socket instance.
Find elsewhere
🌐
Python.org
discuss.python.org › core development
`ssl`: changing the default `SSLContext.verify_flags`? - Core Development - Discussions on Python.org
July 25, 2023 - At the moment (CPython 3.11), the default SSLContext.verify_flags is set to just VERIFY_X509_TRUSTED_FIRST: >>> import ssl >>> ssl.create_default_context().verify_flags <VerifyFlags.VERIFY_X509_TRUSTED_FIRST: 32768> I…
🌐
GitHub
github.com › python › cpython › issues › 107361
`ssl.create_default_context()`: add `VERIFY_X509_STRICT` and `VERIFY_X509_PARTIAL_CHAIN` to the default `verify_flags` · Issue #107361 · python/cpython
July 27, 2023 - Feature or enhancement My proposal is to add two new flags to the SSLContext.verify_flags created within ssl.create_default_context(): VERIFY_X509_STRICT: This will enable stricter RFC 5280 compliance checks within OpenSSL's X.509 path v...
Author   woodruffw
🌐
Program Creek
programcreek.com › python
Python create ssl context
def create_ssl_context(self): ctx = ssl.create_default_context() # Disable all encryption protcols except TLS1_0 ctx.options |= ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 # Try-Except here because OP_NO_TLSv1_3 not available in Python3 before 3.6 try: ctx.options |= ssl.OP_NO_TLSv1_3 | ssl.OP_NO_TLSv1_2 | ssl.OP_NO_TLSv1_1 except(AttributeError): ctx.options |= ssl.OP_NO_TLSv1_2 | ssl.OP_NO_TLSv1_1 ctx.set_ciphers(FORCED_CIPHERS) ctx.check_hostname = False return ctx
🌐
GitHub
github.com › rabbitmq › rabbitmq-management › issues › 225
Python 2.6 doesn't provide 'ssl.create_default_context' · Issue #225 · rabbitmq/rabbitmq-management
File "/usr/local/bin/rabbitmqadmin", line 991, in <module> main() File "/usr/local/bin/rabbitmqadmin", line 413, in main method() File "/usr/local/bin/rabbitmqadmin", line 607, in invoke_declare self.put(uri, json.dumps(upload)) File "/usr/local/bin/rabbitmqadmin", line 439, in put return self.http("PUT", "%s/api%s" % (self.options.path_prefix, path), body) File "/usr/local/bin/rabbitmqadmin", line 449, in http ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) AttributeError: 'module' object has no attribute 'create_default_context' According to the rabbitmqadmin documentation and the version check at the top of the rabbitmqadmin file, Python 2.6 is the minimum version of Python supported, but ssl.create_default_context was added in 2.7.9.
🌐
Reddit
reddit.com › r/learnpython › can someone explain this for me pleased
r/learnpython on Reddit: Can someone explain this for me pleased
May 27, 2023 - ... Yeah but I want to understand why is it written this way? I mean what each function do and everything ... ssl.create_default_context() creates a new SSL context with default settings.
🌐
Chris's Wiki
utcc.utoronto.ca › ~cks › space › blog › python › Python3SSLInClients
A few notes on using SSL in Python 3 client programs
So I went and read the module documentation with a bit more care, where it pointed me to the ssl module's "Security considerations" section, which told me that in modern Python, you want to supply a SSL context and you should normally get that context from ssl.create_default_context().
🌐
CodeQL
codeql.github.com › codeql-query-help › python › py-insecure-default-protocol
Default version of SSL/TLS may be insecure — CodeQL query help documentation
The following code illustrates how to use 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. import ssl # Using flags to restrict the protocol context = ssl.SSLContext() context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 # Declaring a minimum version to restrict the protocol context = ssl.create_default_context() context.minimum_version = ssl.TLSVersion.TLSv1_2
🌐
Electricmonk
electricmonk.nl › log › 2018 › 06 › 02 › ssl-tls-client-certificate-verification-with-python-v3-4-sslcontext
SSL/TLS client certificate verification with Python v3.4+ SSLContext | Electricmonk.nl weblog
June 2, 2018 - #!/usr/bin/python3 import socket import ssl host_addr = '127.0.0.1' host_port = 8082 server_sni_hostname = 'example.com' server_cert = 'server.crt' client_cert = 'client.crt' client_key = 'client.key' context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=server_cert) context.load_cert_chain(certfile=client_cert, keyfile=client_key) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) conn = context.wrap_socket(s, server_side=False, server_hostname=server_sni_hostname) conn.connect((host_addr, host_port)) print("SSL established.
Top answer
1 of 2
6

In order to make the requests library use a custom ssl context, you need to create a custom HTTPAdapter class and override the init_poolmanager method to pass in extra arguments to the base class's implementation.

See sample code here:

from requests import Session
from requests.adapters import HTTPAdapter
import ssl


class CustomHTTPAdapter(HTTPAdapter):

    def init_poolmanager(self, *args, **kwargs):
        # this creates a default context with secure default settings,
        # which enables server certficiate verification using the
        # system's default CA certificates
        context = ssl.create_default_context()

        # alternatively, you could create your own context manually
        # but this does NOT enable server certificate verification
        # context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)

        super().init_poolmanager(*args, **kwargs, ssl_context=context)


def main():
    client_session = Session()
    client_session.mount("https://", CustomHTTPAdapter())

    # now you can use the client_session to make requests
    # r = client_session.get("https://<web address>/rest/info?f=json")
2 of 2
1

Creating an ssl.SSLContext() on its own doesn't enable certificate verification or load CA certificates by default. This is why you're not seeing SSL errors. Using ssl.create_ssl_context() does set verification by default.

So the issue here isn't with SSLContext or certifi, it's with the website's certificate and how you're constructing your SSLContext. Next step would be to look into why the certificate the website is presenting isn't valid.

🌐
GitHub
github.com › python › cpython › issues › 63888
ssl.create_default_context() · Issue #63888 · python/cpython
BPO 19689 Nosy @gvanrossum, @pitrou, @tiran Files ssl_create_default_context.patchssl_create_default_context2.patchssl_create_default_context3.patch Note: these values reflect the state of the issue at the time it was migrated and might ...