Skip to content

Commit

Permalink
Merge pull request #222 from sebadob/pre-computed-html-csp
Browse files Browse the repository at this point in the history
pre-compute CSP's at build time and get rid of nonce-headers per request
  • Loading branch information
sebadob committed Dec 25, 2023
2 parents bbf7d4e + cf3c3d7 commit 8fd2c99
Show file tree
Hide file tree
Showing 15 changed files with 249 additions and 444 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ but also even encryption algorithm encryptions really easy in the future.
[65bbfea](https://github.com/sebadob/rauthy/commit/65bbfea5a1a3b23735b82f3eb05a415ce7c51013)
- Migrate to [spow](https://github.com/sebadob/spow)
[ff579f6](https://github.com/sebadob/rauthy/commit/ff579f60414cb529d727ae27fd83e9506ad770d5)
- Pre-Compute CSP's for all HTML content at build-time and get rid of the per-request nonce computation
[]()
- `noindex, nofollow` globally via headers and meta tag -> Rauthy as an Auth provider should never be indexed
[]()

### Bugfixes

Expand Down
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"dev": "DEV_MODE=true vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/app.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="robots" content="noindex, nofollow">
<link rel="icon" href="%sveltekit.assets%/assets/favicon.svg"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<style nonce="{{ nonce }}">
<style>
* { box-sizing: border-box; }
:root {
--col-act1: #6b3d99;
Expand Down
14 changes: 13 additions & 1 deletion frontend/svelte.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import adapter from '@sveltejs/adapter-static';

const isDev = process.env.DEV_MODE === 'true';

/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
Expand All @@ -13,7 +15,17 @@ const config = {
assets: '../static/v1',
precompress: true,
strict: true,
})
}),
csp: isDev ? {} : {
directives: {
'default-src': ['none'],
'connect-src': ['self'],
'script-src': ['self', 'wasm-unsafe-eval'],
'style-src': ['self', 'unsafe-inline'],
'img-src': ['self', 'data:'],
},
},

},
};

Expand Down
4 changes: 2 additions & 2 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,8 @@ build-ui:
sed -i 's/#383838;/{{{{ col_text }};/g' "$html"
sed -i 's/#f7f7f7;/{{{{ col_bg }};/g' "$html"
# for the nonce in the CSP for script files
sed -i 's/<link /<link nonce="{{{{ nonce }}" /g' "$html"
sed -i 's/<script>/<script nonce="{{{{ nonce }}">/g' "$html"
#sed -i 's/<link /<link nonce="{{{{ nonce }}" /g' "$html"
#sed -i 's/<script>/<script nonce="{{{{ nonce }}">/g' "$html"
done;
done

Expand Down
4 changes: 1 addition & 3 deletions rauthy-common/src/error_response.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::constants::{APPLICATION_JSON, HEADER_DPOP_NONCE, HEADER_HTML, HEADER_RETRY_NOT_BEFORE};
use crate::utils::build_csp_header;
use actix_multipart::MultipartError;
use actix_web::error::BlockingError;
use actix_web::http::header::{
Expand Down Expand Up @@ -75,10 +74,9 @@ impl ErrorResponse {
}
}

pub fn error_response_html(&self, body: String, nonce: &str) -> HttpResponse {
pub fn error_response_html(&self, body: String) -> HttpResponse {
HttpResponseBuilder::new(self.status_code())
.append_header(HEADER_HTML)
.append_header(build_csp_header(nonce))
.body(body)
}
}
Expand Down
9 changes: 0 additions & 9 deletions rauthy-common/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,6 @@ const B64_URL_SAFE: engine::GeneralPurpose = general_purpose::URL_SAFE;
const B64_URL_SAFE_NO_PAD: engine::GeneralPurpose = general_purpose::URL_SAFE_NO_PAD;
const B64_STD: engine::GeneralPurpose = general_purpose::STANDARD;

pub fn build_csp_header(nonce: &str) -> (&str, String) {
let value = format!(
"default-src 'self'; script-src 'strict-dynamic' 'nonce-{}' 'wasm-unsafe-eval'; \
style-src 'self' 'unsafe-inline'; frame-ancestors 'none'; object-src 'none'; img-src 'self' data:;",
nonce,
);
("content-security-policy", value)
}

