Sådan sikres dine WebSocket-forbindelser

Internettet vokser enormt. Flere og flere webapps er dynamiske, fordybende og kræver ikke slutbrugeren at opdatere. Der er nye understøttelse af kommunikationsteknologier med lav latenstid som websockets. Websockets giver os mulighed for at opnå realtidskommunikation mellem forskellige klienter, der er forbundet til en server.

Mange mennesker er uvidende om, hvordan de kan sikre deres websockets mod nogle meget almindelige angreb. Lad os se, hvad de er, og hvad skal du gøre for at beskytte dine websockets.

# 0: Aktivér CORS

WebSocket leveres ikke med CORS indbygget. Når det er sagt, betyder det, at ethvert websted kan oprette forbindelse til ethvert andet websteds websocket-forbindelse og kommunikere uden nogen begrænsning! Jeg går ikke ind på grunde til, at det er sådan det er, men en hurtig løsning på dette er at kontrollere Originheader på websocket-håndtrykket.

Sikker på, Origin-header kan forfalskes af en angriber, men det betyder ikke noget, for for at udnytte det skal angriberen falske Origin-headeren i offerets browser, og moderne browsere tillader ikke normalt javascript, der sidder i webbrowsere, for at ændre Origin-header .

Desuden, hvis du faktisk godkender brugere, der bruger fortrinsvis cookies, så er dette ikke rigtig et problem for dig (mere om dette på punkt 4)

# 1: Implementeringshastighedsbegrænsning

Satsbegrænsning er vigtig. Uden det kan klienter bevidst eller ubevidst udføre et DoS-angreb på din server. DoS står for Denial of Service. DoS betyder, at en enkelt klient holder serveren så travl, at serveren ikke er i stand til at håndtere andre klienter.

I de fleste tilfælde er det et bevidst forsøg fra en hacker at nedbringe en server. Nogle gange kan dårlige frontendimplementeringer også føre til DoS af normale klienter.

Vi skal bruge den utætte skovalgoritme (som tilsyneladende er en meget almindelig algoritme, som netværk kan implementere) til implementering af hastighedsbegrænsning på vores websockets.

Ideen er, at du har en spand, der har et hul i fast størrelse på gulvet. Du begynder at lægge vand i det, og vandet går ud gennem hullet i bunden. Hvis din hastighed for at lægge vand i spanden nu er større end hastigheden for at flyde ud af hullet i lang tid, bliver spanden på et tidspunkt fyldt og begynder at lække. Det er alt.

Lad os nu forstå, hvordan det relaterer til vores websocket:

  1. Vand er websockets trafik, der sendes af brugeren.
  2. Vand passerer ned ad hullet. Dette betyder, at serveren har behandlet den pågældende websocket-anmodning.
  3. Vand, der stadig er i skovlen og ikke er overløb, er grundlæggende ventende trafik. Serveren behandler denne trafik senere. Dette kan også være hård trafikstrøm (dvs. for meget trafik i meget kort tid er okay, så længe skovlen ikke lækker)
  4. Vand, der overløber, er den trafik, der kasseres af serveren (for meget trafik fra en enkelt bruger)

Pointen her er, at du skal kontrollere din websocket-aktivitet og bestemme disse tal. Du tildeler en spand til hver bruger. Vi beslutter, hvor stor skovlen skal være (trafik, som en enkelt bruger kan sende over en fast periode) afhængigt af, hvor stort dit hul er (hvor lang tid i gennemsnit har din server brug for at behandle en enkelt websocket-anmodning, f.eks. Gemme en sendt besked af en bruger i en database).

Dette er en afskåret implementering, jeg bruger på codedamn til implementering af utæt spandalgoritme til websockets. Det er i NodeJS, men konceptet forbliver det samme.

if(this.limitCounter >= Socket.limit) { if(this.burstCounter >= Socket.burst) { return 'Bucket is leaking' } ++this.burstCounter return setTimeout(() => { this.verify(callingMethod, ...args) setTimeout(_ => --this.burstCounter, Socket.burstTime) }, Socket.burstDelay) } ++this.limitCounter

Så hvad sker der her? Dybest set, hvis grænsen overskrides såvel som burstgrænsen (som er konstanter), falder websocket-forbindelsen. Ellers nulstiller vi burst-tælleren efter en særlig forsinkelse. Dette giver plads igen til endnu en burst.

