So I've been doing some research on web security, in which I came upon on this OWASP article on HMAC CSRF tokens. With that in mind, I've been trying to implement it using this middleware:
impl<S, B> Service<ServiceRequest> for CSRFTokenMiddleware<S>
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<EitherBody<B>>;
type Error = Error;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
dev::forward_ready!(service);
fn call(&self, mut request: ServiceRequest) -> Self::Future {
let mut res = self.service.call(request);
Box::pin(async move {
let res = res.await;
set_csrf_token(res.unwrap().response_mut().head_mut(), &request);
res.map(ServiceResponse::map_into_left_body)
})
}
}
I'm not done yet, but it's supposed to set a token after the user successfully logs in. The issue is that self.service.call()
takes ownership of the ServiceRequest
but I need it for my set_csrf_token()
function. Here's how that's like:
pub fn set_csrf_token(response: &mut ResponseHead, req: &ServiceRequest) -> Result<(), ()> {
let csrf_token = generate_csrf_token(req);
let cookie = Cookie::new("csrf", csrf_token);
let val = HeaderValue::from_str(cookie.to_string().as_str()).unwrap();
response.headers_mut().append(SET_COOKIE, val);
Ok(())
}
And the corresponding, generate_csrf_token()
function inside set_csrf_token()
is:
pub fn generate_csrf_token(req: &ServiceRequest) -> String {
let session_id = req.cookie("id").unwrap().value().to_string();
let hmac_key_value = generate_hmac_key_value();
let s_key = hmac::Key::new(hmac::HMAC_SHA256, hmac_key_value.as_ref());
let random_value = Uuid::new_v4();
let message = session_id.clone() + "!" + random_value.to_string().as_str();
let hmac = hmac::sign(&s_key, message.as_bytes());
let hmac_string = hex::encode(hmac.as_ref());
let csrf_token = hmac_string + "." + message.as_str();
csrf_token
}
I thought of making a POST request handler alongside my login function (which would be in the same resource), but that would set the token regardless of whether the user successfully logged in or not.
Would really appreciate any help. Thank you!