Alle Artikel
Shopware 65 min

Shopware 6 Plugin entwickeln – Sicherheit & Best Practices

Wer ein Shopware 6 Plugin entwickeln möchte, steht vor einer wichtigen Frage: Wie stelle ich sicher, dass mein Code nicht nur funktioniert, sondern auch sicher ist? Shopware 6 bietet durch seine moderne Symfony-basierte Architektur viele eingebaute Sicherheitsmechanismen – aber nur wenn du sie richtig einsetzt.

In diesem Artikel zeige ich dir die wichtigsten Sicherheitsaspekte und Best Practices beim Shopware 6 Plugin entwickeln. Von Input-Validierung über ACL-Rechte bis zu häufigen Sicherheitslücken: Du erfährst, worauf es wirklich ankommt.

Warum Sicherheit beim Plugin-Entwickeln kritisch ist

Shopware 6 Plugins haben oft weitreichende Zugriffe auf das System: Kundendaten, Bestellungen, Zahlungsinformationen. Ein unsicheres Plugin kann zum Einfallstor für Angreifer werden und nicht nur deinen Ruf, sondern auch den deiner Kunden gefährden.

Die gute Nachricht: Mit den richtigen Praktiken lassen sich die meisten Sicherheitsprobleme von Anfang an vermeiden.

Input-Validierung: Niemals Benutzereingaben vertrauen

Die goldene Regel der Webentwicklung gilt auch, wenn du ein Shopware 6 Plugin entwickeln willst: Vertraue niemals Benutzereingaben. Jede Eingabe muss validiert und sanitisiert werden.

Symfony Validator nutzen

Shopware 6 nutzt den Symfony Validator. Definiere deine Validierungsregeln direkt in Entity-Klassen:

<?php declare(strict_types=1);

namespace MyPlugin\Core\Content\Example;

use Shopware\Core\Framework\DataAbstractionLayer\Entity;
use Symfony\Component\Validator\Constraints as Assert;

class ExampleEntity extends Entity
{
    #[Assert\NotBlank]
    #[Assert\Length(min: 3, max: 255)]
    private string $name;

    #[Assert\Email]
    private string $email;

    #[Assert\Regex(
        pattern: '/^[a-zA-Z0-9_-]+$/',
        message: 'Only alphanumeric characters allowed'
    )]
    private string $identifier;
}

Request-Daten validieren

In Controllern solltest du Request-Parameter explizit validieren:

<?php declare(strict_types=1);

namespace MyPlugin\Storefront\Controller;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Shopware\Storefront\Controller\StorefrontController;

#[Route(defaults: ['_routeScope' => ['storefront']])]
class ExampleController extends StorefrontController
{
    #[Route(path: '/example/save', name: 'example.save', methods: ['POST'])]
    public function save(Request $request): Response
    {
        $data = $request->request->all();
        
        // Explizite Validierung
        if (empty($data['name']) || strlen($data['name']) > 255) {
            throw new \InvalidArgumentException('Invalid name');
        }
        
        // Whitelist-Ansatz: Nur erwartete Felder verarbeiten
        $allowedFields = ['name', 'email', 'description'];
        $filtered = array_intersect_key($data, array_flip($allowedFields));
        
        // Weitere Verarbeitung...
    }
}

SQL-Injection vermeiden: DAL richtig einsetzen

Einer der größten Vorteile beim Shopware 6 Plugin entwickeln ist das Data Abstraction Layer (DAL). Es verhindert SQL-Injection automatisch – wenn du es richtig nutzt.

Richtig: DAL-Criteria verwenden

<?php declare(strict_types=1);

use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;

// RICHTIG: Parametrisierte Queries durch DAL
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('email', $userInput));

$result = $this->exampleRepository->search($criteria, $context);

Falsch: Raw SQL mit unescapten Werten

<?php declare(strict_types=1);

// FALSCH: Niemals so machen!
$sql = "SELECT * FROM user WHERE email = '" . $userInput . "'";
$connection->executeQuery($sql);

Falls du doch Raw SQL brauchst (sehr selten), nutze Prepared Statements:

<?php declare(strict_types=1);

$sql = "SELECT * FROM custom_table WHERE identifier = :identifier";
$result = $connection->executeQuery($sql, ['identifier' => $userInput]);

XSS-Schutz in Templates

Cross-Site-Scripting (XSS) ist ein häufiges Problem. Shopware nutzt Twig, das automatisch Output-Escaping bietet.

Automatisches Escaping nutzen

