9.17. Šifrované sockety a TLS¶
Vše, co bylo doposud probráno, přenáší bajty v otevřené podobě. Jakékoli zařízení na cestě mezi kamerou a serverem – domácí router, poskytovatel internetových služeb, škodlivý přístupový bod v kavárně – může v principu číst nebo upravovat to, co prochází. Pro většinu internetového provozu to není přijatelné. Standardním řešením je obalit spojení vrstvou šifrování: protokolem TLS (Transport Layer Security). Ikona zámku „HTTPS“ v prohlížeči je TLS běžící nad TCP a stejný obal je to, co dělá jakýkoli jiný internetový protokol „zabezpečeným“. Modul ssl kamery je to, co obaluje socket do TLS.
9.17.1. Co TLS přidává a s čím se kamera dodává¶
TLS se nachází mezi TCP a aplikací – aplikace zapisuje bajty do socketu obaleného TLS, TLS je zašifruje a výsledek předá TCP a na druhé straně se proces obrátí. Ve své plné podobě poskytuje TLS oproti čistému TCP tři záruky:
Důvěrnost. Odposlouchávající na cestě nemohou číst, co si oba koncové body vyměňují.
Integrita. Jakákoli úprava provozu během přenosu je odhalena; spojení se přeruší, místo aby předalo pozměněná data.
Autentizace. Server prokáže, že je oním jmenovaným serverem, a nikoli podvodníkem (a volitelně klient prokáže, kdo je on sám).
První dvě plynou ze samotného šifrování. Třetí vyžaduje certifikáty alespoň na jedné straně, a navíc něco předem důvěryhodného, vůči čemu lze tyto certifikáty ověřit. Kamera OpenMV se dodává bez jakéhokoli vestavěného úložiště certifikátů: čerstvě naflashovaná kamera nedůvěřuje žádné certifikační autoritě, nemá vlastní serverový certifikát a výchozí režim ověřování (ssl.CERT_NONE) neověřuje certifikát protějšku vůči ničemu. Takže ihned po vybalení vám TLS na kameře poskytuje první dvě záruky – šifrování proti odposlechu a manipulaci pasivním pozorovatelem – ale nikoli třetí.
9.17.2. Šifrování odchozího spojení¶
Nejjednodušším použitím je obalení odchozího TCP spojení. Postup je: otevřít běžný TCP socket, předat jej funkci ssl.wrap_socket() a poté číst a zapisovat skrz obalený socket přesně tak, jak byste to dělali s tím obyčejným:
import socket
import ssl
addr = socket.getaddrinfo("example.com", 443)[0][-1]
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(addr)
s = ssl.wrap_socket(sock)
s.send(b"GET / HTTP/1.0\r\nHost: example.com\r\n\r\n")
print(s.recv(4096))
s.close()
Obalení provede TLS handshake; poté je každý bajt procházející skrz s.send na cestě ven zašifrován a každý bajt z s.recv byl na drátě zašifrován. Žádné certifikáty nebyly nakonfigurovány, žádná kotva důvěry nebyla dodána – TLS jednoduše vyjedná dočasný relační klíč s tím serverem, který odpoví, a použije jej.
TLS handshake, který ssl.wrap_socket() provádí. Nachází se nad již otevřeným TCP spojením z předchozího obrázku; jakmile obě strany odeslaly Finished, zbytek konverzace je šifrován v obou směrech.¶
Varování
Toto je pouze šifrování, nikoli autentizované TLS. Kamera bezpečně komunikuje s čímkoli, co odpovědělo na druhém konci TCP spojení. Pokud útočník typu man-in-the-middle přesměruje spojení na server, který ovládá, a tento server předloží jakýkoli certifikát, handshake přesto uspěje a kamera nakonec bezpečně komunikuje s útočníkem. Tento režim používejte pouze tehdy, když man-in-the-middle není součástí modelu hrozeb – uzavřená místní síť, vývojové prostředí, kamera komunikující se službou běžící na stejném hardwaru – nikoli při kontaktu s veřejným internetem.
Pro skutečnou autentizaci – kamera ověřující veřejný server, kamera vystupující jako TLS server nebo vzájemné TLS – je potřeba dostat certifikáty na zařízení. Celý příběh je v Práce s TLS certifikáty.
Stejné obalení funguje i pro příchozí TCP provoz, a to volbou serverového protokolu a předáním server_side=True funkci ssl.wrap_socket(). Výše uvedené varování stále platí: bez vlastního certifikátu nemůže kamera prokázat, kdo je, klientovi, a zvědavý klient by na většině TLS stacků viděl selhání handshake kvůli „no certificate“. Produkční postup práce s certifikáty je to, co odemyká užitečné provozování kamery jako TLS serveru.
9.17.3. S asyncio¶
V kapitole o asyncio byla ukázána funkce asyncio.open_connection() pro obyčejné TCP klienty. Stejné volání přijímá klíčové slovo ssl=True, které obalí spojení do TLS, opět bez jakéhokoli nastavování certifikátů:
import asyncio
async def main():
reader, writer = await asyncio.open_connection(
"example.com", 443, ssl=True,
)
writer.write(b"GET / HTTP/1.0\r\nHost: example.com\r\n\r\n")
await writer.drain()
print(await reader.read(4096))
writer.close()
await writer.wait_closed()
asyncio.run(main())
Dvojice reader/writer za TLS spojením má stejný tvar jako u obyčejného TCP spojení – liší se pouze nastavení. Platí stejná výhrada ohledně autentizace: samotné ssl=True poskytuje pouze šifrování, nikoli ověřování.
9.17.4. DTLS – TLS přes UDP¶
TLS, jak bylo doposud probíráno, jezdí nad TCP. Paralelním protokolem pro UDP je DTLS (Datagram TLS) a modul ssl kamery jej podporuje stejným způsobem. Zatímco TLS proměňuje jedno TCP spojení v jeden šifrovaný proud bajtů, DTLS proměňuje jeden UDP socket v proud šifrovaných, jednotlivě doručovaných datagramů – takže vlastnosti UDP z UDP – odešli paket a doufej v nejlepší, tedy ztrátovost / nezachování pořadí / chybějící řízení toku, se všechny přenášejí, přičemž bajty uvnitř každého datagramu jsou nyní zašifrovány.
Obalení vypadá stejně jako v případě TLS, jen se socketem SOCK_DGRAM a konstantami protokolu DTLS:
import socket
import ssl
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.connect(socket.getaddrinfo("example.com", 4433)[0][-1])
ctx = ssl.SSLContext(ssl.PROTOCOL_DTLS_CLIENT)
s = ctx.wrap_socket(sock)
s.send(b"ping")
print(s.recv(64))
s.close()
(Volání connect() na UDP socketu neotevírá spojení – pouze si zapamatuje výchozí cíl, aby jej následná volání send() / recv() nemusela opakovat. DTLS tento pevný cíl potřebuje, aby vůči němu mohlo provést svůj handshake.)
Handshake má stejný tvar jako výše uvedený TLS diagram; rozdíl je v tom, že každá zpráva handshake je sama o sobě UDP datagram a kterákoli strana provede při ztrátě opakování.
Poznámka
Naruší ztráta paketů šifrování? Ne. Každý DTLS paket nese pořadové číslo a šifrování toto číslo používá k vytvoření odlišného výstupu pro každý paket – takže stejný vstup se nikdy nezašifruje dvakrát na stejné bajty a každý paket lze dešifrovat samostatně, aniž by dorazil ten předchozí. Ztracené pakety nebo pakety v nesprávném pořadí obě strany nerozsynchronizují. (Samotný handshake je jediná část, která musí dorazit spolehlivě, a DTLS to řeší vlastním opakovaným odesíláním.)
Platí stejné varování „pouze šifrování bez certifikátů“ jako výše: DTLS handshake vůči protějšku s CERT_NONE provoz zašifruje, ale neověří, kdo je druhá strana. Celý postup práce s DTLS – certifikáty, serverový cookie proti spoofingu, jak je to stejná oblast jako TLS až na konstanty protokolu – je probrán společně s materiálem o TLS v Práce s TLS certifikáty.
Verze s asyncio používá stejný vzor neblokujícího UDP z Sockety s asyncio. Handshake proveďte synchronně předem, přepněte socket do neblokujícího režimu a poté provádějte dotazování uvnitř korutiny:
import asyncio
import socket
import ssl
async def dtls_ping(target_addr, period_ms):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.connect(target_addr)
# Handshake while still blocking, then switch to async polling.
ctx = ssl.SSLContext(ssl.PROTOCOL_DTLS_CLIENT)
s = ctx.wrap_socket(sock)
s.setblocking(False)
while True:
try:
s.send(b"ping")
except OSError:
pass
await asyncio.sleep_ms(period_ms)
Handshake je jediné místo, kde tato korutina blokuje smyčku událostí; poté se každé s.send / s.recv vrátí okamžitě (nebo vyvolá OSError) a await asyncio.sleep_ms udržuje zbytek programu v běhu.
9.17.5. Jak pokračovat dál¶
Vše navíc oproti TLS s pouhým šifrováním – ověření certifikátu veřejného HTTPS serveru, provozování kamery jako autentizovaného TLS serveru, vzájemné TLS mezi kamerou a backendem, volba klíčů a typů klíčů, řešení vypršení platnosti certifikátu – je v Práce s TLS certifikáty. Tato část popisuje, jak generovat vlastnoručně podepsané certifikáty pro lokální testování, jak získat certifikáty podepsané CA pro produkci, jak je dostat na kameru ve správném formátu (DER), jak ověřit veřejný server, když je kamera klientem, jak přemýšlet o ochraně klíčů na zařízení, které může útočník rozebrat, a jak naplánovat den, kdy platnost certifikátu vyprší.
Pro úplnou referenci API ssl – podporované verze TLS, šifrovací sady a možnosti kontextu – viz ssl — modul SSL/TLS.