Masken und Steuerframes · Übung
Maskierte Frames und die Richtungsregel
apply_mask verwandelt eine Payload in maskierte Bytes und zurück, aber noch trägt kein encode_frame löst NotImplementedError aus, sobald es einen Schlüssel bekommt, und decode_frame tut dasselbe, wenn es als Server lesen soll. Diese Lektion ersetzt beide Zweige. Hier ist noch einmal das maskierte “Hello” des RFC, Feld für Feld:
81 85 37 fa 21 3d 7f 9f 4d 51 58
81: Byte 0, vom Maskieren unverändert. FIN ist gesetzt, und derist 1, Text. 85: Byte 1.0x85ist0x80 | 5, das MASK-Bit zusätzlich zur 7-Bit-Länge 5.37 fa 21 3d: der Maskierungsschlüssel, nur vorhanden, weil MASK gesetzt ist.7f 9f 4d 51 58: die Payload, “Hello”, mit diesem Schlüssel maskiert.
Nur die Payload ist maskiert. Der Header und die Länge bleiben lesbar, weil der Empfänger sie braucht, um zu wissen, wie viele Bytes er lesen muss und ob ein Schlüssel folgt.
MASK sitzt in Byte 1
Das MASK-Bit ist das oberste Bit von Byte 1, demselben Byte, das die 7-Bit-Länge enthält. length_field(n) aus Kapitel 3 gibt die Längenbytes in der kürzesten Form zurück, und ihr erstes Byte ist immer dieses 7-Bit-Feld: die Länge selbst bis 125 oder die Markierung 126 oder 127. MASK zu setzen bedeutet, 0x80 per OR in dieses erste Byte einzufügen und jedes erweiterte Längenbyte danach unverändert zu lassen. Dann kommen die vier Schlüsselbytes, dann die maskierte Payload. Mit dem Schlüssel 01 02 03 04 sind das die Header eines maskierten 256-Byte-Frames, eines maskierten 65.536-Byte-Frames und eines leeren maskierten Text-Frames:
82 fe 01 00 01 02 03 04
82 ff 00 00 00 00 00 01 00 00 01 02 03 04
81 80 01 02 03 04
Im ersten ist fe gleich 0x80 | 126, und die 16-Bit-Länge 01 00 folgt wie zuvor. Im zweiten ist ff gleich 0x80 | 127, und acht Längenbytes folgen. Der leere Frame trägt trotzdem einen Schlüssel, obwohl es nichts zu maskieren gibt.
Was gibt das aus?
print(hex(0x80 | 126), hex(0x80 | 5))
Woher Schlüssel kommen
RFC 6455, Abschnitt 5.3, verlangt vom Client für jeden Frame einen neuen Schlüssel, so gewählt, dass niemand ihn vorhersagen kann. In Kapitel 5 nimmt dein Client ihn von os.urandom(4), der Quelle der Standardbibliothek für unvorhersehbare Bytes. encode_frame ruft sie nicht auf. Es nimmt den Schlüssel als mask_key, so wie make_client_key(random_bytes=os.urandom) in Kapitel 2 seine Quelle für zufällige Bytes als Argument genommen hat. Ein Test kann dann einen bekannten Schlüssel übergeben und den Frame mit bekannten Bytes vergleichen, wie es frames_demo.py mit dem Schlüssel des RFC tut. Wenn mask_key gleich None ist, der Standardwert, baut encode_frame genau wie zuvor einen unmaskierten Frame, und das ist es, was ein Server sendet.
Die Richtungsregel
RFC 6455, Abschnitt 5.1, formuliert die Regel für beide Richtungen. Ein Client maskiert jeden Frame, den er sendet, und ein Server maskiert keinen. Ein Server, der einen unmaskierten Frame empfängt, muss die Verbindung schließen, und ein Client, der einen maskierten Frame empfängt, muss dasselbe tun. Jede Seite liest Frames von der anderen, also weiß jede Seite, welche Art sie erwarten muss:
| Wer liest | Erwartet | Ruft auf |
|---|---|---|
| ein Server, der die Frames eines Clients liest | MASK gesetzt | decode_frame(read_exact, require_mask=True) |
| ein Client, der die Frames eines Servers liest | MASK nicht gesetzt | decode_frame(read_exact), wobei require_mask standardmäßig False ist |
Also vergleicht decode_frame das MASK-Bit des Frames mit require_mask und löst False behält das Verhalten aus Kapitel 3 bei, die Sicht des Clients, und ein Server, der require_mask=True vergisst, lehnt jeden Frame ab, den sein Client sendet: ein lauter Fehler, der bei der ersten Nachricht auffällt. Der RFC sagt, dass der Empfänger die Verbindung schließt. Welchen ValueError.
Dein Server liest einen Frame, dessen MASK-Bit 0 ist. Was tut er?
Einen maskierten Frame dekodieren
Ein maskierter Frame wird in der Reihenfolge gelesen, in der sein Header ihn beschreibt, mit einem Schritt mehr als in Kapitel 3:
read_exact(2), gelesen vonparse_header_bytes;die 0, 2 oder 8 erweiterten Längenbytes, gelesen von
read_length;wenn MASK gesetzt ist,
read_exact(4), der Schlüssel;read_exact(length), die Payload, demaskiert mitapply_mask(payload, key).
Der Schlüssel kommt nach der erweiterten Länge, also würde ein früheres Lesen Längenbytes für den Schlüssel halten. Die Größenprüfung bleibt, wo sie war, direkt nach read_length: Ein Frame, der mehr als max_size Bytes ankündigt, wird abgelehnt, bevor der Schlüssel und die Payload gelesen werden.
decode_frame hat 82 fe 01 00 gelesen. Was liest es als Nächstes?
Deine Aufgabe
Ersetze in ws_frames.py den Zweig am Anfang jeder NotImplementedError auslöst:
encode_frame(opcode, payload, *, fin=True, mask_key=None): Gib mitmask_key=Nonewie zuvor den unmaskierten Frame zurück. Löse andernfallsValueErroraus, außer wenn der Schlüssel genau 4 Bytes lang ist, und gib Byte 0 zurück, dann die Längenbytes mit0x80per OR im ersten davon, dann den Schlüssel, dann die mitapply_maskmaskierte Payload.decode_frame(read_exact, *, require_mask=False, max_size=MAX_SIZE): Ersetze die Ablehnung jedes maskierten Frames aus Kapitel 3 durch die Richtungsregel und löseValueErroraus, wenn das MASK-Bit nicht dem entspricht, wasrequire_maskverlangt. Wenn der Frame maskiert ist, lies den 4 Byte langen Schlüssel nach der Längenprüfung und gib die Payload demaskiert zurück.
Klicke auf Run. Die beiden Abschnitte zu Lektion 2 bauen den maskierten Frame des RFC und lesen ihn so zurück, wie es ein Server tut, von einer Verbindung, die ein Byte nach dem anderen liefert:
Lesson 2: a masked text frame
81 85 37 fa 21 3d 7f 9f 4d 51 58
Lesson 2: the server unmasks it, one byte at a time
{'fin': True, 'opcode': 1, 'payload': b'Hello'}
Lesson 3: the client's pong for the server's ping
not written yet
Klicke dann auf Submit. Die Prüfung baut maskierte Frames mit Schlüsseln und Payloads, die du nicht gesehen hast, mit 0, 7, 256 und 65.536 Bytes, und vergleicht sie Byte für Byte, den Header zuerst. Sie liest maskierte Frames als Server, am Stück, ein Byte und jeweils zwei Bytes auf einmal geliefert, und prüft die Reihenfolge der Lesevorgänge; sie erwartet ValueError für einen Schlüssel, der nicht 4 Bytes lang ist, für jede Art, die Richtungsregel zu verletzen, und für einen maskierten Frame über max_size, bevor sein Schlüssel gelesen wird. Die unmaskierten Frames aus Kapitel 3 müssen weiterhin funktionieren. Jeder Frame bisher besteht aus Daten; die nächste Lektion fügt die Frames hinzu, die eine Verbindung in Ordnung halten.
Aufgabe
Ersetze in ws_frames.py den Zweig, der am Anfang von encode_frame und von decode_frame NotImplementedError auslöst:
encode_frame(opcode, payload, *, fin=True, mask_key=None): Baue mitmask_key=Nonewie zuvor den unmaskierten. Löse andernfalls aus, außer wenn mask_keygenau 4 Bytes lang ist, und gib Byte 0, die Längenbytes mit0x80(MASK) per OR im ersten davon, den Schlüssel und die mitapply_maskmaskierte Payload zurück.decode_frame(read_exact, *, require_mask=False, max_size=MAX_SIZE): LöseValueErroraus, wenn das MASK-Bit des Frames nicht dem entspricht, wasrequire_maskverlangt (ein Server übergibtTrue, ein Client behält den StandardwertFalse). Vergleiche die Länge wie zuvor direkt nachread_lengthmitmax_size; lies dann bei einem maskierten Frame den 4 Byte langen Schlüssel und gib die Payload demaskiert zurück.
Klicke auf Run: Die Abschnitte zu Lektion 2 sollten 81 85 37 fa 21 3d 7f 9f 4d 51 58 und {'fin': True, 'opcode': 1, 'payload': b'Hello'} ausgeben. Klicke dann auf Submit. Die Prüfung vergleicht maskierte Frames mit 0, 7, 256 und 65.536 Bytes und unbekannten Schlüsseln Byte für Byte, liest maskierte Frames als Server am Stück, ein Byte und jeweils zwei Bytes auf einmal, und erwartet die Lesevorgänge in der Reihenfolge Header, Länge, Schlüssel, Payload (Schlüssel und Payload in einem Lesevorgang sind in Ordnung). Sie erwartet ValueError für einen Schlüssel, der nicht 4 Bytes lang ist, einen unmaskierten Frame, der mit require_mask=True gelesen wird, einen maskierten Frame, der ohne es gelesen wird, und einen maskierten Frame über max_size, ohne Lesevorgang für seinen Schlüssel. Unmaskierte Frames müssen weiterhin wie in Kapitel 3 kodiert und dekodiert werden.