{# SICHER: Automatisches HTML-Escaping #}
<p>{{ userInput }}</p>

{# UNSICHER: Raw-Output nur für vertrauenswürdige Daten #}
<p>{{ trustedHtml|raw }}</p>

JavaScript-Context beachten

{# UNSICHER in JavaScript-Context #}
<script>
    var data = "{{ userInput }}";
</script>

{# BESSER: JSON-Encoding #}
<script>
    var data = {{ userInput|json_encode|raw }};
</script>

ACL: Berechtigungen korrekt implementieren

Beim Shopware 6 Plugin entwickeln für die Administration sind Access Control Lists (ACL) unverzichtbar.

ACL-Ressourcen definieren

Definiere ACL-Privileges in deiner plugin.xml oder per Code:

<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <admin>
        <privileges>
            <privilege key="my_plugin.viewer">
                <resource>my_custom_entity</resource>
                <privilege>read</privilege>
            </privilege>
            <privilege key="my_plugin.editor">
                <resource>my_custom_entity</resource>
                <privilege>update</privilege>
            </privilege>
        </privileges>
    </admin>
</plugin>

ACL in API-Routes prüfen

<?php declare(strict_types=1);

namespace MyPlugin\Core\Api;

use Symfony\Component\Routing\Annotation\Route;
use Shopware\Core\Framework\Routing\Annotation\Acl;

#[Route(defaults: ['_routeScope' => ['api']])]
class ExampleApiController
{
    #[Route(
        path: '/api/_action/my-plugin/data',
        name: 'api.action.my_plugin.data',
        methods: ['GET']
    )]
    #[Acl(['my_plugin.viewer'])]
    public function getData(): JsonResponse
    {
        // Nur Nutzer mit my_plugin.viewer Berechtigung kommen hier rein
    }
}

CSRF-Protection

Shopware 6 bietet CSRF-Schutz für Formulare. Nutze ihn konsequent:

{% block example_form %}
    <form method="post" action="{{ path('example.save') }}">
        {{ sw_csrf('example.save') }}
        
        <input type="text" name="name" required>
        <button type="submit">Speichern</button>
    </form>
{% endblock %}

Im Controller wird das Token automatisch validiert, wenn du die Route entsprechend konfigurierst.

Secrets und API-Keys sicher speichern

Niemals API-Keys oder Secrets hardcoded im Code speichern!

System-Config nutzen

<?php declare(strict_types=1);

namespace MyPlugin\Service;

use Shopware\Core\System\SystemConfig\SystemConfigService;

class ApiService
{
    public function __construct(
        private SystemConfigService $systemConfig
    ) {}
    
    public function callApi(): void
    {
        // API-Key aus Plugin-Config laden
        $apiKey = $this->systemConfig->get('MyPlugin.config.apiKey');
        
        if (empty($apiKey)) {
            throw new \RuntimeException('API-Key not configured');
        }
        
        // API-Call mit Key...
    }
}

Definiere die Config in config.xml mit type="password" für sensible Daten:

<input-field type="password">
    <name>apiKey</name>
    <label>API Key</label>
    <label lang="de-DE">API-Schlüssel</label>
</input-field>

Häufige Sicherheitsfehler beim Shopware 6 Plugin entwickeln

1. Fehlende Context-Prüfung

// FALSCH: Sales-Channel-Context in Admin-Kontext verwenden
public function process(SalesChannelContext $context): void
{
    // Kann zu Rechte-Eskalation führen
}

// RICHTIG: Context-Typ prüfen
public function process(Context $context): void
{
    if (!$context->getSource() instanceof AdminApiSource) {
        throw new \RuntimeException('Admin access required');
    }
}

2. Ungeschützte API-Endpoints

Jeder API-Endpoint braucht explizite Berechtigungsprüfung – entweder per ACL oder manuelle Validierung.

3. File-Upload ohne Validierung

// IMMER Dateityp, Größe und Inhalt validieren
$allowedMimeTypes = ['image/jpeg', 'image/png'];
$maxSize = 5 * 1024 * 1024; // 5MB

if (!in_array($file->getMimeType(), $allowedMimeTypes)) {
    throw new \InvalidArgumentException('Invalid file type');
}

Dependency-Management und Updates

Halte deine Dependencies aktuell. Nutze composer audit regelmäßig:

composer audit

Definiere in deiner composer.json Versions-Constraints, die Security-Updates erlauben:

{
    "require": {
        "guzzlehttp/guzzle": "^7.5"
    }
}

Testing: Sicherheit automatisiert prüfen

Schreibe Tests, die auch Sicherheitsaspekte abdecken:

<?php declare(strict_types=1);

namespace MyPlugin\Tests\Unit;

use PHPUnit\Framework\TestCase;

class SecurityTest extends TestCase
{
    public function testSqlInjectionPrevention(): void
    {
        $maliciousInput = "'; DROP TABLE users; --";
        
        // Test, dass Input korrekt escaped wird
        $criteria = new Criteria();
        $criteria->addFilter(new EqualsFilter('name', $maliciousInput));
        
        // Weitere Assertions...
    }
}

Fazit: Sicherheit von Anfang an mitdenken

Wenn du ein Shopware 6 Plugin entwickeln willst, ist Sicherheit kein nachträglicher Gedanke, sondern muss von Beginn an Teil deiner Architektur sein. Die wichtigsten Punkte:

  • Nutze das DAL statt Raw SQL
  • Validiere alle Eingaben mit Symfony Validator
  • Implementiere ACL-Rechte für Admin-Funktionen
  • Escapen Output in Twig-Templates
  • Speichere Secrets in der System-Config
  • Halte Dependencies aktuell

Mit diesen Best Practices schaffst du eine solide Grundlage für sichere und professionelle Shopware 6 Plugins.

Du arbeitest an einem Shopware 6 Projekt und brauchst Unterstützung bei der Plugin-Entwicklung? Ich helfe dir gerne – mit über 10 Jahren Erfahrung in E-Commerce und maßgeschneiderten Shopware-Lösungen.

Shopware 6 Plugin entwickelnShopware 6FreelancerWebentwicklungDüsseldorf