mirror of
https://github.com/BookStackApp/BookStack.git
synced 2026-05-04 18:08:46 +03:00
Compare commits
1 Commits
MilnerMart
...
Zhey-on/fe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e42fda893c |
@@ -395,6 +395,18 @@ ALLOWED_IFRAME_HOSTS=null
|
||||
# Current host and source for the "DRAWIO" setting will be auto-appended to the sources configured.
|
||||
ALLOWED_IFRAME_SOURCES="https://*.draw.io https://*.youtube.com https://*.youtube-nocookie.com https://*.vimeo.com"
|
||||
|
||||
# A list of sources/hostnames that can be loaded as CSS styles within BookStack.
|
||||
# Space separated if multiple. BookStack host domain is auto-inferred.
|
||||
# Defaults to a permissive set if not provided.
|
||||
# Example: ALLOWED_CSS_SOURCES="https://fonts.googleapis.com"
|
||||
ALLOWED_CSS_SOURCES=null
|
||||
|
||||
# A list of sources/hostnames that can be loaded as image content within BookStack.
|
||||
# Space separated if multiple. BookStack host domain is auto-inferred.
|
||||
# Defaults to a permissive set if not provided.
|
||||
# Example: ALLOWED_IMAGE_SOURCES="https://images.example.com data:"
|
||||
ALLOWED_IMAGE_SOURCES=null
|
||||
|
||||
# A list of the sources/hostnames that can be reached by application SSR calls.
|
||||
# This is used wherever users can provide URLs/hosts in-platform, like for webhooks.
|
||||
# Host-specific functionality (usually controlled via other options) like auth
|
||||
|
||||
@@ -72,6 +72,16 @@ return [
|
||||
// Current host and source for the "DRAWIO" setting will be auto-appended to the sources configured.
|
||||
'iframe_sources' => env('ALLOWED_IFRAME_SOURCES', 'https://*.draw.io https://*.youtube.com https://*.youtube-nocookie.com https://*.vimeo.com'),
|
||||
|
||||
// A list of sources/hostnames that can be loaded as CSS styles within BookStack.
|
||||
// Space separated if multiple. BookStack host domain is auto-inferred.
|
||||
// If not set, a permissive default set is used to reduce potential breakage.
|
||||
'css_sources' => env('ALLOWED_CSS_SOURCES', null),
|
||||
|
||||
// A list of sources/hostnames that can be loaded as image content within BookStack.
|
||||
// Space separated if multiple. BookStack host domain is auto-inferred.
|
||||
// If not set, a permissive default set is used to reduce potential breakage.
|
||||
'image_sources' => env('ALLOWED_IMAGE_SOURCES', null),
|
||||
|
||||
// A list of the sources/hostnames that can be reached by application SSR calls.
|
||||
// This is used wherever users can provide URLs/hosts in-platform, like for webhooks.
|
||||
// Host-specific functionality (usually controlled via other options) like auth
|
||||
|
||||
@@ -30,6 +30,8 @@ class CspService
|
||||
$this->getFrameAncestors(),
|
||||
$this->getFrameSrc(),
|
||||
$this->getScriptSrc(),
|
||||
$this->getStyleSrc(),
|
||||
$this->getImgSrc(),
|
||||
$this->getObjectSrc(),
|
||||
$this->getBaseUri(),
|
||||
];
|
||||
@@ -45,6 +47,8 @@ class CspService
|
||||
$headers = [
|
||||
$this->getFrameSrc(),
|
||||
$this->getScriptSrc(),
|
||||
$this->getStyleSrc(),
|
||||
$this->getImgSrc(),
|
||||
$this->getObjectSrc(),
|
||||
$this->getBaseUri(),
|
||||
];
|
||||
@@ -115,6 +119,22 @@ class CspService
|
||||
return "object-src 'self'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates CSP 'style-src' rule to restrict where styles can be loaded from.
|
||||
*/
|
||||
protected function getStyleSrc(): string
|
||||
{
|
||||
return 'style-src ' . implode(' ', $this->getAllowedStyleSources());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates CSP 'img-src' rule to restrict where images can be loaded from.
|
||||
*/
|
||||
protected function getImgSrc(): string
|
||||
{
|
||||
return 'img-src ' . implode(' ', $this->getAllowedImageSources());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates CSP 'base-uri' rule to restrict what base tags can be set on
|
||||
* the page to prevent manipulation of relative links.
|
||||
@@ -144,6 +164,51 @@ class CspService
|
||||
return array_filter($sources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get allowed style sources for the style-src directive.
|
||||
*/
|
||||
protected function getAllowedStyleSources(): array
|
||||
{
|
||||
$configured = config('app.css_sources');
|
||||
|
||||
if (is_string($configured)) {
|
||||
$sources = array_filter(explode(' ', $configured));
|
||||
array_unshift($sources, "'self'");
|
||||
|
||||
return array_values(array_unique($sources));
|
||||
}
|
||||
|
||||
return [
|
||||
"'self'",
|
||||
"'unsafe-inline'",
|
||||
'http:',
|
||||
'https:',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get allowed image sources for the img-src directive.
|
||||
*/
|
||||
protected function getAllowedImageSources(): array
|
||||
{
|
||||
$configured = config('app.image_sources');
|
||||
|
||||
if (is_string($configured)) {
|
||||
$sources = array_filter(explode(' ', $configured));
|
||||
array_unshift($sources, "'self'");
|
||||
|
||||
return array_values(array_unique($sources));
|
||||
}
|
||||
|
||||
return [
|
||||
"'self'",
|
||||
'data:',
|
||||
'blob:',
|
||||
'http:',
|
||||
'https:',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the host name of the configured drawio URL for use in CSP.
|
||||
* Returns empty string if not in use.
|
||||
|
||||
@@ -31,6 +31,48 @@ BookStack has a large suite of PHP tests to cover application functionality. We
|
||||
|
||||
For details about setting-up, running and writing tests please see the [php-testing.md document](php-testing.md).
|
||||
|
||||
## Content Security Policy Controls
|
||||
|
||||
BookStack enforces a Content Security Policy (CSP) response header to reduce risk from injected content and untrusted embeds.
|
||||
|
||||
For backward compatibility, image and CSS controls are intentionally permissive by default, but can be tightened via environment options.
|
||||
|
||||
### Related Environment Options
|
||||
|
||||
These values are defined in `.env.example.complete`:
|
||||
|
||||
- `ALLOWED_CSS_SOURCES`
|
||||
- Controls allowed `style-src` sources.
|
||||
- Defaults to a permissive fallback if unset.
|
||||
- `ALLOWED_IMAGE_SOURCES`
|
||||
- Controls allowed `img-src` sources.
|
||||
- Defaults to a permissive fallback if unset.
|
||||
|
||||
Values should be space-separated source expressions.
|
||||
|
||||
### Example Configurations
|
||||
|
||||
Allow Google Fonts CSS and local styles only:
|
||||
|
||||
```bash
|
||||
ALLOWED_CSS_SOURCES="https://fonts.googleapis.com"
|
||||
```
|
||||
|
||||
Allow local images, embedded data images, and a dedicated image CDN:
|
||||
|
||||
```bash
|
||||
ALLOWED_IMAGE_SOURCES="data: https://images.example.com"
|
||||
```
|
||||
|
||||
### Tightening Guidance
|
||||
|
||||
When hardening a deployment:
|
||||
|
||||
1. Start with defaults to avoid unexpected breakage.
|
||||
2. Set explicit `ALLOWED_CSS_SOURCES` and `ALLOWED_IMAGE_SOURCES` values for the domains you actually use.
|
||||
3. Test key workflows (editor, page display, theme assets, external embeds) and browser console CSP warnings.
|
||||
4. Remove unnecessary protocols and hosts over time.
|
||||
|
||||
## Code Standards
|
||||
|
||||
We use tools to manage code standards and formatting within the project. If submitting a PR, formatting as per our project standards would help for clarity but don't worry too much about using/understanding these tools as we can always address issues at a later stage when they're picked up by our automated tools.
|
||||
|
||||
@@ -102,6 +102,7 @@ Big thanks to these companies for supporting the project.
|
||||
## 🛠️ Development & Testing
|
||||
|
||||
Please see our [development docs](dev/docs/development.md) for full details regarding work on the BookStack source code.
|
||||
For details on Content Security Policy controls (including image and CSS source options), see the **Content Security Policy Controls** section in the [development docs](dev/docs/development.md).
|
||||
|
||||
If you're just looking to customize or extend your own BookStack instance, take a look at our [Hacking BookStack documentation page](https://www.bookstackapp.com/docs/admin/hacking-bookstack/) for details on various options to achieve this without altering the BookStack source code.
|
||||
|
||||
|
||||
@@ -151,6 +151,42 @@ class SecurityHeaderTest extends TestCase
|
||||
$this->assertEquals('frame-src \'self\' https://example.com https://diagrams.example.com:8080', $scriptHeader);
|
||||
}
|
||||
|
||||
public function test_style_src_csp_header_set_to_permissive_defaults_when_not_configured()
|
||||
{
|
||||
$resp = $this->get('/');
|
||||
$header = $this->getCspHeader($resp, 'style-src');
|
||||
|
||||
$this->assertEquals("style-src 'self' 'unsafe-inline' http: https:", $header);
|
||||
}
|
||||
|
||||
public function test_style_src_csp_header_can_be_overridden_by_config()
|
||||
{
|
||||
config()->set('app.css_sources', 'https://fonts.example.com');
|
||||
|
||||
$resp = $this->get('/');
|
||||
$header = $this->getCspHeader($resp, 'style-src');
|
||||
|
||||
$this->assertEquals("style-src 'self' https://fonts.example.com", $header);
|
||||
}
|
||||
|
||||
public function test_img_src_csp_header_set_to_permissive_defaults_when_not_configured()
|
||||
{
|
||||
$resp = $this->get('/');
|
||||
$header = $this->getCspHeader($resp, 'img-src');
|
||||
|
||||
$this->assertEquals("img-src 'self' data: blob: http: https:", $header);
|
||||
}
|
||||
|
||||
public function test_img_src_csp_header_can_be_overridden_by_config()
|
||||
{
|
||||
config()->set('app.image_sources', 'https://images.example.com data:');
|
||||
|
||||
$resp = $this->get('/');
|
||||
$header = $this->getCspHeader($resp, 'img-src');
|
||||
|
||||
$this->assertEquals("img-src 'self' https://images.example.com data:", $header);
|
||||
}
|
||||
|
||||
public function test_cache_control_headers_are_set_on_responses()
|
||||
{
|
||||
// Public access
|
||||
|
||||
Reference in New Issue
Block a user