Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add (more) CSRF protection #14

Open
SergioBenitez opened this issue Sep 25, 2016 · 55 comments
Open

Add (more) CSRF protection #14

SergioBenitez opened this issue Sep 25, 2016 · 55 comments
Assignees
Labels
enhancement A minor feature request
Milestone

Comments

@SergioBenitez
Copy link
Member

CSRF is a thing. Protect against it automatically in forms.

@SergioBenitez SergioBenitez added this to the 0.2.0 milestone Sep 25, 2016
@SergioBenitez SergioBenitez added the enhancement A minor feature request label Sep 25, 2016
@SergioBenitez SergioBenitez changed the title Add CSRF prevention mechanism Add CSRF protection Sep 25, 2016
@SergioBenitez SergioBenitez modified the milestones: 0.3.0, 0.2.0 Sep 29, 2016
@jrozner
Copy link

jrozner commented Jan 8, 2017

Tying this specifically to forms is a bad idea because it makes supporting Single Page Applications (SPAs) written in JavaScript more difficult and can break support for AJAX requests that use json/xml APIs. It's common for those applications to send the token in an HTTP request header instead of in the form body, especially if the body isn't a standard form encoded value (eg. JSON, XML), because it's easier to simply add a header before a request is sent than have to rewrite the DOM each time a request is made.

A common way to handle this is to provide two code paths for the check determined by the presence and value of the X-Requested-With header. jQuery specifically, and other AJAX libraries, set the X-Requested-With header before sending a request and set the value to "XMLHttpRequest". In the case that the header isn't set or it's not that value defaulting to looking in the form payload for the specified value is the correct place to find the token. Otherwise, if the header is set and it denotes an xhr you look in the header for the token.

It's also worth noting that a reasonable implementation should depend on sessions existing for storage of the token. I'm happy to help work on this if it's not already being done.

@golddranks
Copy link
Contributor

golddranks commented Jan 13, 2017

I'd like to also have CSRF protection by default on when accessing the site with any mutating HTTP method, like POST, PUT or DELETE. At least check the origin + referer by default in these cases, and make it easy to additionally annotate routes that need checking, plus make it easy to have a HTTP header or cookie based check too.

@scull7
Copy link

scull7 commented May 3, 2017

Is there anyone working on this? If not, I wouldn't mind having a go at it. I believe that checking for a header token and then falling back to the form body would cover most cases (80/20 rule).

@SergioBenitez
Copy link
Member Author

With a rather large sigh of grief, I'm pushing this to 0.4.

Implementing this in a simple, efficient, and straightforward manner has been a challenging endeavor. While it's simple to validate the incoming request against CSRF, it's not simple to emit a response with the appropriate information for later validation. In particular, we'd like to make it easy and efficient to insert a CSRF token into a web form in a template. To complete the defense mechanism, we need to insert the same token into a cookie. This presents a challenge: how do we know that a token has been inserted into a template so that we can then use the same token as a cookie in the response?

I have two candidate solutions, each with a somewhat large drawback.

  1. Use a global per-thread token generator.

    The per-thread token generator would expose three functions/methods, get_token() -> Token, reset() and last_token() -> Option<Token>. When a new request is received, reset() is called. The call should be a noop under most conditions. A template helper is exposed that calls get_token() to get an appropriate CSRF token. When Template generates the final response, it calls last_token() after rendering the template to determine if a CSRF token was used. If one was used, it is inserted into the response as a cookie.

    There are two major drawback here. First, we've introduced "global" state. Rocket works hard to keep all state local; this would be the only "global" state. Second, this approach won't work when more than one task maps to a single thread (i.e, with coroutines or futures).

  2. Add support for rendering state to existing template engines.

    In this approach, each template engine is extended with the ability to hold "rendering" state: state that is generated by helpers at render time. This means that we can implement a CSRF token generator helper than records, in local template state at render time, if and which token was generated. This even allows multiple tokens to be generated for a given template. The Template responder then simply queries this state after rendering the template to retrieve the CSRF token, if any, and inject it as a cookie into the response.

    The drawback to this approach is that we need buy-in from template engine authors. It also means that we would depend on this feature being present for any newly supported template engines if automatic CSRF protection is desired.

