Cara mendapatkan sambungan WebSocket anda

Web berkembang pesat. Semakin banyak aplikasi web yang dinamik, mendalam dan tidak memerlukan pengguna akhir untuk menyegarkan semula. Terdapat sokongan yang muncul untuk teknologi komunikasi latensi rendah seperti soket web. Websockets membolehkan kita mencapai komunikasi masa nyata antara pelanggan yang berbeza yang dihubungkan dengan pelayan.

Ramai orang tidak menyedari cara mengamankan soket web mereka daripada beberapa serangan yang sangat biasa. Mari kita lihat apa itu dan apa yang harus anda lakukan untuk melindungi soket web anda.

# 0: Dayakan CORS

WebSocket tidak dilengkapi dengan CORS inbuilt. Walaupun begitu, ini bermaksud bahawa mana-mana laman web boleh menyambung ke sambungan websocket laman web lain dan berkomunikasi tanpa sekatan! Saya tidak akan menerangkan alasan mengapa ini berlaku, tetapi penyelesaian yang cepat untuk ini adalah dengan mengesahkan Originheader di handshake websocket.

Sudah tentu, tajuk Header dapat dipalsukan oleh penyerang, tetapi tidak masalah, kerana untuk mengeksploitasinya, penyerang perlu memalsukan header Origin pada penyemak imbas mangsa, dan penyemak imbas moden tidak membenarkan javascript biasa yang duduk di penyemak imbas web untuk menukar header Origin .

