Skip to content

Getting full request body in Request Guard or Request Fairing #2952

@luckydonald

Description

@luckydonald

What's missing?

Hi there. I'm trying to implement the server side of AWS SigV4 Authentication algorithm.
https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_sigv-create-signed-request.html

With other frameworks I'd create a middleware which can calculate the required value, and allows or denies the requests accordingly.

With rocket it seems to be quite difficult.

Here's a nice summary of the Algorithm used by AWS:
https://towardsaws.com/aws-sigv4-in-3-mins-c324d20f19cf

So all I have to do is collect url, some headers and the payload,

#Create a Canonical Request
CanonicalRequest =
  HTTPRequestMethod + '\n' +
  CanonicalURI + '\n' +
  CanonicalQueryString + '\n' +
  CanonicalHeaders + '\n' +
  SignedHeaders + '\n' +
  HexEncode(Hash(RequestPayload))

and then shove that through HMAC a few times in different configuration, together with a secret which both the server and the client know.
The resulting string is then compared with the Authorization header, to be equal.

Here's an example with requwests:
https://github.com/aws-samples/sigv4-signing-examples/blob/main/no-sdk/rust/src/main.rs

So, my idea is to use a Request Guard,

#[rocket::async_trait]
impl<'r> FromRequest<'r> for AuthCheckPassed {

Unfortunately, those can't access the body, which is needed in the signing process (RequestPlayload above).

I thought to combine it with an Request fairing (https://docs.rs/rocket/latest/rocket/fairing/trait.Fairing.html#method.on_request), which could then inspect the body, and calculate the Header to compare with.
It would then use request-local state (https://rocket.rs/guide/v0.5/state/#request-local-state) to store it, and have a FormRequest pick it back up again and do the final check against the Authorization header (or check a comparison result boolean), as the Fairing itself can't return a "permission denied" response.

While this is cumbersome and inconvenient, it sounds doable, unless you realize not even the Fairings have full access to the data.
The maximum upload part size is however allowed to be 5GiB (see AWS docs: https://docs.aws.amazon.com/AmazonS3/latest/userguide/qfacts.html).

In the code for Data.peek, however the length is hardcoded to 512 bytes, which is not even close.
Using something like Limits::new().limit("json", ByteUnit::Megabyte(1000 * 1024 * 1024)) will not change that.

Unfortunately, data is of type data: &mut Data<'_> instead of data: Data<'_>, making the open() function to get a buffer unavailable (can't move).

TL;DR: The Problem

With the current rocket, it is impossible to implement the industry standard Amazon Webservices SigV4 Authentication algorithm as laid out above.

Ideal Solution

No response

Why can't this be implemented outside of Rocket?

Auth and inspecting the request should be part of the webserver (rocket). Having a reverse proxy just for what would be a simple middleware in other frameworks makes no sense.

Are there workarounds usable today?

Not really, no.

The closest thing is using Data.peek and hoping the request will never be more than 512 bytes (uuuh bad idea, it will be)

Alternative Solutions

Use a different framework.

Additional Context

This is related to the closed #700, which has a response similar to "Q: how do I do that? A: You don't, your use case is stupid.", which is not helpful (good old stack overflow vibes).

System Checks

  • I do not believe that this feature can or should be implemented outside of Rocket.
  • I was unable to find a previous request for this feature.

Metadata

Metadata

Assignees

No one assigned

    Labels

    requestRequest for new functionality

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions