Is it possible to connect to a local Python server using secure web sockets from a user script on an HTTPS webpage?

2 weeks ago 15
ARTICLE AD BOX

We might have installed websockets for Python on macOS using this terminal command:

(.venv) a@as-MacBook-Pro dic % python3 -m pip install websockets

server.py:

#!/usr/bin/env python3 """Secure WebSocket echo server with TLS.""" import asyncio import ssl from websockets.asyncio.server import serve async def echo(websocket): async for message in websocket: await websocket.send(message) async def main(): # Create and configure SSL context ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) ssl_context.load_cert_chain(certfile="cert.pem", keyfile="key.pem") print("Starting secure WebSocket server on wss://localhost:8765") async with serve(echo, "localhost", 8765, ssl=ssl_context): await asyncio.Future() # Run forever if __name__ == "__main__": asyncio.run(main())

localhost_cert.cnf:

[req] default_bits = 2048 prompt = no default_md = sha256 req_extensions = req_ext distinguished_name = dn [dn] CN = localhost [req_ext] subjectAltName = @alt_names [alt_names] DNS.1 = localhost

We might have generated key.pem using this terminal command:

(.venv) a@as-MacBook-Pro dic % openssl genrsa -out key.pem 2048

We might have created/overridden cert.pem using this terminal command:

(.venv) a@as-MacBook-Pro dic % openssl req -x509 -new -nodes \ -key key.pem \ -sha256 \ -days 365 \ -out cert.pem \ -config localhost_cert.cnf \ -extensions req_ext

We might have already gone to Keychain Access > File > Import Items... > /Users/a/Documents/dic/cert.pem > Open > entered our password and clicked Modify Keychain > double-clicked on localhost > clicked on Trust > changed When using this certificate: value from Use System Defaults to Always Trust > closed the window > entered our password > clicked Update Settings

We might have tested the following code with multiple domains:

const ws = new WebSocket('wss://localhost:8765'); ws.onopen = () => console.log('%cConnected securely!', 'color: lime'); ws.onmessage = e => console.log('Server →', e.data); ws.onclose = e => console.log('Closed:', e); window.send = msg => ws.send(msg);

http://127.0.0.1:5500/index.html browser console:

const ws = new WebSocket('wss://localhost:8765'); ws.onopen = () => console.log('%cConnected securely!', 'color: lime'); ws.onmessage = e => console.log('Server →', e.data); ws.onclose = e => console.log('Closed:', e); window.send = msg => ws.send(msg); msg => ws.send(msg) VM1009:2 Connected securely!

However, we might not be able to do the same on HTTPS websites.

https://github.com/ browser console:

const ws = new WebSocket('wss://localhost:8765'); ws.onopen = () => console.log('%cConnected securely!', 'color: lime'); ws.onmessage = e => console.log('Server →', e.data); ws.onclose = e => console.log('Closed:', e); window.send = msg => ws.send(msg); VM185:1 Connecting to 'wss://localhost:8765/' violates the following Content Security Policy directive: "connect-src 'self' uploads.github.com ...". The action has been blocked. (anonymous) @ VM185:1 msg => ws.send(msg)

https://github.com/:

Declarative net rule:

{ "id": 23, "priority": 1, "action": { "type": "modifyHeaders", "responseHeaders": [ { "header": "Content-Security-Policy", "operation": "remove" }, { "header": "X-Frame-Options", "operation": "remove" } ] }, "condition": { "resourceTypes": [ "main_frame", "sub_frame", "stylesheet", "script", "image", "font", "object", "xmlhttprequest", "ping", "csp_report", "media", "websocket", "webtransport", "webbundle", "other" ] } }

browser console after using the declarative net rule on Brave browser:

const ws = new WebSocket('wss://localhost:8765'); ws.onopen = () => console.log('%cConnected securely!', 'color: lime'); ws.onmessage = e => console.log('Server →', e.data); ws.onclose = e => console.log('Closed:', e); window.send = msg => ws.send(msg); msg => ws.send(msg) VM856:1 WebSocket connection to 'wss://localhost:8765/' failed: (anonymous) @ VM856:1 VM856:4 Closed: CloseEvent {isTrusted: true, wasClean: false, code: 1006, reason: '', type: 'close', …}

https://mastodon.social/home browser console:

Warning: Don’t paste code into the DevTools Console that you don’t understand or haven’t reviewed yourself. This could allow attackers to steal your identity or take control of your computer. Please type ‘allow pasting’ below and press Enter to allow pasting. allow pasting const ws = new WebSocket('wss://localhost:8765'); ws.onopen = () => console.log('%cConnected securely!', 'color: lime'); ws.onmessage = e => console.log('Server →', e.data); ws.onclose = e => console.log('Closed:', e); window.send = msg => ws.send(msg); VM177:1 Connecting to 'wss://localhost:8765/' violates the following Content Security Policy directive: "connect-src 'self' data: blob: https://mastodon.social https://files.mastodon.social wss://streaming.mastodon.social". The action has been blocked. (anonymous) @ VM177:1 msg => ws.send(msg)

https://mastodon.social/explore browser console after using declarative net rules to remove the Content-Security-Policy headers.

const ws = new WebSocket('wss://localhost:8765'); ws.onopen = () => console.log('%cConnected securely!', 'color: lime'); ws.onmessage = e => console.log('Server →', e.data); ws.onclose = e => console.log('Closed:', e); window.send = msg => ws.send(msg); msg => ws.send(msg) VM140:1 WebSocket connection to 'wss://localhost:8765/' failed: (anonymous) @ VM140:1 VM140:4 Closed: CloseEvent {isTrusted: true, wasClean: false, code: 1006, reason: '', type: 'close', …}

https://x.com/home browser console:

const ws = new WebSocket('wss://localhost:8765'); ws.onopen = () => console.log('%cConnected securely!', 'color: lime'); ws.onmessage = e => console.log('Server →', e.data); ws.onclose = e => console.log('Closed:', e); window.send = msg => ws.send(msg); VM529:1 Connecting to 'wss://localhost:8765/' violates the following Content Security Policy directive: "connect-src 'self' blob: https://fonts.googleapis.com/css ...". The action has been blocked. (anonymous) @ VM529:1 msg => ws.send(msg)

https://x.com/home browser console after using declarative net rules to remove content security headers:

const ws = new WebSocket('wss://localhost:8765'); ws.onopen = () => console.log('%cConnected securely!', 'color: lime'); ws.onmessage = e => console.log('Server →', e.data); ws.onclose = e => console.log('Closed:', e); window.send = msg => ws.send(msg); msg => ws.send(msg) VM436:1 WebSocket connection to 'wss://localhost:8765/' failed: (anonymous) @ VM436:1 VM436:4 Closed: CloseEvent {isTrusted: true, wasClean: false, code: 1006, reason: '', type: 'close', …}
Read Entire Article