Compare commits

..

No commits in common. "57a0dfdbdbb5f26c4f626c0083ec637532236726" and "c229208bd513597eec1ef94771c9413b5c8ec6fa" have entirely different histories.

11 changed files with 20 additions and 184 deletions

5
.env
View file

@ -1,10 +1,6 @@
#### Part-DB Configuration #### Part-DB Configuration
# See https://docs.part-db.de/configuration.html for documentation of available options # See https://docs.part-db.de/configuration.html for documentation of available options
# Change this to a random value to secure your installation! You can generate a random string with "openssl rand -hex 16"
# Share that value with nobody and keep it secret
APP_SECRET=a03498528f5a5fc089273ec9ae5b2849
################################################################################### ###################################################################################
# Database settings # Database settings
################################################################################### ###################################################################################
@ -162,6 +158,7 @@ CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$'
###> symfony/framework-bundle ### ###> symfony/framework-bundle ###
APP_ENV=prod APP_ENV=prod
APP_SECRET=a03498528f5a5fc089273ec9ae5b2849
APP_SHARE_DIR=var/share APP_SHARE_DIR=var/share
###< symfony/framework-bundle ### ###< symfony/framework-bundle ###

View file

@ -1 +1 @@
2.12.1 2.12.0

View file

@ -114,21 +114,10 @@ bundled with Part-DB. Set `DATABASE_MYSQL_SSL_VERIFY_CERT` if you want to accept
* `datastructure_create`: Creation of a new data structure (e.g. category, manufacturer, ...) * `datastructure_create`: Creation of a new data structure (e.g. category, manufacturer, ...)
* `CHECK_FOR_UPDATES` (default `1`): Set this to 0 if you do not want Part-DB to connect to GitHub to check for new * `CHECK_FOR_UPDATES` (default `1`): Set this to 0 if you do not want Part-DB to connect to GitHub to check for new
versions, or if your server cannot connect to the internet. versions, or if your server cannot connect to the internet.
* `APP_SECRET` (env only): A secret key used by Symfony for cryptographic operations — signing cookies, generating * `APP_SECRET` (env only): This variable is a configuration parameter used for various security-related purposes,
CSRF tokens, and other security-sensitive tasks. **You must change this from the default value before exposing particularly for securing and protecting various aspects of your application. It's a secret key that is used for
Part-DB to any network.** The default value shipped with Part-DB is publicly known; leaving it in place would allow cryptographic operations and security measures (session management, CSRF protection, etc..). Therefore this
an attacker to forge signed cookies and bypass CSRF protection. value should be handled as confidential data and not shared publicly.
Generate a secure value and add it to `.env.local`:
```bash
echo "APP_SECRET=$(openssl rand -hex 32)" >> .env.local
```
For Docker, pass it in the `environment` section of your `docker-compose.yaml`:
```yaml
environment:
- APP_SECRET=<output of: openssl rand -hex 32>
```
Part-DB displays a warning on the homepage (visible to administrators only) as long as the default value is in use.
* `SHOW_PART_IMAGE_OVERLAY`: Set to 0 to disable the part image overlay, which appears if you hover over an image in the * `SHOW_PART_IMAGE_OVERLAY`: Set to 0 to disable the part image overlay, which appears if you hover over an image in the
part image gallery part image gallery
* `IPN_SUGGEST_REGEX`: A global regular expression, that part IPNs have to fulfill. Enforce your own format for your users. * `IPN_SUGGEST_REGEX`: A global regular expression, that part IPNs have to fulfill. Enforce your own format for your users.

View file

