- main
- manifest
- deps
- main
- Install
- Run
use fastly::http::{Method, StatusCode};
use fastly::{mime, Error, Request, Response};
use sha1::{Digest, Sha1};
static BACKEND_APP_SERVER: &str = "origin_0";
static BACKEND_SECURITY_CHECK: &str = "origin_1";
const PREFIX_LENGTH: usize = 5;
static LOGIN_HTML: &str = r#"<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Compromised password detection demo</title>
</head>
<body>
<form action="/post" method="post">
<div class="container">
<label for="username"><b>Username</b></label>
<input type="text" placeholder="Enter Username" name="username" required />
<label for="password"><b>Password</b></label>
<input type="password" placeholder="Enter Password" name="password" required />
<button type="submit">Login</button>
</div>
</form>
</body>
</html>"#;
#[derive(serde::Deserialize)]
struct BodyParams {
password: Option<String>,
}
#[fastly::main]
fn main(mut req: Request) -> Result<Response, Error> {
if let (&Method::GET, "/") = (req.get_method(), req.get_path()) {
Ok(Response::from_status(StatusCode::OK)
.with_content_type(mime::TEXT_HTML_UTF_8)
.with_body(LOGIN_HTML))
} else {
let params = req.take_body_form::<BodyParams>().unwrap();
if let Some(plain_cred) = params.password {
let mut hasher = Sha1::new();
hasher.update(plain_cred.as_bytes());
let hashed_cred = hex::encode_upper(hasher.finalize());
let hash_left = &hashed_cred[0..PREFIX_LENGTH];
let hash_right = &hashed_cred[PREFIX_LENGTH..];
let api_url = format!("https://api.pwnedpasswords.com/range/{hash_left}");
let api_req = Request::get(api_url);
let mut api_res = api_req.send(BACKEND_SECURITY_CHECK)?;
let api_res_body = api_res.take_body_str();
let result = if api_res_body.contains(hash_right) {
"compromised-credential"
} else {
"safe-credential"
};
req.set_header("fastly-password-status", result);
}
Ok(req.send(BACKEND_APP_SERVER)?)
}
}