We 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 generated key.pem using this terminal command:
(.venv) a@as-MacBook-Pro dic % openssl genrsa -out key.pem 2048
We 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 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 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 are not 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)
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', …}