@ -47,9 +47,6 @@ services:
- DATABASE_URL=sqlite:///%kernel.project_dir%/var/db/app.db - DATABASE_URL=sqlite:///%kernel.project_dir%/var/db/app.db
# In docker env logs will be redirected to stderr # In docker env logs will be redirected to stderr
- APP_ENV=docker - APP_ENV=docker
# Secret key used to sign cookies and CSRF tokens. MUST be changed to a unique random value before going live!
# Generate one with: openssl rand -hex 32
- APP_SECRET=CHANGE_ME
# Uncomment this, if you want to use the automatic database migration feature. With this you have you do not have to # Uncomment this, if you want to use the automatic database migration feature. With this you have you do not have to
# run the doctrine:migrations:migrate commands on installation or upgrade. A database backup is written to the uploads/ # run the doctrine:migrations:migrate commands on installation or upgrade. A database backup is written to the uploads/
@ -92,11 +89,7 @@ services:
4. Customize the settings by changing the environment variables (or adding new ones). See [Configuration]({% link 4. Customize the settings by changing the environment variables (or adding new ones). See [Configuration]({% link
configuration.md %}) for more information. configuration.md %}) for more information.
5. Make sure to change the `APP_SECRET` variable to a unique random value (32 characters). You can generate one with the following command: 5. Inside the folder, run
```bash
openssl rand -hex 32
```
6. Inside the folder, run
```bash ```bash
docker-compose up -d docker-compose up -d
@ -107,7 +100,7 @@ openssl rand -hex 32
> Otherwise Part-DB console might use the wrong configuration to execute commands. > Otherwise Part-DB console might use the wrong configuration to execute commands.
7. Create the initial database with 6. Create the initial database with
```bash ```bash
docker exec --user=www-data partdb php bin/console doctrine:migrations:migrate docker exec --user=www-data partdb php bin/console doctrine:migrations:migrate
@ -115,7 +108,7 @@ docker exec --user=www-data partdb php bin/console doctrine:migrations:migrate
and watch for the password output and watch for the password output
8. Part-DB is available under `http://localhost:8080` and you can log in with the username `admin` and the password shown 6. Part-DB is available under `http://localhost:8080` and you can log in with the username `admin` and the password shown
before before
The docker image uses a SQLite database and all data (database, uploads, and other media) is put into folders relative to The docker image uses a SQLite database and all data (database, uploads, and other media) is put into folders relative to
@ -128,7 +121,6 @@ If you want to use MySQL as a database, you can use the following docker-compose
{: .warning } {: .warning }
> You have to replace the values for MYSQL_ROOT_PASSWORD and MYSQL_PASSWORD with your own passwords!! > You have to replace the values for MYSQL_ROOT_PASSWORD and MYSQL_PASSWORD with your own passwords!!
> You have to change MYSQL_PASSWORD in the database section and for the DATABASE_URL in the partdb section. > You have to change MYSQL_PASSWORD in the database section and for the DATABASE_URL in the partdb section.
> Generate a random string for APP_SECRET.
```yaml ```yaml
version: '3.3' version: '3.3'
@ -150,11 +142,6 @@ services:
environment: environment:
# Replace SECRET_USER_PASSWORD with the value of MYSQL_PASSWORD from below # Replace SECRET_USER_PASSWORD with the value of MYSQL_PASSWORD from below
- DATABASE_URL=mysql://partdb:SECRET_USER_PASSWORD@database:3306/partdb - DATABASE_URL=mysql://partdb:SECRET_USER_PASSWORD@database:3306/partdb
# Secret key used to sign cookies. MUST be changed to a unique random value before going live!
# Generate one with: openssl rand -hex 32
- APP_SECRET=CHANGE_ME
# In docker env logs will be redirected to stderr # In docker env logs will be redirected to stderr
- APP_ENV=docker - APP_ENV=docker

View file

@ -136,15 +136,6 @@ cp .env .env.local
In your `.env.local` you can configure Part-DB according to your wishes and overwrite web interface settings. In your `.env.local` you can configure Part-DB according to your wishes and overwrite web interface settings.
A full list of configuration options can be found [here](../configuration.md). A full list of configuration options can be found [here](../configuration.md).
{: .important }
> **Change `APP_SECRET` before going live.** The default value shipped with Part-DB is publicly known and must not be
> used in production — it would allow an attacker to forge signed cookies and bypass CSRF protection.
> Generate a new value and add it to your `.env.local`:
> ```bash
> echo "APP_SECRET=$(openssl rand -hex 32)" >> .env.local
> ```
> or edit the file with a text editor and add a new value for `APP_SECRET` (you can generate a random value with `openssl rand -hex 32`).
Please check that the configured base currency matches your mainly used currency, as Please check that the configured base currency matches your mainly used currency, as
this can not be changed after creating price information. this can not be changed after creating price information.

View file

