dbxapp Knowledge Security & Performance

Security & Performance

On this page
  1. Threat model and verifiable controls
  2. Architectural decision
  3. Why rights are not enough for state-changing GET requests alone
  4. Configuration and Secrets
  5. Read only demo mode
  6. Workflow contract
  7. Shop contract
  8. Scheme, DB and output limits
  9. Guest sessions, cache and header
  10. Telemetry and content
  11. Verification
  12. Introduction and fallback plan
  13. Related documentation

Reference code last changed: August 2, 2026
Revalidated against dbxapp 4.5.3: August 22, 2026

This chapter documents the hardening of the public runtime, of dbxWorkflow, dbxShop and dbxContent admin. It complements the mandatory architectural rules and the domain guides. Existing GET navigation is maintained. Only GETs that change state require evidence of the specific browser action.

Security review for dbxapp 4.5.3Trust boundaries, threat model, and regression evidence were revalidated on August 22, 2026.

Threat model and verifiable controls

ThreatBoundaryImplementationEvidence
CSRFprove the intended browser actiondbxForm CSRF and HMAC action tokensdbxApi security test and token audit
IDORmodule, DD, and owner permissionsdbxDB through a complete DD; RID in token scopeauthorization and module contract tests
SQL injectionone database access pathdbxDB and DD; direct drivers prohibiteddatabase access boundary test
XSSseparate output, templates, and uploadscontext-aware output, template hygiene, upload allowliststemplate hygiene and browser matrix
Session fixationidentity changes end the old contextsession regeneration and a new action secretsession and API security tests
Secret disclosurelocal configuration and masked displayconfig.local.php and masked get_cfg outputconfiguration and release checks
ZIP slipstaging plus path/type allowlistssignature, hash, and normalized package pathsupdate and package contract tests

Architectural decision

The changes remain in the existing dbxapp architecture:

  • Data access is also provided in maintenance and migration tools exclusively via dbxDB and DD.
  • Use input and lists dbxForm or dbxReport.
  • Logic remains in workflow and shop service/Repository.
  • Full output areas are in dbxTPL; Templates remain separate from data access and domain mutations.
  • There is no second token class and no parallel security stack.

Kernel changes were only made for system-wide contracts:

Kernel area Necessity
dbxApi HMAC action tokens, automatic action URLs, secure random values
dbxWebApp automatic action policies, check before module start, no API autologin
dbxReport Automatic tokenization of standard and grid mutations
dbxForm own rotating POST-CSRF protection; Action URL only with automatically detected RID mutation
dbx session Avoid centralized guest session writing load and discard action secret to Login/Logout
dbxRuntime Common safety headers

For mutating link actions, the existing kernel infrastructure action token() / check action token() used. dbx token is only their transport parameters; There is still no second token class and no parallel security stack.

Since the central action policy, concrete tokens are derived stateless from exactly one session secret via HMAC. Even RID-specific scopes do not generate a growing token list in the PHP session. Scope tokens rendered before the changeover will continue to be accepted for the duration of their existing session.

During login and logout dbx session Both the HMAC secret and any legacy tokens. A link rendered before the user change is then invalid. delete and Save together with ride detected directly from the dbx action parameters; No configuration access is required for this. Unusual older action names can still use an explicit policy for compatibility.

A token check itself does not generate a session state. If no action secret is yet available, a formally correct-looking foreign token is also discarded directly. Only the rendering of a real action link generates a session secret if necessary. Neither action nor form tokens or parts thereof are written in debug messages.

Why rights are not enough for state-changing GET requests alone

module and DD permissions determine, who perform an operation. They do not prove that this user wanted to trigger exactly this mutation. Without action tokens, a foreign website can prompt a logged-in browser to call a start, pause, resume, cancel or finish GET (cross-site request forgery).

This applies especially if the user is authorized: The browser sends its session, then module and DD rights allow the mutation correctly. OWASP expressly states that: SameSite = Lax in the case of a state-changing top-level GET navigation is not sufficient: OWASP CSRF Prevention Cheat Sheet.