Lebih-lebih lagi, jika anda benar-benar mengesahkan pengguna menggunakan, lebih baik, kuki, maka ini sebenarnya tidak menjadi masalah bagi anda (lebih lanjut mengenai perkara ini pada perkara # 4)

# 1: Melaksanakan pembatasan kadar

Mengehadkan kadar adalah penting. Tanpanya, pelanggan dapat dengan sengaja atau tidak sedar melakukan serangan DoS pada pelayan anda. DoS bermaksud Denial of Service. DoS bermaksud pelanggan tunggal membuat pelayan sibuk sehingga pelayan tidak dapat menangani klien lain.

Dalam kebanyakan kes, ini adalah percubaan yang disengaja oleh penyerang untuk menjatuhkan pelayan. Kadang-kadang pelaksanaan frontend yang buruk juga boleh menyebabkan DoS oleh pelanggan biasa.

Kami akan menggunakan algoritma bucket bocor (yang nampaknya merupakan algoritma yang sangat biasa untuk dilaksanakan oleh rangkaian) untuk melaksanakan pembatasan kadar pada soket web kami.

Ideanya ialah anda mempunyai baldi yang mempunyai lubang ukuran tetap di lantai. Anda mula memasukkan air ke dalamnya dan air keluar melalui lubang di bahagian bawah. Sekarang, jika kadar memasukkan air ke dalam baldi lebih besar daripada kadar mengalir keluar dari lubang untuk waktu yang lama, pada satu ketika, baldi akan menjadi penuh dan mula bocor. Itu sahaja.

Mari kita fahami bagaimana ia berkaitan dengan soket web kami:

  1. Air adalah trafik soket web yang dihantar oleh pengguna.
  2. Air melepasi lubang. Ini bermaksud pelayan berjaya memproses permintaan soket web tertentu.
  3. Air yang masih ada di dalam baldi dan belum melimpah pada dasarnya menunggu jalan raya. Pelayan akan memproses lalu lintas ini di kemudian hari. Ini juga mungkin aliran lalu lintas yang padat (iaitu terlalu banyak lalu lintas untuk waktu yang sangat kecil tidak mengapa selagi baldi tidak bocor)
  4. Air yang melimpah adalah lalu lintas yang dibuang oleh pelayan (terlalu banyak lalu lintas yang datang dari satu pengguna)

Maksudnya di sini adalah, anda harus memeriksa aktiviti soket web anda dan menentukan nombor-nombor ini. Anda akan memberikan baldi kepada setiap pengguna. Kami memutuskan seberapa besar baldi itu (lalu lintas yang dapat dikirim oleh satu pengguna dalam jangka masa yang tetap) bergantung pada seberapa besar lubang anda (berapa lama masa yang diperlukan oleh pelayan anda untuk memproses permintaan soket web tunggal, katakan menyimpan mesej yang dihantar oleh pengguna ke dalam pangkalan data).

Ini adalah pelaksanaan yang dipotong yang saya gunakan di codedamn untuk menerapkan algoritma bucket bucket untuk soket web. Ia ada di NodeJS tetapi konsepnya tetap sama.

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

Jadi apa yang berlaku di sini? Pada asasnya, jika had dilewati serta had pecah (yang ditetapkan pemalar), sambungan soket web akan merosot. Jika tidak, selepas kelewatan tertentu, kita akan menetapkan semula kaunter pecah. Ini meninggalkan ruang lagi untuk letupan lain.

# 2: Hadkan saiz muatan

Ini harus dilaksanakan sebagai ciri dalam perpustakaan soket web sisi pelayan anda. Sekiranya tidak, inilah masanya untuk menukarnya menjadi yang lebih baik! Anda harus menghadkan panjang maksimum mesej yang dapat dihantar melalui soket web anda. Secara teorinya tidak ada had. Sudah tentu, mendapatkan muatan yang besar sangat mungkin menggantung contoh soket tertentu dan memakan lebih banyak sumber sistem daripada yang diperlukan.

Sebagai contoh, jika anda menggunakan perpustakaan WS untuk Node untuk membuat soket web di pelayan, anda boleh menggunakan pilihan maxPayload untuk menentukan ukuran muatan maksimum dalam bait. Sekiranya saiz muatan lebih besar daripada itu, perpustakaan secara semula jadi akan memutuskan sambungan.

Jangan cuba melaksanakannya sendiri dengan menentukan panjang mesej. Kami tidak mahu membaca keseluruhan mesej ke dalam RAM sistem terlebih dahulu. Sekiranya 1 bait lebih besar daripada had yang kami tetapkan, lepaskan. Itu hanya dapat dilaksanakan oleh perpustakaan (yang menangani mesej sebagai aliran byte dan bukan rentetan tetap).

# 3: Buat protokol komunikasi yang mantap

Kerana sekarang anda menggunakan sambungan dupleks, anda mungkin menghantar apa sahaja ke pelayan. Pelayan boleh menghantar teks apa pun kepada pelanggan. Anda perlu mempunyai cara untuk komunikasi yang berkesan antara kedua-duanya.

Anda tidak boleh menghantar mesej mentah jika anda ingin meningkatkan aspek pemesejan di laman web anda. Saya lebih suka menggunakan JSON, tetapi ada cara lain yang dioptimumkan untuk mengatur komunikasi. Walau bagaimanapun, dengan mempertimbangkan JSON, inilah skema pemesejan asas untuk laman web generik:

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

Kini lebih mudah bagi anda di pelayan untuk memeriksa acara dan format yang sah. Hentikan sambungan dengan segera dan log alamat IP pengguna jika format mesej berbeza. Tidak mungkin format akan berubah melainkan seseorang secara manual menggunakan sambungan soket web anda. Sekiranya anda menggunakan nod, saya cadangkan menggunakan perpustakaan Joi untuk pengesahan lebih lanjut mengenai data masuk dari pengguna.

# 4: Sahkan pengguna sebelum sambungan WS dibuat

Sekiranya anda menggunakan soket web untuk pengguna yang disahkan, adalah idea yang baik untuk hanya membenarkan pengguna yang disahkan untuk membuat sambungan soket web yang berjaya. Jangan biarkan sesiapa membuat sambungan dan kemudian menunggu mereka mengesahkan melalui soket web itu sendiri. Pertama sekali, mewujudkan sambungan soket web agak mahal. Oleh itu, anda tidak mahu orang yang tidak dibenarkan melompat ke soket web anda dan sambungan yang boleh digunakan oleh orang lain.

Untuk melakukan ini, semasa anda membuat sambungan di frontend, hantarkan beberapa data pengesahan ke soket web. Ini boleh menjadi tajuk seperti X-Auth-Token:. Secara lalai, kuki akan tetap diteruskan.

Sekali lagi, ia benar-benar datang ke perpustakaan yang anda gunakan di pelayan untuk melaksanakan soket web. Tetapi jika anda menggunakan Node dan menggunakan WS, ada fungsi verifikasi klien yang membolehkan anda mengakses objek maklumat yang disalurkan ke sambungan soket web. (Sama seperti anda mempunyai akses ke objek req untuk permintaan HTTP.)

# 5: Gunakan SSL melalui soket web

Ini tidak perlu difikirkan, tetapi masih perlu dikatakan. Gunakan wss: // dan bukannya ws: //. Ini menambahkan lapisan keselamatan pada komunikasi anda. Gunakan pelayan seperti Nginx untuk soket web proksi terbalik dan aktifkan SSL di atasnya. Menyiapkan Nginx akan menjadi tutorial lain. Saya akan meninggalkan arahan yang perlu anda gunakan untuk Nginx bagi mereka yang sudah biasa dengannya. Maklumat lanjut di sini.

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"; }

Di sini dianggap pelayan soket web anda mendengar di port 1337 dan pengguna anda menyambung ke soket web anda dengan cara ini:

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

Soalan?

Have any questions or suggestions? Ask away!