@ -22,7 +22,6 @@ declare(strict_types=1);
*/ */
namespace App\Command; namespace App\Command;
use App\Services\System\AppSecretChecker;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
@ -34,9 +33,7 @@ use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface;
#[AsCommand('partdb:check-requirements', 'Checks if the requirements Part-DB needs or recommends are fulfilled.')] #[AsCommand('partdb:check-requirements', 'Checks if the requirements Part-DB needs or recommends are fulfilled.')]
class CheckRequirementsCommand extends Command class CheckRequirementsCommand extends Command
{ {
public function __construct(protected ContainerBagInterface $params, public function __construct(protected ContainerBagInterface $params)
private readonly AppSecretChecker $appSecretChecker
)
{ {
parent::__construct(); parent::__construct();
} }
@ -124,16 +121,6 @@ class CheckRequirementsCommand extends Command
$io->success('Debug mode disabled.'); $io->success('Debug mode disabled.');
} }
//Check if APP_SECRET has been changed from the default
if ($io->isVerbose()) {
$io->comment('Checking APP_SECRET...');
}
if ($this->appSecretChecker->isInsecureSecret()) {
$io->warning('APP_SECRET is set to the default value shipped with Part-DB. This is a security risk! Generate a new secret (e.g. using "openssl rand -hex 32") and set it as APP_SECRET in your .env.local file.');
} elseif (!$only_issues) {
$io->success('APP_SECRET has been changed from the default value.');
}
} }
protected function checkPHPExtensions(SymfonyStyle $io, bool $only_issues = false): void protected function checkPHPExtensions(SymfonyStyle $io, bool $only_issues = false): void

View file

@ -24,7 +24,6 @@ namespace App\Controller;
use App\DataTables\LogDataTable; use App\DataTables\LogDataTable;
use App\Entity\Parts\Part; use App\Entity\Parts\Part;
use App\Services\System\AppSecretChecker;
use App\Services\System\BannerHelper; use App\Services\System\BannerHelper;
use App\Services\System\GitVersionInfoProvider; use App\Services\System\GitVersionInfoProvider;
use App\Services\System\UpdateAvailableFacade; use App\Services\System\UpdateAvailableFacade;
@ -37,11 +36,8 @@ use Symfony\Component\Routing\Attribute\Route;
class HomepageController extends AbstractController class HomepageController extends AbstractController
{ {
public function __construct( public function __construct(private readonly DataTableFactory $dataTable, private readonly BannerHelper $bannerHelper)
private readonly DataTableFactory $dataTable, {
private readonly BannerHelper $bannerHelper,
private readonly AppSecretChecker $appSecretChecker,
) {
} }
@ -88,8 +84,6 @@ class HomepageController extends AbstractController
'new_version_available' => $updateAvailableManager->isUpdateAvailable(), 'new_version_available' => $updateAvailableManager->isUpdateAvailable(),
'new_version' => $updateAvailableManager->getLatestVersionString(), 'new_version' => $updateAvailableManager->getLatestVersionString(),
'new_version_url' => $updateAvailableManager->getLatestVersionUrl(), 'new_version_url' => $updateAvailableManager->getLatestVersionUrl(),
'insecure_app_secret' => $this->appSecretChecker->isInsecureSecret(),
'suggested_app_secret' => $this->appSecretChecker->isInsecureSecret() ? $this->appSecretChecker->generateSecret() : null,
]); ]);
} }
} }

View file

@ -1,63 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Services\System;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
/**
* Checks whether APP_SECRET has been changed from the default value shipped with Part-DB.
*/
final readonly class AppSecretChecker
{
/** Known default/example secrets that must not be used in production. */
public const INSECURE_SECRETS = [
'a03498528f5a5fc089273ec9ae5b2849', // default in .env
'318b5d659e07a0b3f96d9b3a83b254ca', // default in .env.dev
'CHANGE_ME' //example secret used in documentation and error messages
];
public function __construct(
#[Autowire('%kernel.secret%')]
private string $appSecret,
) {
}
/**
* @return bool True if the app secret is one of the known insecure default secrets, false otherwise.
*/
public function isInsecureSecret(): bool
{
return in_array($this->appSecret, self::INSECURE_SECRETS, true);
}
/**
* Generates a new random app secret that can be used to replace the default insecure one.
* @return string
* @throws \Random\RandomException
*/
public function generateSecret(): string
{
//Symfony docs recommend 32 characters for the app secret, which are 16 random bytes when hex-encoded.
return bin2hex(random_bytes(16));
}
}

View file