The binding contract is therefore:

  • pure navigation, display, filter and detail GETs remain tokenless;
  • Standard actions of dbxReport (row delete’, multi delete, delete tab, Activation/Deaktivieren) are automatically tokenized;
  • Writing grid endpoints with the dbxReport Convention * grid <save|insert|delete|sort|sync>, data <... > or fields <... > are recognized from the actual route and automatically tokenized;
  • delete and Save in the dbx action parameters together with ride automatically detected and bound to this RID;
  • dbxWebApp checks the detected policy before starting the module code;
  • old mutating direct links remain accessible as a route, but do not mutate without valid tokens;
  • normal POST forms use only the automatically managed dbxForm- Tokens and receive no additional dbx token;
  • a report link in the form can contain both protective layers;
  • the token never replaces module, owner or DD rights.

The URL is generated without policy configuration, scope construction or manual token verification:

$url = dbx()->action_url(
'?dbx_modul=myInvoices&dbx_run1=delete&rid=' . $rid
);

action url() Change the URL only if dbxWebApp Find a suitable policy. The token binds module, run context, action name and automatically the RID. Transport values such as: dbx ajax, dbx window and dbx token They are not part of the scope. The same action link therefore functions as a normal GET and as an Ajax POST of a report. dbx token It is also removed from self, filter and pagination URLs.

With the grid, protection does not depend on an optional marker. dbxWebApp recognizes the actual save/insert/delete/sort/sync route. Therefore, a directly constructed request without dbx token Rejected. A configured writing grid URL that does not follow a supported convention is dbxReport Not unsigned. Reading grid URLs remain unchanged.

Explicit special technical scopes exist only for processes that are not a dbxReport or Delete/Save-RID standard action: CMS/SEO-Medienaktionen, workflow launch and instance commands, shop collection and status actions, dbxKi Plan/Execute, re-registration shipping, and the additional user management actions Verify, Lock, Unlock, and password reset. The binding Allowlist and its justifications are dbxActionTokenUsageAudit_test.php checked. New manual token logic in another module makes this test fail.

Missing, false or generated tokens for another RID provide HTTP 403, generate a security system message and do not reach the module code.

The evidence of necessity is independent of the module rights: Browsers automatically send session cookies on same-site navigation; Rights thus only identify the logged-in user. Only the session-bound, not known by a foreign side action token proves the concrete action. For this reason, dbx token necessary for writing GETs, but not for normal GET navigation.

Verification evidence of 24. July 2026: A direct workflow start without token provided the overview with a tokenized start link. The number of workflow instance-Data records remained at 30.

Configuration and Secrets

dbx/modules/dbx/cfg/config.php no longer contains production credentials. Local values are in:

dbx/modules/dbx/cfg/config.local.php

The file is over **/cfg/config.local.php ignored. An empty, versionable template is below:

dbx/modules/dbx/cfg/config.local.example.php

dbxApi::get_cfg() first loads the base and then overlays the local file recursively. set cfg() removes local override paths when writing the base. Local installation values are exclusively set cfg($module, $values, 'local') written. As a result, an admin storage operation cannot accidentally copy local secrets back to the versioned configuration.

There are deliberately no parallel methods get config(), set config() or set local config(). Each configuration reading runs over get cfg()any change in configuration over set cfg(). Management views request a masked copy in demo mode with the fourth parameter:

$runtime = dbx()->get_cfg('dbx'); // echte Werte intern
$display = dbx()->get_cfg('dbx', '', null, true); // Secrets als ******
dbx()->set_cfg('dbx', $config);
dbx()->set_cfg('dbx', $localConfig, 'local');

Important: Removing from the current source does not delete values from existing Git history, backups or logs. All access data previously stored in the repository must be rotated after the changeover.

The former special treatment dbx api -> login(2) has been removed. A request parameter must not generate an identity or authorization.

Read only demo mode

The installation-wide demo mode will be updated in config.local.php activated:

$config['demo_mode'] = 1;