I believe that choice #2 above is a better approach. I'm against adding any kind of global state, though I'll note that this is precisely what other frameworks use to implement this kind of automatic CSRF protection. Any thoughts, suggestions, or ideas on this matter would be greatly appreciated. This is an important feature to have, and I'm excited to really nail down a good solution here.

@SergioBenitez SergioBenitez modified the milestones: 0.4.0, 0.3.0 Jun 2, 2017
@jrozner
Copy link

jrozner commented Jun 3, 2017

I'm not following why global state is needed for implementing the token system or why the design of 1 is being considered. Validation and generation can always happen at request time before a response with the template ever occurs. Typically the way most frameworks handle this is when a request comes in the framework determines if the request requires a token and if it needs to be validated. If not, it's simply a nop and the the request/response cycle goes through as expected. In the case where a token needs to be validated one of two events can happen:

  1. The token is valid and a new token is generated/rotated (this should be stored in the session) and the request continues
  2. The token is invalid/missing and a new token is generated/rotated (this should be stored in the session) and the request is stopped

You should never need to know a prior token's value and that information should not need to be kept around or generated at template generation time. Furthermore you also shouldn't need to expose a global, the template's scope just needs to include access to the session where the token can be taken out of it.

@SergioBenitez
Copy link
Member Author

As I said in my previous comment:

While it's simple to validate the incoming request against CSRF, it's not simple to emit a response with the appropriate information for later validation.

Validation is easy, as you've determined. As is generating a new token. The hard part (for us) is inserting that token in two places: a template and a cookie. We want to use a new token for each response requiring one, and we don't want to generate a new token unless we're going to eventually use it. Thus, generating a new token each time a request is received is a non-starter.

You say:

...the template's scope just needs to include access to the session where the token can be taken out of it.

But that's exactly part of the issue; how do you do that? You've already assumed that there's some global session that we can simply refer to: there isn't. We also can't simply inject information into a template's scope: the user specifies the scope. It is conceivable that we can extend existing template engines with a second, framework provided scope. This would be equivalent to my second proposal except with information flowing the inverse direction.

Let me try to be as clear as possible. In a template, a user should be able to write something like:

<form ..>
  <!-- in practice, a form helper will generate the form tag AND the token field -->
  {% csrf_token %}
</form>

Rocket should generate the CSRF token hidden field from the csrf_token helper. Then, when a response is sent to the client, Rocket should include a CSRF token cookie with the same value as that in the form field. Rocket shouldn't send a cookie unless a token was used (though there's no harm in doing so), and Rocket should generate a new token for each response requiring a token.

You should never need to know a prior token's value and that information should not need to be kept around or generated at template generation time.

In my proposal, last_token() returns the token that was just generated so that a cookie can be created with its value, not a token used in a previous response. This is required information; there's no way around it. Something needs to know which token value was used in the response's HTML to include it in the response as a cookie. Other web frameworks use global state (such as server-side sessions) to do this; we don't have that ability.

@jrozner
Copy link

jrozner commented Jun 3, 2017