@ -85,16 +85,6 @@
{% block content %} {% block content %}
{% if insecure_app_secret and is_granted('@system.server_infos') %}
<div class="alert alert-warning" role="alert">
<h5><i class="fa-solid fa-triangle-exclamation fa-fw"></i> {% trans %}system.app_secret.insecure.title{% endtrans %}</h5>
<p class="mb-1">{% trans %}system.app_secret.insecure.message{% endtrans %}</p>
<p class="mb-0">{% trans %}system.app_secret.insecure.suggestion{% endtrans %}
<br><code>APP_SECRET={{ suggested_app_secret }}</code></p>
<small>{% trans %}update_manager.new_version_available.only_administrators_can_see{% endtrans %}</small>
</div>
{% endif %}
{% if is_granted('@system.show_updates') %} {% if is_granted('@system.show_updates') %}
{{ nv.new_version_alert(new_version_available, new_version, new_version_url) }} {{ nv.new_version_alert(new_version_available, new_version, new_version_url) }}
{% endif %} {% endif %}

View file

@ -64,7 +64,7 @@
<unit id="KSFhj_3" name="currency.iso_code.caption"> <unit id="KSFhj_3" name="currency.iso_code.caption">
<segment state="translated"> <segment state="translated">
<source>currency.iso_code.caption</source> <source>currency.iso_code.caption</source>
<target>ISO-Code</target> <target>ISO Code</target>
</segment> </segment>
</unit> </unit>
<unit id="UqMuNZQ" name="currency.symbol.caption"> <unit id="UqMuNZQ" name="currency.symbol.caption">
@ -13647,23 +13647,5 @@ Buerklin-API-Authentication-Server:
<target>Aktiviert das Browser-Plugin für diese Instanz.</target> <target>Aktiviert das Browser-Plugin für diese Instanz.</target>
</segment> </segment>
</unit> </unit>
<unit id="JqcvhX_" name="system.app_secret.insecure.title">
<segment state="translated">
<source>system.app_secret.insecure.title</source>
<target>Unsicheres APP_SECRET</target>
</segment>
</unit>
<unit id="XSSYWTJ" name="system.app_secret.insecure.message">
<segment state="translated">
<source>system.app_secret.insecure.message</source>
<target>Die Umgebungsvariable &lt;code&gt;APP_SECRET&lt;/code&gt; ist auf den Standardwert gesetzt, der mit Part-DB ausgeliefert wird. Dies stellt ein Sicherheitsrisiko dar! Setzen Sie sie in Ihrer Datei &lt;code&gt;.env.local&lt;/code&gt; oder &lt;code&gt;docker-compose.yaml&lt;/code&gt; auf einen neuen Zufallswert.</target>
</segment>
</unit>
<unit id="e7.f3m0" name="system.app_secret.insecure.suggestion">
<segment state="translated">
<source>system.app_secret.insecure.suggestion</source>
<target>Sie können diesen zufällig generierten Wert verwenden (geben Sie ihn niemandem weiter):</target>
</segment>
</unit>
</file> </file>
</xliff> </xliff>

View file

@ -13649,23 +13649,5 @@ Buerklin-API Authentication server:
<target>When enabled users with the info provider permission can submit pages to Part-DB and retrieve them later.</target> <target>When enabled users with the info provider permission can submit pages to Part-DB and retrieve them later.</target>
</segment> </segment>
</unit> </unit>
<unit id="JqcvhX_" name="system.app_secret.insecure.title">
<segment state="translated">
<source>system.app_secret.insecure.title</source>
<target>Insecure APP_SECRET</target>
</segment>
</unit>
<unit id="XSSYWTJ" name="system.app_secret.insecure.message">
<segment state="translated">
<source>system.app_secret.insecure.message</source>
<target>The &lt;code&gt;APP_SECRET&lt;/code&gt; environment variable is set to the default value shipped with Part-DB. This is a security risk! Set it to a new random value in your &lt;code&gt;.env.local&lt;/code&gt; or &lt;code&gt;docker-compose.yaml&lt;/code&gt; file.</target>
</segment>
</unit>
<unit id="e7.f3m0" name="system.app_secret.insecure.suggestion">
<segment state="translated">
<source>system.app_secret.insecure.suggestion</source>
<target>You can use this randomly generated value (share it with nobody):</target>
</segment>
</unit>
</file> </file>
</xliff> </xliff>