Skip to content

Latest commit

 

History

History
136 lines (94 loc) · 5.77 KB

ch_06.md

File metadata and controls

136 lines (94 loc) · 5.77 KB

Chapter 6 - Reject Invalid Subscribers #1

  • input validation

Defense

Focus on:

  • denial of service - e.g. trying to take our service down to prevent other people from signing up. A common threat for basically any online service;
  • data theft - e.g. steal a huge list of email addresses;
  • phishing - e.g. use our service to send what looks like a legitimate email to a victim to trick them into clicking on some links or perform other actions.

Remedy by:

  • input validation
  • parameterised queries
  • escaping parameterised input in emails

Type Driven Development

Type-driven development is a powerful approach to encode the constraints of a domain we are trying to model inside the type system, leaning on the compiler to make sure they are enforced. The more expressive the type system of our programming language is, the tighter we can constrain our code to only be able to represent states that are valid in the domain we are working in.

tldr; Leverage Rust's type system to enforce domain business logic

  • The tuple struct pub struct SubscriberName(String); whose single field is private and impossible to use or access outside of its module.

With the new signature we can be sure that new_subscriber.name is non-empty - it is impossible to call insert_subscriber passing an empty subscriber name. And we can draw this conclusion just by looking up the definition of the types of the function arguments - we can once again make a local judgement, no need to go and check all the calling sites of our function.

Used to do a cheap reference-to-reference conversion.

This trait is similar to AsMut which is used for converting between mutable references. If you need to do a costly conversion it is better to implement From with type &T or write a custom function.

Is a powerful trait/interface that allows for implicit type conversion.

Panics

Panics in Rust are used to deal with unrecoverable errors: failure modes that were not expected or that we have no way to meaningfully recover from.

  • i.e. host machine running out of memory or a full disk.

If your Rust application panics in response to any user input, then the following should be true: your application has a bug, whether it be in a library or in the primary application code.

  • burntsushi via reddit
  • A panic in a request handler does not crash the whole application.
  • actix-web spins up multiple workers to deal with incoming requests and it is resilient to one or more of them crashing: it will just spawn new ones to replace the ones that failed.

The ? Operator

The question mark operator is syntactic sugar for dealing with fallible functions and you want to "bubble up".

i.e.

insert_subscriber(&pool, &new_subscriber)
  .await
  .map_err(|_| HttpResponse::InternalServerErrojr().finish())?;

is equivalent to this:

if let Err(error) = insert_subscriber(&pool, &new_subscriber)
  .await
  .map_err(|_| HttpResponse::InternalServerError().finish()) {
    return Err(error);
  }

It allows for an early return with minimal code.

But since ? triggers an early return using an Err, it can only be used within a function that returns a Result.

When you use and implement TryFrom/TryInto you're making your intent clearer; "This is a type conversion"!

pub trait TryFrom<T>: Sized {
  /// The type returned in the event of a conversion error.
  type Error;

  /// Performs the conversion.
  fn try_from(value: T) -> Result<Self, Self::Error>;
}

If you provide a TryFrom implementation, your type automatically gets the corresponding TryInto implementation FOR FREE!!!

pub trait TryInto<T> {
  type Error;

  fn try_into(self) -> Result<T, Self::Error>;
}

TryInto's function signature mirrors TryFrom - the conversion just goes the other direction!

form.0.try_into()

// Same Same

NewSubscriber::try_from(form.0)

Terminology

  • Defense in depth is a concept used in information security in which multiple layers of security controls (defense) are placed throughout an information technology (IT) system. Its intent is to provide redundancy in the event a security control fails or a vulnerability is exploited that can cover aspects of: personnel, procedural, technical and physical security for the duration of the system's life cycle.

📦 Used

References