I understand the issue now. One suggestion I have, and this may not be the popular opinion or desired behavior, is to not try and push the token injection into the templates as part of rocket. I think unless rocket is dictating the template/rendering engine. The reason I say this is a couple reasons: First, There's going to be a lot of variation and support needed as well as buy in from all the developers working on the engines. Second, The updating of the tokens itself is going to be very app specific and going to be difficult to impossible to handle without providing a frontend js component like Rails does with it's UJS. If the application is using xhr the template may not be re-rendered and rotating the token on the server side (especially if it wasn't validated) will immediately break the page.

In my day job we had to solve this problem for apps that use our security plugin and what we did was append some JavaScript to the end of the any responses that have a handful of characteristics (content-type, etc.). This puts a small wrapper around the xhr code as well as adds an event listener into the dom and delegates to a handful of elements (submit, a, button) to add tokens into xhr requests from a cookie do some clever dom manipulation to drop the token into forms before the request is submitted. This let's us avoid parsing the response and manipulating it to try and put the tokens into the forms. The parsing wont be an issue for Rocket because the location is going to explicitly be set for where insertion is needed but this would solve the issue of tokens updating and the dom not being rewritten.

This solution is sort of out of the scope of Rocket as a rust based web framework but it's a solution that we've found solves some of these problems well.

@DemiMarie
Copy link

@SergioBenitez May I ask why?

Generating a token is easy. It should be as simple as grabbing a few bytes from an optimized implementation of ChaCha20 or (if AES-NI is available) AES256-CTR. Much, much less effort than the upstream proxy server spent decrypting the incoming HTTPS request.

Regarding sessions: Rocket already supports authenticated encryption of cookies. That means that we can store the token client side without any security loss. Client side code can still read the token, but can’t tamper with it without being detected (resulting in a 400 Bad Request error), nor can it make any sense of its contents.

Finally, just checking Origin and Referer headers would go a LONG way towards improving security.

@jrozner
Copy link

jrozner commented Aug 31, 2017

@DemiMarie so I'm sure that Sergio will have more to add to this but it's not so much the generation that's difficult but the submitting the tokens with requests. The problem becomes getting the token into the DOM for form based requests and into XHR requests. If the goal is to provide server side templating with support for csrf tokens (which it appears this is a goal) the hard part is getting the token into the template and updating the template.

Rails solved this by providing some helpers with global state on the server side rendering end and some additional JS that was part of their UJS package. Global state is undesired in this case which complicates the problem significantly.

One solution that might be worth looking at is providing two separate pieces of middleware. One to handle generation/validation of tokens as well as adding them into responses for JS SPA frameworks, such as angular, to use that already have built in support for this. Then provide a second middleware or component that adds support for the helpers on the server side rendering.

If you're looking for more ideas I spoke at DEF CON this year about a solution I outlined above that included some JS that is injected to provide support client side. It seemed that there wasn't interest in a solution like this but just to offer more options. https://www.youtube.com/watch?v=foFf4gMbL2w

@DemiMarie
Copy link

@jrozner What about having some sort of per-request state?

I really really really don’t want the solution to be dependent on JS. I personally use NoScript, for instance, and believe that a simple form should not require JS to submit. There are also accessibility issues, if I recall correctly, though I don’t remember what they are.

@jrozner
Copy link

jrozner commented Aug 31, 2017

@DemiMarie The issue isn't so much about the state at the handler point but providing the state to the templating engine in a way that the templating engine can actually use it. The state in sessions is probably fine at the handler level but getting that into a template is more complicated.

Part of the problem is attempting to solve this for all cases and Rocket not being opinionated allowing you to bring basically any templating with you or bring none (eg. no server side rendering and returning JSON).

I realistically don't see a possible solution that isn't split into multiple parts because a non-js dependent solution that allows for server side rendering but also for SPAs (or at least requests that don't re-render) is incompatible and solve very different problems. One problem is generation, validation, and storage of tokens while the other is insertion of tokens into requests and updating tokens after responses.

Supporting arbitrary templating engines isn't really a csrf problem but is required to be solved for server side rendering in Rocket and providing necessary support. Either adapters likely will need to be provided for specific templating engines to provide a common interface for this or maybe an alternative solutions. What I'm proposing here is build the functionality for dealing with generation, validation, and providing tokens in responses so this becomes available now for a large majority of users and applications that don't have the specific requirements you do. At this point, whether you like it or not, NoScript is not viable for most parts of the web and in most cases JS based SPAs have won and their frameworks handle the token insertion for you.

@ssokolow
Copy link

At this point, whether you like it or not, NoScript is not viable for most parts of the web and in most cases JS based SPAs have won and their frameworks handle the token insertion for you.

Unless you've got some objective research here, I'm going to say we're both suffering from selection bias. I certainly don't see them as having won, nor do I see NoScript as being non-viable.

@jrozner
Copy link

jrozner commented Aug 31, 2017

@ssokolow I'll cede to that and accept that you might be right here. Given trends in web development I doubt it but it's very possible. However, the earlier point about separate problems I think still holds and is relevant.

@seanlinsley
Copy link

https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Checking_the_Referer_Header

Checking the Referer is a commonly used method of preventing CSRF on embedded network devices because it does not require any per-user state. This makes Referer a useful method of CSRF prevention when memory is scarce or server-side state doesn't exist. This method of CSRF mitigation is also commonly used with unauthenticated requests, such as requests made prior to establishing a session state which is required to keep track of a synchronization token.

It appears that checking Origin & Referer are sufficient, except when:

  • the user is behind a proxy (e.g. a corporate customer), which seems like an exceptional use case that we shouldn't be burdened with supporting
  • your site has an open redirect vulnerability and also has a GET request that performs changes, in which case you have bigger problems

@ssokolow
Copy link

ssokolow commented Nov 25, 2017

It appears that checking Origin & Referer are sufficient, except when:

Here are another two reasons which CSRF protection via the Referer header is inadequate:

  1. The Referer header has never been more unreliable, thanks to the concerns about being tracked around the web and extensions which meddle with the header to prevent the risk that sites like Javascript CDNs and Google Font Library might be using IP+Referer to track them.

  2. Proper CSRF protection is robust against attacks using ALL kinds of user agents, including ones that can fully customize the headers that get sent. That's why the OWASP page you link puts the word "AND" in all caps when saying that you should check Origin/Referer AND a CSRF token.

@jrozner
Copy link

jrozner commented Nov 25, 2017

@seanlinsley suggesting that it's reasonable to simply have an application not work for users behind a proxy is a pretty concerning stance. Especially if the application can't explain why it's not working for the user. We shouldn't make users choose between security and functionality when there are solutions available that don't require that.

@seanlinsley
Copy link

@ssokolow could you link to such browser extensions? Is there data to suggest that they block the referer header indiscriminately on any website, even when navigating from page to page on the same domain? (as opposed to stripping the header once it becomes a cross-domain request)

I'm not sure I understand the argument in point 2. Could you elaborate?

@jrozner AFAICT proxies in general don't remove the header unless specifically configured to.

This is entirely explainable to end users. For example:

Unable to verify your request. Please try again after turning off privacy extensions in your browser, or moving to a different internet connection.

@ssokolow
Copy link

ssokolow commented Nov 26, 2017

Firefox has it built in through these about:config keys:

  • network.http.sendRefererHeader
  • network.http.referer.trimmingPolicy
  • network.http.referer.XOriginTrimmingPolicy
  • network.http.referer.XOriginPolicy
  • network.http.referer.spoofSource

...though I prefer to use an extension like RefControl (legacy) or Smart Referer (WebExt) which implements a whitelist similar to NoScript.

(RefControl, which I've used for years and which I'll continue to use until I get off Firefox 52ESR, allows you to choose between Block (no referrer), Forge, and Allow, both for the default policy and for per-domain rules... including ones which only apply to 3rd-party requests.)

For Chrome, you either launch it with --no-referrers (also possible by editing Preferences) or use an extension like Referer Control which, again, provides NoScript-like support for per-site rules.

@DemiMarie
Copy link

DemiMarie commented Nov 26, 2017 via email

@ssokolow
Copy link

ssokolow commented Nov 26, 2017

...as for point 2, my argument is that I don't think it's robust enough to rely exclusively on headers for CSRF protection and the OWASP document seems to agree.

The whole point of the token-based approach is twofold:

  1. It's robust in the face of overly-strict security measures. (No corporate proxy would block HTTP POST or session cookies because they're so fundamentally necessary.)
  2. No matter what a user's browser may allow or do to the request, it's still necessary that two requests be made, in the proper order, within the timeout period for the CSRF token, and that the token be communicated from one request to the other in a sufficiently automatic fashion... and, if at all possible, without the user noticing and calling their bank to report the fraudulent transaction. That's a much trickier thing to find an exploit to allow.

Heck, I watched a talk where one of the takeaways was "Don't assume your design's custom fonts will load. There are still corporate proxies that don't have headers like Origin on their whitelist."

@seanlinsley
Copy link

FWIW the above-linked Smart Referer browser extension only removes the header when navigating cross-domain.

From my point of view, if you have corporate customers you can either afford the budget to write a ton of custom code, or you can get them to whitelist your site so the headers aren't stripped.

Given @SergioBenitez's desire not to add global state, and the complexity involved in building a common API for multiple possible template engines, this seems like a reasonable solution that will work for almost all users. The perfect is the enemy of the good, etc.

@DemiMarie
Copy link

DemiMarie commented Nov 26, 2017 via email

@ssokolow
Copy link

ssokolow commented Aug 3, 2018

Also, a CSRF token can be used to ensure users are following a certain request flow, while something like SameSite would allow an attacker to attack any part of your site from any other part of your site if they find an XSS vulnerability.

@SergioBenitez
Copy link
Member Author

I'm pushing this to 0.5 in an effort to get 0.4 out as soon as possible. Ideally this is in a 0.4 point release, however.

@PrismaPhonic
Copy link
Contributor

Any update to this feature?

@Jayshua
Copy link

Jayshua commented Nov 12, 2019

So I thought I'd just put in my two cents. @jrozner's comment suggest an answer that makes sense to me, but maybe I'm just missing something.

Part of the problem is attempting to solve this for all cases and Rocket not being opinionated allowing you to bring basically any templating with you or bring none.

Since rocket supports simply not using templates, the CSRF support should just be totally independent of templates. Therefore, whatever method you use to create a response, it's up to you to insert the CSRF token. Something like this maybe:

#[get("/login")]
fn login(form_data: FormData, csrf: Csrf) -> Template {
   ...
   Template::render("login", json!({
    csrf: csrf.token(),
    ...
  })
}

Or, if you're not using templates, provide the token to whatever system you are using to generate a response.

This seems like a reasonable solution to me because, ultimately, it is up to you to get the CSRF into the DOM. Even if the templating system is extended to support framework state as was suggested, it would still be up to the user to get the token into the DOM anyway with {% csrf %}.

But again, maybe I'm just missing something here.

@kylone
Copy link

kylone commented Feb 16, 2020

@dgriffen The SameSite attribute is still a draft and isn't supported by some major browsers including the current major releases of Safari on macOS and iOS (though is in the next major release). According to caniuse.com, you can expect about 2/3 of your users' browsers to support the attribute, leaving 1/3 of users in the dust.

Support for SameSite is now more wide spread, if you're only considering supported browsers, according to caniuse.com.

@Aloso
Copy link

Aloso commented May 20, 2020

The way I see it, if you don't want to rely on browser support for SameSite cookies, sessions are required; it's the only way to make sure that two different users don't have the same CSRF token.

If you have a stateless API without a session cookie (e.g. a REST API), you need another way to authenticate each request, which makes CSRF basically impossible.

You can implement a Session type containing a CSRF token as a request guard. You need a global, concurrent hashmap containing the sessions, so parts of it must be implemented in user code.

Proof of concept:

#[derive(PartialEq)]
struct CsrfToken(String);

#[derive(Hash)]
struct SessionId(String);

struct Session {
    csrf_token: CsrfToken,
    // ...other fields
}

impl<'a, 'r> FromRequest<'a, 'r> for &'r mut Session { ... }

impl<'a, 'r> FromRequest<'a, 'r> for CsrfToken {
    type Error = ();

    fn from_request(request: &'a Request<'r>) -> request::Outcome<CsrfToken, ()> {
        let token = CsrfToken(request.get_param("CSRF_TOKEN")?.to_string());
        let session = request.guard::<&mut Session>()?;
        if &session.csrf == &token {
            Outcome::Success(token)
        } else {
            Outcome::Failure(())
        }
    }
}

#[derive(Default)]
struct SessionMap {
    sessions: DashMap<SessionId, Session>,
}

rocket::ignite()
    .manage(SessionMap::default())

@DemiMarie
Copy link

What about the stateless approach based on Origin and Referer headers?

@Aloso
Copy link

Aloso commented May 24, 2020

@DemiMarie according to this article a server shouldn't rely on the fact that either of these headers is present. However, the Origin and Referer headers can be verified, if they are present, in addition to using a cryptographically strong CSRF token.

I just learned about another nice way to store CSRF tokens without requiring a session: The double submit cookie technique. It means that the CSRF token is stored in an HTTP cookie and not on the server, which eliminates the need for sessions or global state.

  • When the server creates a form, it generates a CSRF token. This token is added both as an HTTP cookie and as an invisible field to the form. If the cookie is already present, it can be reused.
  • When the server receives a request from the form, it compares the CSRF token in the invisible field with the cookie. If they don't match or either of them is missing, the request is rejected.

For additional security, the cookie containing the CSRF token can be encrypted or hashed with a secret salt.

@SergioBenitez SergioBenitez modified the milestones: 0.5.0, 0.6.0 Aug 6, 2020
@kotovalexarian
Copy link

kotovalexarian commented Oct 16, 2020

I've started working on some CSRF protection: https://github.com/kotovalexarian/rocket_csrf

It is not safe enough maybe because it doesn't encrypt authenticity token included in forms. I'm not sure why it's needed to encrypt it but Ruby on Rails does this. Also my implementation doesn't provide any template helpers. However it may be a good working prototype to see how CSRF protection can be done in Rocket in idiomatic way. Tell me if it is non-idiomatic.

I'm ready to transfer crate name (rocket_csrf) to @SergioBenitez if needed.

Here is an example:

#![feature(decl_macro)]

#[macro_use] extern crate rocket;
#[macro_use] extern crate serde_derive;

use rocket::response::{Flash, Redirect};
use rocket::request::{FlashMessage, Form};
use rocket_contrib::templates::Template;
use rocket_csrf::CsrfToken;

#[derive(Serialize)]
struct TemplateContext {
    authenticity_token: String,
    flash: Option<String>,
}

#[derive(FromForm)]
struct Comment {
    authenticity_token: String,
    text: String,
}

fn main() {
    rocket::ignite()
        .attach(rocket_csrf::Fairing::new())
        .attach(Template::fairing())
        .mount("/", routes![new, create])
        .launch();
}

#[get("/comments/new")]
fn new(csrf_token: CsrfToken, flash: Option<FlashMessage>) -> Template {
    let template_context = TemplateContext {
        authenticity_token: csrf_token.0,
        flash: flash.map(|msg| format!("{}! {}", msg.name(), msg.msg())),
    };

    Template::render("comments/new", &template_context)
}

#[post("/comments", data = "<form>")]
fn create(csrf_token: CsrfToken, form: Form<Comment>) -> Flash<Redirect> {
    if let Err(_) = csrf_token.verify(&form.authenticity_token) {
        return Flash::error(
            Redirect::to(uri!(new)),
            "invalid authenticity token",
        );
    }

    Flash::success(
        Redirect::to(uri!(new)),
        format!("created comment: {:#?}", form.text),
    )
}

@JonasCir
Copy link

Thank you for working on this! :) One improvement could be to create an attribute-like macro which takes care of the token verification and the early return such that it gets harder to introduce errors. Or maybe post and put macros could take an additional argument e.g., #[post("/comments", data = "<form>", csrf)] or similar.

@JonasCir
Copy link

I am not sure about the token encryption, but I'll do some research and get back to you

@kotovalexarian
Copy link

One improvement could be to create an attribute-like macro which takes care of the token verification and the early return such that it gets harder to introduce errors.

This is mentioned in TODO. It requires data guard because usual guards can't extract form data from request AFAIK. Anyway, thank you for mentioning this, I'll try to focus on this task first.

@NilsIrl
Copy link

NilsIrl commented Oct 17, 2020

It requires data guard because usual guards can't extract form data from request AFAIK.

I think this may still require #775

@kotovalexarian
Copy link

kotovalexarian commented Oct 17, 2020

Yes. I just found that only single data guard can be used in a route. So there is no way to avoid authenticity token validation code in a route for now.

@mjanda
Copy link

mjanda commented Oct 19, 2020

@kotovalexarian it was possible to peek into first bytes of request data and read token value this way, but it's impossible in current master. See #1438

@ejmg
Copy link

ejmg commented Oct 24, 2020

Since Rocket doesn't have built in CSRF support yet and this hasn't been mentioned in the comments yet, it's worth noting that a ready-to-go fairing already exists with Plume's csrf_rocket. It can auto-inject CSRF tokens for you into all routes (along with blacklisting, etc) and can handle CSRF verification for you - alternatively, you can use the fairing just for it's CSRF guard and do the injection yourself.

I currently have a private fork of my own that relies on the checking mechanism while updating the crypto -- I accidentally converged on Plume's solution after realizing the tradeoff between using Rocket's built in form support and defining my own data guard for CSRF -- but the fairing as it exists is very good and should work as a stopgap until Rocket has built in CSRF utilities.

@trinity-1686a
Copy link

trinity-1686a commented Oct 24, 2020

As the main developer of rocket_csrf, I'd recommend against using the auto-injecting feature, while it works most of the time, it has history of corrupting content. It is fixed as far as I know, but still quite fragile.

@MorezMartin
Copy link

Hi all,
I am trying rust & and rocket. It is the first time i try to develop an REST API.
I try to make it secure (xss, csrf) and i don"t use templates.
I use cookies in order to store User id (uuid / nanoid) when user log in.
I use crsf_token in a get function/route in order to get token (not implemented yet but we'd say that user must be login, not difficult to do just add cookie guard).
I ask for this csrf_token for POST operations like update / delete User. For very sensitive changes like password / email, i also check the request have a correct password.

Is this a good way of doing ?
I thought about use this API with Zola static website builder, i think i can create a form template, with a hidden field csrf_token, which can be filled with a GET request, is this ok ?

Thanks in advance,
Sorry for my poor english and being a newbie,
Martin

@elibroftw
Copy link

elibroftw commented Dec 17, 2022

This is long past due. Rocket.rs claims to be ready for production. There's two libraries for CSRF protection so far. I'm hesitant to use both since I'm making a youtube tutorial on how to use templates and forms in Rocket.rs and the last thing I want to do is give people improper information.

kotovalexarian/rocket_csrf says the project may not be ready for production due to a simple implementation and Trinitiy's Plume-org/rocket_csrf is undocumented.

The way I see it, Rocket explicitly refers to Flask and even support Tera which is inspired from Jinaj2.
flask_wtf supports csrf protection for its forms as well as agnostic uses. They support agnostic cases by automatically injecting a csrf_token function into the template context. The function implementation can be seen here https://github.com/wtforms/flask-wtf/blob/main/src/flask_wtf/csrf.py#L23 which makes use of https://github.com/pallets/itsdangerous/blob/aa7e016aae5ddc66517b37b64377352b88d0bcb3/src/itsdangerous/url_safe.py#L76

I really hope someone can implement the Rocket csrf library without a disclaimer. If the implementation might take only a couple days I might try my hand at it.

I also found https://www.stackhawk.com/blog/rust-csrf-protection-guide-examples-and-how-to-enable-it/ but it's so maybe there might not be a need to create a new library.

@SergioBenitez SergioBenitez changed the title Add CSRF protection Add (more) CSRF protection Apr 5, 2023
@SergioBenitez
Copy link
Member Author

SergioBenitez commented Apr 5, 2023

This is long past due. Rocket.rs claims to be ready for production. There's two libraries for CSRF protection so far. I'm hesitant to use both since I'm making a youtube tutorial on how to use templates and forms in Rocket.rs and the last thing I want to do is give people improper information.

Note that Rocket readily and automatically leverages the SameSite cookie attribute, which as of today means that 95%+ of your users are automatically protected against CSRF. I do agree that Rocket should still implement token-based CSRF countermeasures, but it is important to note that for the vast majority of cases and users, Rocket already automatically protects your application and its users.

@SergioBenitez SergioBenitez self-assigned this Dec 12, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement A minor feature request
Projects
Status: In progress
Development

No branches or pull requests