Alternatively, the environment variable DBX DEMO MODE=1 priority. The mode is not an admin login and does not use dbxRunAsAdminBypass: The visitor remains a technical guest, but sees the admin menu and is allowed to open all modules for demonstration.

The read-only boundaries are central and independent of the interface:

  • set cfg() refuses any basic and local configuration change;
  • dbxDB::insert(), Update(), save() and delete() are rejected in the case of active rights check before the admin special authorization;
  • state-changing POST, PUT, PATCH, DELETE and token-bearing GET actions do not reach the module code and deliver HTTP 403;
  • Configuration views get secrets masked only as ******;
  • Demo pages are never stored as normal guest pages in the full-page cache.

Technical session writing processes remain expressly possible. They are calling dbxDB with verify access=0 and therefore do not go through the domain-specific demo permission check. This exception applies to the session lifecycle, not modules or configuration values.

Workflow contract

Access

  • Authenticated users load instances with DD check; Owner and admin remain relevant.
  • Guest instances are additionally bound to the active PHP session.
  • owner = 0 There is no public release alone.
  • A maximum of 100 guest instance IDs are held per session.

Atomarity and Repetition

  • Step and instance progress are stored in a transaction.
  • Identical repeated step POSTs do not produce a double workflow step.
  • Automation results are first collected and then stored together with the instance state.
  • Finish claims the state atomically as Finishing.
  • Only the request with exactly one successful status change may execute the domain operation or external completion.
  • A technical error sets a traceable status; Success only then Finished.

The new DD option Finishing is part of the status agreement and is displayed in the admin and user interface.

Shop contract

Reading and maintenance

Public catalog and normal admin GETs do not perform schema changes, default maintenance or data cleanup. dbxShopRepository::install() is guaranteed write-free without maintenance parameters. DD synchronization, channel defaults, primary groups and group images run exclusively via the explicit, authorized and tokenized admin point. Installation and maintenance. Demo data is created only by this maintenance run. The same applies to the provisioning/Aktualisierung of the shop help pages, the creation of the CMS shop media folder and the complete re-establishment of the shop media uses. Normal product, form and media views only read this data; Concrete image mappings update their media use immediately after the successful mutation.

The catalog loads filter candidates bundled. Groups, attributes and the requested channel are loaded for all candidates in a few queries. Only the visible report page then receives the complete decoration with pictures, shipping and channel groups. Direct inactive channel assignments continue to suppress an inherited active sharing.

Full product lists also use a uniform bundled data view in the shop repository. Product groups, dispatch groups, channel groups, channels, images, attribute definitions and values shall be displayed at most once per operation through: dbxDB loaded and then assigned by ID. The data view ends with the method call; it cannot continue to live obsoletely after a later write access. products() and productsByIds() use the same path, the previous individual methods remain compatible.

dbxDB::select1() cached identical single set accesses within exactly one request. The key contains DD, WHERE, column selection, rights verification, and user context. Successful write accesses invalidate the affected DD; Transactions bypass the cache and discard the server’s DD caches when completed. There is still no cross-process, session or request DB result cache.

In addition, the shop repository notices small, often reused reference lists within exactly one request: Groups, channels, attribute definitions and filter definitions. Each associated repository mutation immediately clears this cache. It does not contain user, token or shopping cart data and ends with the request. DD, rights and data access remain central in dbxDB; Only reuse and domain assignment are in the repository.

Measurement on 24. July 2026 with five uncatched HTTPS catalog calls each and 30 existing products:

Measurement point Before Later Amendment
dbxShop 179,0 ms 77.8 ms -56,5 %
total DB time 73,8 ms 23,0 ms -68,8 %
Total server-side time 223,8 ms 121.6 ms -45,7 %

All ten comparison calls provided HTTP 200. In addition, the fully decorated results of all 30 products were compared against the previous single path; Field values, types and orders were identical.

