From 30e9620d0a91bb02cf3716b83dcc5620642c463b Mon Sep 17 00:00:00 2001 From: Trinity Pointard Date: Sun, 24 Jun 2018 18:58:57 +0200 Subject: [PATCH] Add csrf protection --- Cargo.lock | 85 +++++++++++++++++++++++++++++++++ Cargo.toml | 4 ++ src/main.rs | 14 +++++- src/routes/blogs.rs | 4 +- src/routes/comments.rs | 6 +-- src/routes/errors.rs | 15 ++++++ src/routes/posts.rs | 4 +- src/routes/session.rs | 4 +- src/routes/user.rs | 6 +-- templates/errors/csrf.html.tera | 6 +++ 10 files changed, 135 insertions(+), 13 deletions(-) create mode 100644 templates/errors/csrf.html.tera diff --git a/Cargo.lock b/Cargo.lock index 1ab9542b..1984d244 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -213,6 +213,14 @@ dependencies = [ "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "colored" version = "1.6.0" @@ -295,6 +303,24 @@ dependencies = [ "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "csrf" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "data-encoding 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "data-encoding" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "derive-error-chain" version = "0.11.1" @@ -429,6 +455,11 @@ dependencies = [ "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "gcc" +version = "0.3.54" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "generic-array" version = "0.9.0" @@ -960,6 +991,7 @@ dependencies = [ "rocket 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)", "rocket_codegen 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)", "rocket_contrib 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)", + "rocket_csrf 0.1.0", "rocket_i18n 0.1.1 (git+https://github.com/BaptisteGelez/rocket_i18n?rev=5b4225d5bed5769482dc926a7e6d6b79f1217be6)", "rpassword 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1117,6 +1149,23 @@ dependencies = [ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "redox_syscall" version = "0.1.37" @@ -1247,6 +1296,17 @@ dependencies = [ "tera 0.11.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rocket_csrf" +version = "0.1.0" +dependencies = [ + "csrf 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "data-encoding 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rocket 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)", + "serde 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rocket_http" version = "0.4.0-dev" @@ -1282,11 +1342,28 @@ dependencies = [ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rust-crypto" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rustc-demangle" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "rustc-serialize" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "safemem" version = "0.2.0" @@ -1944,6 +2021,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8b9d2900f78631a5876dc5d6c9033ede027253efcd33dd36b1309fc6cab97ee0" "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" "checksum chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1cce36c92cb605414e9b824f866f5babe0a0368e39ea07393b9b63cf3844c0e6" +"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b0aa3473e85a3161b59845d6096b289bb577874cafeaf75ea1b1beaa6572c7fc" "checksum cookie 0.11.0-dev (git+https://github.com/alexcrichton/cookie-rs?rev=0365a18)" = "" "checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67" @@ -1953,6 +2031,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum crossbeam-epoch 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9b4e2817eb773f770dcb294127c011e22771899c21d18fce7dd739c0b9832e81" "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" "checksum crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d636a8b3bcc1b409d7ffd3facef8f21dcb4009626adbd0c5e6c4305c07253c7b" +"checksum csrf 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "38f2ee2a7e76740d81de006e61eff53206c56448a30d8017b4ac97b5486682bd" +"checksum data-encoding 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "67df0571a74bf0d97fb8b2ed22abdd9a48475c96bd327db968b7d9cace99655e" "checksum derive-error-chain 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cb4450afbe280461e78299b39182a085b70e3e71be049cf4a588ad72f1e44d33" "checksum diesel 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24815a0c2094f2c8dafe74ab3b9e975892f44acbb94b4d4b4898025a7615efa4" "checksum diesel_derives 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6471a2b637b414d3ee1504cf230409a550381c79204282f8fe06c527e4ae56be" @@ -1969,6 +2049,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum futf 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b" "checksum futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "1a70b146671de62ec8c8ed572219ca5d594d9b06c0b364d5e67b722fc559b48c" "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" +"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" "checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" "checksum gettext-rs 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b8c2412d5758f68a9eeba161f9ecb9a55f56bfdbf17857650b98f2b9b281a47" "checksum gettext-sys 0.19.8 (registry+https://github.com/rust-lang/crates.io-index)" = "62c644c0b8b73706fb8c7420533fd30abf6f41c2703994bc6f0826fceb7fb3d6" @@ -2042,6 +2123,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum r2d2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f9078ca6a8a5568ed142083bb2f7dc9295b69d16f867ddcc9849e51b17d8db46" "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" +"checksum rand 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d0d9f869af32e387d9e0f2bdb64326b8ac84c81d5e55459d0bc7526b0fdb3671" +"checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2" "checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" "checksum regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "aec3f58d903a7d2a9dc2bf0e41a746f4530e0cab6b615494e058f67a3ef947fb" "checksum regex-syntax 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bd90079345f4a4c3409214734ae220fd773c6f2e8a543d07370c6c1c369cfbfb" @@ -2056,7 +2139,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rocket_http 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)" = "" "checksum rocket_i18n 0.1.1 (git+https://github.com/BaptisteGelez/rocket_i18n?rev=5b4225d5bed5769482dc926a7e6d6b79f1217be6)" = "" "checksum rpassword 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d127299b02abda51634f14025aec43ae87a7aa7a95202b6a868ec852607d1451" +"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" "checksum rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11fb43a206a04116ffd7cfcf9bcb941f8eb6cc7ff667272246b0a1c74259a3cb" +"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" "checksum schannel 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "85fd9df495640643ad2d00443b3d78aae69802ad488debab4f1dd52fc1806ade" "checksum scheduled-thread-pool 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a2ff3fc5223829be817806c6441279c676e454cc7da608faf03b0ccc09d3889" diff --git a/Cargo.toml b/Cargo.toml index a904a2da..e1fca138 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,10 @@ features = ["tera_templates", "json"] git = "https://github.com/SergioBenitez/Rocket" rev = "df7111143e466c18d1f56377a8d9530a5a306aba" +[dependencies.rocket_csrf] +git = "https://github.com/fdb-hiroshima/rocket_csrf" +rev = "80687a64a8b9d44e4983e63cca6d707498e92fc7" + [dependencies.rocket_i18n] git = "https://github.com/BaptisteGelez/rocket_i18n" rev = "5b4225d5bed5769482dc926a7e6d6b79f1217be6" diff --git a/src/main.rs b/src/main.rs index 11bb10b9..ab54ad59 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ extern crate plume_common; extern crate plume_models; extern crate rocket; extern crate rocket_contrib; +extern crate rocket_csrf; extern crate rocket_i18n; extern crate rpassword; #[macro_use] @@ -19,6 +20,7 @@ extern crate serde_json; extern crate webfinger; use rocket_contrib::Template; +use rocket_csrf::CsrfFairingBuilder; mod inbox; mod setup; @@ -84,7 +86,9 @@ fn main() { routes::well_known::host_meta, routes::well_known::nodeinfo, - routes::well_known::webfinger + routes::well_known::webfinger, + + routes::errors::csrf_violation ]) .catch(catchers![ routes::errors::not_found, @@ -95,5 +99,13 @@ fn main() { rocket_i18n::tera(&mut engines.tera); })) .attach(rocket_i18n::I18n::new("plume")) + .attach(CsrfFairingBuilder::new() + .set_default_target("/csrf-violation?target=".to_owned(), rocket::http::Method::Post) + .add_exceptions(vec![ + ("/inbox".to_owned(), "/inbox".to_owned(), rocket::http::Method::Post), + ("/@//inbox".to_owned(), "/@//inbox".to_owned(), rocket::http::Method::Post), + + ]) + .finalize().unwrap()) .launch(); } diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index 68fb818b..1e627439 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -1,6 +1,6 @@ use activitypub::collection::OrderedCollection; use rocket::{ - request::Form, + request::LenientForm, response::{Redirect, Flash} }; use rocket_contrib::Template; @@ -55,7 +55,7 @@ struct NewBlogForm { } #[post("/blogs/new", data = "")] -fn create(conn: DbConn, data: Form, user: User) -> Redirect { +fn create(conn: DbConn, data: LenientForm, user: User) -> Redirect { let form = data.get(); let slug = utils::make_actor_id(form.title.to_string()); diff --git a/src/routes/comments.rs b/src/routes/comments.rs index 3fd0f97e..3387f4b9 100644 --- a/src/routes/comments.rs +++ b/src/routes/comments.rs @@ -1,5 +1,5 @@ use rocket::{ - request::Form, + request::LenientForm, response::Redirect }; use serde_json; @@ -27,12 +27,12 @@ struct NewCommentForm { // See: https://github.com/SergioBenitez/Rocket/pull/454 #[post("/~///comment", data = "")] -fn create(blog_name: String, slug: String, data: Form, user: User, conn: DbConn) -> Redirect { +fn create(blog_name: String, slug: String, data: LenientForm, user: User, conn: DbConn) -> Redirect { create_response(blog_name, slug, None, data, user, conn) } #[post("/~///comment?", data = "")] -fn create_response(blog_name: String, slug: String, query: Option, data: Form, user: User, conn: DbConn) -> Redirect { +fn create_response(blog_name: String, slug: String, query: Option, data: LenientForm, user: User, conn: DbConn) -> Redirect { let blog = Blog::find_by_fqn(&*conn, blog_name.clone()).unwrap(); let post = Post::find_by_slug(&*conn, slug.clone(), blog.id).unwrap(); let form = data.get(); diff --git a/src/routes/errors.rs b/src/routes/errors.rs index 1f731017..ee17982d 100644 --- a/src/routes/errors.rs +++ b/src/routes/errors.rs @@ -20,3 +20,18 @@ fn server_error(req: &Request) -> Template { "account": user })) } + +#[derive(FromForm)] +pub struct Uri { + target: String, +} + +#[post("/csrf-violation?")] +fn csrf_violation(uri: Option) -> Template { + if let Some(uri) = uri { + eprintln!("Csrf violation while acceding \"{}\"", uri.target) + } + Template::render("errors/csrf", json!({ + "error_message":"" + })) +} diff --git a/src/routes/posts.rs b/src/routes/posts.rs index 56a5018f..84cdd410 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -1,6 +1,6 @@ use activitypub::object::Article; use heck::KebabCase; -use rocket::request::Form; +use rocket::request::LenientForm; use rocket::response::{Redirect, Flash}; use rocket_contrib::Template; use serde_json; @@ -85,7 +85,7 @@ struct NewPostForm { } #[post("/~//new", data = "")] -fn create(blog_name: String, data: Form, user: User, conn: DbConn) -> Redirect { +fn create(blog_name: String, data: LenientForm, user: User, conn: DbConn) -> Redirect { let blog = Blog::find_by_fqn(&*conn, blog_name.to_string()).unwrap(); let form = data.get(); let slug = form.title.to_string().to_kebab_case(); diff --git a/src/routes/session.rs b/src/routes/session.rs index 60bb4afe..c99548ba 100644 --- a/src/routes/session.rs +++ b/src/routes/session.rs @@ -2,7 +2,7 @@ use gettextrs::gettext; use rocket::{ http::{Cookie, Cookies, uri::Uri}, response::{Redirect, status::NotFound}, - request::{Form,FlashMessage} + request::{LenientForm,FlashMessage} }; use rocket_contrib::Template; @@ -39,7 +39,7 @@ struct LoginForm { } #[post("/login", data = "")] -fn create(conn: DbConn, data: Form, flash: Option, mut cookies: Cookies) -> Result> { +fn create(conn: DbConn, data: LenientForm, flash: Option, mut cookies: Cookies) -> Result> { let form = data.get(); let user = match User::find_by_email(&*conn, form.email_or_name.to_string()) { Some(usr) => Ok(usr), diff --git a/src/routes/user.rs b/src/routes/user.rs index 616268eb..3e47b8fc 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -2,7 +2,7 @@ use activitypub::{ activity::Follow, collection::OrderedCollection }; -use rocket::{request::Form, +use rocket::{request::LenientForm, response::{Redirect, Flash} }; use rocket_contrib::Template; @@ -148,7 +148,7 @@ struct UpdateUserForm { } #[put("/@/<_name>/edit", data = "")] -fn update(_name: String, conn: DbConn, user: User, data: Form) -> Redirect { +fn update(_name: String, conn: DbConn, user: User, data: LenientForm) -> Redirect { user.update(&*conn, data.get().display_name.clone().unwrap_or(user.display_name.to_string()).to_string(), data.get().email.clone().unwrap_or(user.email.clone().unwrap()).to_string(), @@ -166,7 +166,7 @@ struct NewUserForm { } #[post("/users/new", data = "")] -fn create(conn: DbConn, data: Form) -> Result { +fn create(conn: DbConn, data: LenientForm) -> Result { let form = data.get(); if form.username.clone().len() < 1 { diff --git a/templates/errors/csrf.html.tera b/templates/errors/csrf.html.tera new file mode 100644 index 00000000..f55bf13f --- /dev/null +++ b/templates/errors/csrf.html.tera @@ -0,0 +1,6 @@ +{% extends "errors/base" %} + +{% block error %} +

{{ "Invalid CSRF token." | _ }}

+

{{ "Something is wrong with your CSRF token. Make sure cookies are enabled in you browser, and try reloading this page. If you continue to see this error message, please report it." | _ }}

+{% endblock error %}