#[deprecated]
// Decrypts a `&Vec<u8>` which was [encrypted](encrypt) before with the same key.
pub fn decrypt_legacy(ciphertext: &[u8], key: &[u8]) -> Result<Vec<u8>, ErrorResponse> {
Expand Down
99 changes: 28 additions & 71 deletions rauthy-handlers/src/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use rauthy_common::constants::{
APPLICATION_JSON, CACHE_NAME_LOGIN_DELAY, HEADER_HTML, IDX_LOGIN_TIME, RAUTHY_VERSION,
};
use rauthy_common::error_response::ErrorResponse;
use rauthy_common::utils::build_csp_header;
use rauthy_models::app_state::AppState;
use rauthy_models::entity::api_keys::{AccessGroup, AccessRights};
use rauthy_models::entity::app_version::LatestAppVersion;
Expand Down Expand Up @@ -55,12 +54,9 @@ pub async fn get_index(
) -> Result<HttpResponse, ErrorResponse> {
let colors = ColorEntity::find_rauthy(&data).await?;
let lang = Language::try_from(&req).unwrap_or_default();
let (body, nonce) = IndexHtml::build(&colors, &lang);
let body = IndexHtml::build(&colors, &lang);

Ok(HttpResponse::Ok()
.insert_header(HEADER_HTML)
.insert_header(build_csp_header(&nonce))
.body(body))
Ok(HttpResponse::Ok().insert_header(HEADER_HTML).body(body))
}

#[get("/{_:.*}")]
Expand Down Expand Up @@ -127,162 +123,123 @@ pub async fn get_account_html(
) -> Result<HttpResponse, ErrorResponse> {
let colors = ColorEntity::find_rauthy(&data).await?;
let lang = Language::try_from(&req).unwrap_or_default();
let (body, nonce) = AccountHtml::build(&colors, &lang);
let body = AccountHtml::build(&colors, &lang);

Ok(HttpResponse::Ok()
.insert_header(HEADER_HTML)
.insert_header(build_csp_header(&nonce))
.body(body))
Ok(HttpResponse::Ok().insert_header(HEADER_HTML).body(body))
}

#[get("/admin")]
pub async fn get_admin_html(data: web::Data<AppState>) -> Result<HttpResponse, ErrorResponse> {
let colors = ColorEntity::find_rauthy(&data).await?;
let (body, nonce) = AdminHtml::build(&colors);
let body = AdminHtml::build(&colors);

Ok(HttpResponse::Ok()
.insert_header(HEADER_HTML)
.insert_header(build_csp_header(&nonce))
.body(body))
Ok(HttpResponse::Ok().insert_header(HEADER_HTML).body(body))
}

#[get("/admin/api_keys")]
pub async fn get_admin_api_keys_html(
data: web::Data<AppState>,
) -> Result<HttpResponse, ErrorResponse> {
let colors = ColorEntity::find_rauthy(&data).await?;
let (body, nonce) = AdminApiKeysHtml::build(&colors);
let body = AdminApiKeysHtml::build(&colors);

Ok(HttpResponse::Ok()
.insert_header(HEADER_HTML)
.insert_header(build_csp_header(&nonce))
.body(body))
Ok(HttpResponse::Ok().insert_header(HEADER_HTML).body(body))
}

#[get("/admin/attributes")]
pub async fn get_admin_attr_html(data: web::Data<AppState>) -> Result<HttpResponse, ErrorResponse> {
let colors = ColorEntity::find_rauthy(&data).await?;
let (body, nonce) = AdminAttributesHtml::build(&colors);
let body = AdminAttributesHtml::build(&colors);

Ok(HttpResponse::Ok()
.insert_header(HEADER_HTML)
.insert_header(build_csp_header(&nonce))
.body(body))
Ok(HttpResponse::Ok().insert_header(HEADER_HTML).body(body))
}

#[get("/admin/blacklist")]
pub async fn get_admin_blacklist_html(
data: web::Data<AppState>,
) -> Result<HttpResponse, ErrorResponse> {
let colors = ColorEntity::find_rauthy(&data).await?;
let (body, nonce) = AdminBlacklistHtml::build(&colors);
let body = AdminBlacklistHtml::build(&colors);

Ok(HttpResponse::Ok()
.insert_header(HEADER_HTML)
.insert_header(build_csp_header(&nonce))
.body(body))
Ok(HttpResponse::Ok().insert_header(HEADER_HTML).body(body))
}

#[get("/admin/clients")]
pub async fn get_admin_clients_html(
data: web::Data<AppState>,
) -> Result<HttpResponse, ErrorResponse> {
let colors = ColorEntity::find_rauthy(&data).await?;
let (body, nonce) = AdminClientsHtml::build(&colors);
let body = AdminClientsHtml::build(&colors);

Ok(HttpResponse::Ok()
.insert_header(HEADER_HTML)
.insert_header(build_csp_header(&nonce))
.body(body))
Ok(HttpResponse::Ok().insert_header(HEADER_HTML).body(body))
}

#[get("/admin/config")]
pub async fn get_admin_config_html(
data: web::Data<AppState>,
) -> Result<HttpResponse, ErrorResponse> {
let colors = ColorEntity::find_rauthy(&data).await?;
let (body, nonce) = AdminConfigHtml::build(&colors);
let body = AdminConfigHtml::build(&colors);

Ok(HttpResponse::Ok()
.insert_header(HEADER_HTML)
.insert_header(build_csp_header(&nonce))
.body(body))
Ok(HttpResponse::Ok().insert_header(HEADER_HTML).body(body))
}

#[get("/admin/docs")]
pub async fn get_admin_docs_html(data: web::Data<AppState>) -> Result<HttpResponse, ErrorResponse> {
let colors = ColorEntity::find_rauthy(&data).await?;
let (body, nonce) = AdminDocsHtml::build(&colors);
let body = AdminDocsHtml::build(&colors);

Ok(HttpResponse::Ok()
.insert_header(HEADER_HTML)
.insert_header(build_csp_header(&nonce))
.body(body))
Ok(HttpResponse::Ok().insert_header(HEADER_HTML).body(body))
}

#[get("/admin/groups")]
pub async fn get_admin_groups_html(
data: web::Data<AppState>,
) -> Result<HttpResponse, ErrorResponse> {
let colors = ColorEntity::find_rauthy(&data).await?;
let (body, nonce) = AdminGroupsHtml::build(&colors);
let body = AdminGroupsHtml::build(&colors);

Ok(HttpResponse::Ok()
.insert_header(HEADER_HTML)
.insert_header(build_csp_header(&nonce))
.body(body))
Ok(HttpResponse::Ok().insert_header(HEADER_HTML).body(body))
}

#[get("/admin/roles")]
pub async fn get_admin_roles_html(
data: web::Data<AppState>,
) -> Result<HttpResponse, ErrorResponse> {
let colors = ColorEntity::find_rauthy(&data).await?;
let (body, nonce) = AdminRolesHtml::build(&colors);
let body = AdminRolesHtml::build(&colors);

Ok(HttpResponse::Ok()
.insert_header(HEADER_HTML)
.insert_header(build_csp_header(&nonce))
.body(body))
Ok(HttpResponse::Ok().insert_header(HEADER_HTML).body(body))
}

#[get("/admin/scopes")]
pub async fn get_admin_scopes_html(
data: web::Data<AppState>,
) -> Result<HttpResponse, ErrorResponse> {
let colors = ColorEntity::find_rauthy(&data).await?;
let (body, nonce) = AdminScopesHtml::build(&colors);
let body = AdminScopesHtml::build(&colors);

Ok(HttpResponse::Ok()
.insert_header(HEADER_HTML)
.insert_header(build_csp_header(&nonce))
.body(body))
Ok(HttpResponse::Ok().insert_header(HEADER_HTML).body(body))
}

#[get("/admin/sessions")]
pub async fn get_admin_sessions_html(
data: web::Data<AppState>,
) -> Result<HttpResponse, ErrorResponse> {
let colors = ColorEntity::find_rauthy(&data).await?;
let (body, nonce) = AdminSessionsHtml::build(&colors);
let body = AdminSessionsHtml::build(&colors);

Ok(HttpResponse::Ok()
.insert_header(HEADER_HTML)
.insert_header(build_csp_header(&nonce))
.body(body))
Ok(HttpResponse::Ok().insert_header(HEADER_HTML).body(body))
}

#[get("/admin/users")]
pub async fn get_admin_users_html(
data: web::Data<AppState>,
) -> Result<HttpResponse, ErrorResponse> {
let colors = ColorEntity::find_rauthy(&data).await?;
let (body, nonce) = AdminUsersHtml::build(&colors);
let body = AdminUsersHtml::build(&colors);

Ok(HttpResponse::Ok()
.insert_header(HEADER_HTML)
.insert_header(build_csp_header(&nonce))
.body(body))
Ok(HttpResponse::Ok().insert_header(HEADER_HTML).body(body))
}

/// Check if the current session is valid
Expand Down
Loading

0 comments on commit 8fd2c99

Please sign in to comment.