Skip to main content

TokenCheck

Struct TokenCheck 

Source
pub struct TokenCheck<V> { /* private fields */ }
Expand description

Verifies the anti-forgery token the moment its field ends, before any later part starts: a forged upload is refused before its first file opens a sink, so it costs no disk.

verify is called once, with the value of the text field named name (byte for byte, as TokenField matches it); it does the comparison (csrf-sec’s Csrf::verify, constant time) and returns Err with the refusal to report. The check refuses, with status 403:

  • csrf.missing: a file part starts before a token was verified, or the body ends without one;
  • csrf.duplicate: a second field with the token’s name (two tokens would let an attacker choose which one is verified);
  • csrf.not_text: a token that is not UTF-8;
  • whatever verify returns.

It works for multipart and urlencoded bodies alike, and composes with other inspectors as a pair: (customs, check). Browsers send fields in document order, so the form puts the hidden token input before its file inputs. With TokenField::anywhere, where files may come first, verify captured_token (or the upload’s) after the parse instead. The token field stays in the result; leave it out of a typed form with ignore.

Cost: one name comparison per text field, and verify once.

§Examples

use form_net::{Limits, Policy, Refusal, TokenCheck};

let mut check = TokenCheck::new("csrf_token", |token| {
    if csrf.verify(session, token) { Ok(()) } else { Err(Refusal::new(403, "csrf.rejected")) }
});
let body = b"csrf_token=good&email=a%40b.example";
let form = form_net::parse_urlencoded_inspected(body, &Limits::DEFAULT, &Policy::DEFAULT, &mut check)?;
assert!(check.verified());
assert_eq!(form.get("email"), Some("a@b.example"));

let mut check = TokenCheck::new("csrf_token", |_| Err(Refusal::new(403, "csrf.rejected")));
let err = form_net::parse_urlencoded_inspected(b"csrf_token=bad", &Limits::DEFAULT, &Policy::DEFAULT, &mut check)
    .unwrap_err();
assert_eq!((err.status_hint(), err.code()), (403, "csrf.rejected"));

Implementations§

Source§

impl<V: FnMut(&str) -> Result<(), Refusal>> TokenCheck<V>

Source

pub const fn new(name: &'static str, verify: V) -> TokenCheck<V>

A check of the field name with verify.

Source

pub const fn verified(&self) -> bool

Whether the token was seen and verify accepted it.

Trait Implementations§

Source§

impl<V> Debug for TokenCheck<V>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<V: FnMut(&str) -> Result<(), Refusal>> Inspector for TokenCheck<V>

Source§

fn part_start(&mut self, head: &PartHead<'_>) -> Verdict

A part’s headers were read and passed every rule. Called before any byte of the part reaches a sink.
Source§

fn field(&mut self, name: &str, value: &[u8]) -> Verdict

A complete text field (multipart) or a decoded pair (urlencoded): the value as bytes, so the inspector can apply its own charset rules. Skip leaves the field out of the result.
Source§

fn end(&mut self) -> Verdict

The body ended. Skip here is treated as Continue.
Source§

fn sniff(&mut self, head: &PartHead<'_>, first: &[u8]) -> Verdict

The first min(sniff_bytes, part length) bytes of a file part, as one contiguous slice however the input was split, before any data call for that part.
Source§

fn data(&mut self, part: u32, chunk: &[u8]) -> Verdict

Bytes of part part’s data, text fields included.
Source§

fn part_end(&mut self, summary: &PartSummary) -> Verdict

A part ended. Skip here is treated as Continue.
Source§

fn failed(&mut self, error: &FormError)

The parse failed with error (for any reason, this inspector’s refusal included). Called once; it cannot change the outcome. The hook for security signals: a stream of refusals from one client is a probe.

Auto Trait Implementations§

§

impl<V> Forget for TokenCheck<V>

§

impl<V> Freeze for TokenCheck<V>
where V: Freeze,

§

impl<V> RefUnwindSafe for TokenCheck<V>
where V: RefUnwindSafe,

§

impl<V> Send for TokenCheck<V>
where V: Send,

§

impl<V> Sync for TokenCheck<V>
where V: Sync,

§

impl<V> Unpin for TokenCheck<V>
where V: Unpin,

§

impl<V> UnsafeUnpin for TokenCheck<V>
where V: UnsafeUnpin,

§

impl<V> UnwindSafe for TokenCheck<V>
where V: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.