# 2: Begræns nyttelaststørrelse

Dette skal implementeres som en funktion i dit websidesbibliotek på serversiden. Hvis ikke, er det tid til at ændre det til en bedre! Du bør begrænse den maksimale længde på meddelelsen, der kan sendes via din websocket. Teoretisk er der ingen grænse. Det er meget sandsynligt, at det at få en enorm nyttelast er meget sandsynligt at hænge den særlige stikkontakt og spise flere systemressourcer end krævet.

Hvis du f.eks. Bruger WS-bibliotek til node til oprettelse af websockets på serveren, kan du bruge indstillingen maxPayload til at angive den maksimale nyttelaststørrelse i byte. Hvis nyttelaststørrelsen er større end det, slipper biblioteket oprindeligt forbindelsen.

Forsøg ikke at implementere dette alene ved at bestemme meddelelsens længde. Vi vil ikke først læse hele meddelelsen i systemets RAM. Hvis det endda er 1 byte større end vores indstillede grænse, skal du slippe det. Dette kunne kun implementeres af biblioteket (som håndterer meddelelser som en strøm af bytes snarere end faste strenge).

# 3: Opret en solid kommunikationsprotokol

Fordi du nu har en duplexforbindelse, kan du sende noget til serveren. Serveren kunne sende enhver tekst tilbage til klienten. Du bliver nødt til at have en måde til effektiv kommunikation mellem begge.

Du kan ikke sende rå beskeder, hvis du vil skalere beskedaspektet på dit websted. Jeg foretrækker at bruge JSON, men der er andre optimerede måder at oprette en kommunikation på. Men i betragtning af JSON er her, hvordan et grundlæggende meddelelseskema ville se ud for et generisk websted:

Client to Server (or vice versa):  status: "ok"

Nu er det lettere for dig på serveren at kontrollere gyldige begivenheder og format. Slip forbindelsen med det samme, og log brugerens IP-adresse, hvis meddelelsesformatet er forskelligt. Der er ingen måde, formatet ville ændre sig, medmindre nogen manuelt kribler med din websocket-forbindelse. Hvis du er på node, anbefaler jeg at bruge Joi-biblioteket til yderligere validering af indgående data fra brugeren.

# 4: Godkend brugere, før WS-forbindelse opretter

Hvis du bruger websockets til godkendte brugere, er det en ret god ide at kun tillade godkendte brugere at oprette en vellykket websocket-forbindelse. Tillad ikke nogen at oprette en forbindelse, og vent derefter på, at de godkendes via selve websocket. Først og fremmest er det alligevel lidt dyrt at oprette en websocket-forbindelse. Så du vil ikke have, at uautoriserede mennesker hopper på dine websockets og hogger forbindelser, som kan bruges af andre mennesker.

For at gøre dette, når du opretter en forbindelse på frontend, skal du sende nogle godkendelsesdata til websocket. Det kan være et overskrift som X-Auth-Token:. Som standard overføres cookies alligevel.

Igen kommer det virkelig ned til det bibliotek, du bruger på serveren til implementering af websockets. Men hvis du er på Node og bruger WS, er der denne verificeringsfunktion, der giver dig adgang til info-objekt, der er sendt til en websocket-forbindelse. (Ligesom du har adgang til req-objekt til HTTP-anmodninger.)

# 5: Brug SSL over websockets

Dette er en no-brainer, men skal stadig siges. Brug wss: // i stedet for ws: //. Dette tilføjer et sikkerhedslag over din kommunikation. Brug en server som Nginx til reverse proxy-websockets og aktiver SSL over dem. Opsætning af Nginx ville være en helt anden tutorial. Jeg efterlader det direktiv, du skal bruge til Nginx til de mennesker, der er fortrolige med det. Mere info her.

location /your-websocket-location/ { proxy_pass ​//127.0.0.1:1337; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; }

Her antages det, at din websocket-server lytter på port 1337, og at dine brugere opretter forbindelse til din websocket på denne måde:

const ws = new WebSocket('wss://yoursite.com/your-websocket-location')

Spørgsmål?

Har du spørgsmål eller forslag? Spørg løs!