The second stage avoids invisible rendering: Map and detail templates report on their local {replacement nameWhat values they actually need. The service generates gallery, attribute table, shipping/storage blocks and purchasedbxForm only with a corresponding placeholder. Own templates thus retain the full contract. The request local cache contains only template field names and no product, user or token data.

Controlled A/B-Lauf with 15 uncached HTTPS catalog calls each:

Measurement point complete production Selective production Amendment
Total median server 203 ms 164 ms -19,2 %
Median dbxShop 127 ms 99 ms -22,0 %
isolated card generation, 9 products × 20 579.5 ms 94,0 ms -83,8 %

All 30 A/B-Aufrufe delivered HTTP 200. The reference card issued retained exactly 1,127 bytes and the same SHA-256 hash; the reference detail view was likewise byte-precisely identical. The HTTP median values are more meaningful than the mean because the local Apache/SQLite measurement contained individual DB outliers.

The admin image list eliminates the same N+1 type for labels. At 52 image records, a call to allImages() on average from 21.1 ms to 2.9 ms (-86.3%). Ten before/after calls provided the same serialized SHA-256 result hash. Product and group titles are now loaded in a maximum of two DD set queries.

Scheme, DB and output limits

DD files are the only source for tables, columns and indices. Technical requests shall not: PRAGMA, AGE TABLE, CREATE TABLE still CREATE INDEX execute. The former CMS and voice schema assistants are retained as write-free compatibility methods; The actual synchronization takes place via dbxDD in the administrative DD sequence.

Even unique tools meet this limit:

  • Permalinks are written directly via Content-DD and dbxDB normalised; a subsequent migration tool does not exist;
  • rebuild_tutorial_callouts.php uses content, media and media usage DDs and separate dbxDB transactions;
  • the two MySQL test tools use the configured dbxDB server.

Direct PDO or mysqli use is exclusively within the central class dbxDB allowed. An automated boundary test scans all project PHP files and prevents a relapse.

Complete shop invoice, payment test and workflow designers are in module templates and are dbxTPL filled. Dynamic repeat workflow field components may be generated on the server side; Data access and domain mutations remain outside the template. Inline event attributes have been removed. Delegated handlers use the common libraries ajax.js, confirm.js, OpenWin and core.js; The print sequence uses the existing print library.

Ordering and stock

Order, positions, inventory reservation and history form a transaction. Physical stock is reserved with a conditional SQL update:

stock = stock - menge
WHERE stock >= menge

Exactly one changed line is required. Thus, parallel checkout cannot generate a negative stock. A chargeback is also atomically through stock reserved and stock released claimed and is repeatable.

Channel imports check the external reference before and within the write transaction. Serialized at SQLite BEGIN IMMEDIATE parallel writers.

Checkout idempotency

Each checkout form contains a random checkout request id. The session assigns this ID to an already generated order. Repeated POSTs of the same browser session continue the same process instead of creating a second order. The session holds a maximum of 25 assignments.

Provider-Create/Capture/Complete also receive deterministic idempotency keys.

Provider return

A browser query is not evidence of payment. The assignment takes place exclusively via the server-side stored combination of:

payment_provider + payment_reference

order no This is just an additional consistency check. Thereafter:

  • atomic claim open|created|failed -> processing;
  • Only the claim winner executes Capture/Complete;
  • one aborted Processing- Claim can only be made after the configured lease payment processing retry seconds (standard 300 seconds) are re-applied; the provider continues to receive the same deterministic idempotency key;
  • PayPal: Provider ID, root status, capture status, order reference, amount and currency must fit;
  • Amazon Pay: Session ID, merchant reference, amount, currency and documented provider status must fit;
  • terminally paid states are not downgraded by later weaker returns;
  • repeated successful returns do not trigger either provider call or mail again;
  • Browser-Cancel-GETs are purely informative and do not change payment/stock status.

Live requests to PayPal or Amazon Pay were not part of the local test. Before production, sandbox end-to-end tests with the real provider accounts are mandatory.

Public channel webhook

The channel webhook is publicly accessible as a provider endpoint. Therefore, a module group cannot protect it like an admin page. The import is fail-closed:

  • order import enabled and active channels are required;
  • a not empty webhook secret is necessary;
  • The secret will come out X-DBX shop secret, X-channel secret or the request body;
  • GET query secrets are not accepted because they may appear in logs, browser histories and referrer headers;
  • Missing configuration provides HTTP 503, false authentication HTTP 403.

Before checking an external channel reference, the import also blocks the associated shop channelLine within the same transaction. This also serializes parallel imports of the same reference between separate application processes; The Unique/Duplicate Check remains the authoritative safeguard.

Verification evidence: An unauthenticated sample import provided HTTP 503; the number of shop order-Data records remained at 14.

Guest sessions, cache and header

session db guest=0 This is the new standard. PHP session, language, shopping cart and guest workflow work unchanged; fresh guests, however, do not create a central dbx sessionRecord of data per request. Authenticated users are still stored. session db guest=1 restores the previous operating behavior.

Volatile PHP sessions without a cookie

The central dbx sessionThe table and the PHP session file are separate memories. session db guest=0 prevents only anonymous database lines. An unconditional session start() Can be used for any request without PHPSESSIDE a new file in the configured session.save path Create.

An IP address explicitly does not replace the session ID:

  • many users can come via the same NAT/Proxy IP;
  • Mobile, IPv6 privacy and provider changes change the IP;
  • Shopping cart, registration and CSRF status should never be shared between IP users;
  • an IP is not a sufficiently secret authentication feature.

Normal guest sessions therefore remain bound to the random PHP-SID in the secure cookie. volatile and at the request end with session destroy() Remove only:

  • UID 0, GET or HEAD, no incoming PHP session cookie and by dbxBrowser detected robot; or
  • UID 0, GET or HEAD, no incoming PHP session cookie and a full-page cache hit already validated as unpersonalized/tokenfrei.

POSTs, authenticated users, valid cookie sessions and normal non-cacheable browser calls remain persistent. A cookie is only valid as an existing session if its SID with that of PHP under session.use strict mode accepted active SID. Random or outdated cookie values therefore do not bypass the robot/cache hit treatment. And when it is rejected, session start() earmarked Set cookie-Header removed. The conclusion is centrally located in dbx session; dbxApi Calls him both at the normal request end and before the early full-page cache exit.

Verification evidence of 24. July 2026:

Robot ohne Cookie: HTTP 200, kein Set-Cookie, Sessiondatei-Delta 0
Gast auf Page-Cache-Hit: HTTP 200, public/ETag, Sessiondatei-Delta 0
Normaler Gast, Cache-Miss: Set-Cookie, Sessiondatei-Delta 1
Folgerequest mit Cookie: gleiche SID, Sessiondatei-Delta 0

Already accumulated before deployment sess *- Files are not deleted lump sum. They must be phased out via PHP garbage collection or age-tested service maintenance. An unfiltered deletion would log out active users and lose shopping carts.

The full guest page cache:

  • uses shared locks for normal hits and exclusive locks only for initialization/Rotation;
  • removed on secure, unpersonalized hits session cookies and private cache headers;
  • Delivery Cache control: Public, ETag and 304 Not Modified;
  • Used full page browser ttlStandard 60 seconds.

Send common responses:

X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
X-Frame-Options: SAMEORIGIN

SAMEORIGINE receives the dbxapp own window/frame sequences. A strict CSP has deliberately not yet been activated due to existing older inline script blocks. Inline event attributes, on the other hand, are no longer allowed and are checked automatically.

For compatibility reasons, unknown permalinks still show the configured home representation, but deliver HTTP 404, keep the requested path and are not cached as a successful page.

Telemetry and content

The performance timer:

  • check the DD scheme via a versioned daily marker instead of per request;
  • performs retention cleanup via a non-blocking day lock;
  • writes request and detail timer in a transaction.

The content renderer:

  • cached folders for the current request for rights and inherited settings;
  • Loads used media bundled;
  • resolves language siblings directly via lng uid instead of a full permalink index scan.

Verification

Reproducible UI regression matrix

The complete run of dbxSelfTest Lead dbxUiRegressionMatrix_browser_test.js in a rendered browser viewport outside the visible area. HiddenFrames are inadmissible because they do not realistically depict focus, rectangles, overflow and layer.

The fixed matrix loads /home, Content ID 1 and a real dbxReport page as desktop (1440×960), tablet (1024×768) and mobile (390×844). It shall consider:

  • DOM, resource and navigation time budgets and horizontal overflow;
  • focusability, unique DOM IDs and visible runtime errors;
  • Insert directly in front of and behind the Bootstrap “Talk about project” link in the Jodit editor without saving the page;
  • exactly one HTML tooltip positioned above without parallel native Title;
  • one per OpenWin and central ajax.js loaded window including viewport and Z-index contract;
  • Media browser, folder tree, media grid, complete dbxForm upload form and the maintenance dialog above. No file is automatically uploaded and no maintenance started so that a regression test never mutates customer data.

The fast PHP contract dbxUiRegressionMatrix_contract_test.php prevents profiles, landing pages, interactions, budgets or the layout viewport from being removed unnoticed. Real upload / deletion processes are additionally checked with a specially generated test file and immediately rectified.

On 24 November July 2026 were executed:

PHP-Syntax: 373 von 373 Laufzeitdateien erfolgreich
Regression: 34 von 34 PHP-Testdateien erfolgreich
JavaScript: 2 von 2 Testdateien; 32 von 32 Dateien per node --check
Browser: Startseite, Shop-Katalog und Workflow-Übersicht
Browser: Warenkorb Einzel-Löschen und Leeren per AJAX/Confirm, Badge korrekt
Shop-GET: 10 Katalogaufrufe HTTP 200, DB-Hash und Änderungszeit unverändert
Shop: Mengen-/Einzeldekoration für 30 Produkte vollständig identisch
Performance: dbxShop 179,0 -> 77,8 ms; DB 73,8 -> 23,0 ms
Performance: selektives Kartenrendering 579,5 -> 94,0 ms
Performance: Admin-Bildliste 21,1 -> 2,9 ms pro Aufruf
HTTP: Cache 200/ETag, bedingter Request 304, kein Set-Cookie
HTTP: unbekannter Permalink 404 ohne Redirect
Session: 20 Robot-Shop-GETs, HTTP 200, kein Set-Cookie, Sessiondatei-Delta 0
Webhook: nicht authentifizierter POST 503, shop_order Delta 0
Workflow: alter Start-GET ohne Token, workflow_instance Delta 0
Doxygen 1.17.0: HTML vollständig erzeugt, 0 Warnungen

Especially relevant tests:

php dbx/include/tests/db_access_boundary_test.php
php dbx/include/tests/template_hygiene_test.php
php dbx/include/tests/dbxApi_security_test.php
php dbx/include/tests/dbxAjax_submitter_test.php
php dbx/include/tests/dbxSession_ephemeral_guest_test.php
php dbx/modules/dbxContent/tests/dbxContentPageCache_test.php
php dbx/modules/dbxContent_admin/tests/cms_action_security_test.php
php dbx/modules/dbxWorkflow/tests/workflow_security_test.php
php dbx/modules/dbxWorkflow_admin/tests/workflow_roundtrip_test.php
php dbx/modules/dbxShop/tests/shop_integrity_test.php
php dbx/modules/dbxShop/tests/shop_admin_action_security_test.php
php dbx/modules/dbxShop/tests/shop_repository_request_cache_test.php
php dbx/modules/dbxShop/tests/payment_validation_test.php

Introduction and fallback plan

  1. Local access data config.local.example.php derive.
  2. Rotate previously versioned access data.
  3. Shop-DD-Sync once administratively controlled.
  4. Set a strong webhook secret for each active order import and switch the provider to header/body transmission.
  5. Fully test PayPal and Amazon Pay in the Sandbox account.
  6. Check cache headers and guest pages after deployment.
  7. Only then enable in production provider and channel.

Compatibility switch:

  • session db guest=1: central guest-session persistence as before.
  • full page browser ttl=0: Deactivate browser TTL, keep server cache.

The insecure contracts API autologin, unified payment return, webhook without authentication or state-changing workflow GET without action tokens may not be withdrawn.

Related documentation