Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@
**Vulnerability:** The `verify_github_sig` function in `apps/server/src/infra_routes.rs` verified GitHub webhook signatures using standard string comparison `expected.eq_ignore_ascii_case(hex_sig)`.
**Learning:** String comparison functions exit early as soon as a character mismatch is found. In a cryptographic context, this variable response time leaks information and allows an attacker to perform a timing attack to forge a valid HMAC signature by guessing it byte by byte.
**Prevention:** Always use a constant-time comparison library (like `hmac`'s `verify_slice()`) to compare cryptographic hashes, secrets, and signatures to prevent side-channel timing attacks. Avoid custom implementations of standard cryptographic algorithms.

## 2023-10-24 - [Add Security Headers to Backend]
**Vulnerability:** Missing critical security headers (X-Frame-Options, X-Content-Type-Options, Strict-Transport-Security) in Axum HTTP responses.
**Learning:** In axum, the `HeaderName::from_static` method strictly requires the header string to be lowercase (e.g., `x-frame-options`). Using uppercase letters will cause a runtime panic. The `security.rs` file manages static asset and cache headers via middleware where these security headers should logically reside.
**Prevention:** Always verify that all newly added HTTP headers in the axum backend use exclusively lowercase strings when passing to `from_static` or `insert`. Consider creating a unified security middleware in `security.rs` that applies strict defaults (like `nosniff` and HSTS) to all endpoints to enforce defense in depth.
13 changes: 13 additions & 0 deletions apps/server/src/security.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ pub fn cors_layer() -> CorsLayer {
pub async fn static_cache_headers(req: Request<Body>, next: Next) -> Response<Body> {
let path = req.uri().path().to_string();
let mut res = next.run(req).await;

// Ajout des en-têtes de sécurité de base pour toutes les requêtes interceptées
if let Ok(v) = HeaderValue::from_str("DENY") {
res.headers_mut().insert(header::X_FRAME_OPTIONS, v);
}
if let Ok(v) = HeaderValue::from_str("nosniff") {
res.headers_mut().insert(header::X_CONTENT_TYPE_OPTIONS, v);
}
if let Ok(v) = HeaderValue::from_str("max-age=31536000; includeSubDomains") {
res.headers_mut().insert(header::STRICT_TRANSPORT_SECURITY, v);
}

let value = if path.starts_with("/_astro/") {
"public, max-age=31536000, immutable"
} else if path.ends_with(".html")
Expand All @@ -67,5 +79,6 @@ pub async fn static_cache_headers(req: Request<Body>, next: Next) -> Response<Bo
if let Ok(v) = HeaderValue::from_str(value) {
res.headers_mut().insert(header::CACHE_CONTROL, v);
}

res
}
Loading