Skip to content
intermediate Phase 71 · Security Fundamentals

Input Validation

45m
2 problems
Topic Progress 0%

Server-Side Validation

Data Validation in Models

namespace Vendor\Module\Model;

use Magento\Framework\Data\Object\IdentityInterface;
use Magento\Framework\Validation\ValidatorInterface;

class Product implements IdentityInterface
{
    public function validate()
    {
        $errors = [];

        if (empty($this->getName())) {
            $errors[] = __('Product name is required');
        }

        if (strlen($this->getName()) > 255) {
            $errors[] = __('Product name must be less than 255 characters');
        }

        if (!filter_var($this->getPrice(), FILTER_VALIDATE_FLOAT)) {
            $errors[] = __('Price must be a valid number');
        }

        if ($this->getPrice() < 0) {
            $errors[] = __('Price cannot be negative');
        }

        return $errors;
    }
}

Controller Validation

namespace Vendor\Module\Controller\Adminhtml;

use Magento\Backend\App\Action;

class Save extends Action
{
    public function execute()
    {
        $data = $this->getRequest()->getParams();

        // Validate required fields
        $requiredFields = ['name', 'sku', 'price'];
        foreach ($requiredFields as $field) {
            if (empty($data[$field])) {
                $this->messageManager->addErrorMessage(
                    __('%1 is required', ucfirst($field))
                );
                return $this->resultRedirectFactory->create()->setPath('*/*/');
            }
        }

        // Validate data types
        if (!is_numeric($data['price']) || $data['price'] < 0) {
            $this->messageManager->addErrorMessage(__('Invalid price'));
            return $this->resultRedirectFactory->create()->setPath('*/*/');
        }
    }
}

Key Points

  • Always validate on the server side, never trust client validation
  • Validate data types, lengths, and formats
  • Provide clear error messages for invalid input
  • Use Magento's validation framework for consistency

Input Filtering

Magento Input Filter

use Magento\Framework\DataObject\Factory as DataObjectFactory;

class InputFilter
{
    private $dataObjectFactory;

    public function __construct(DataObjectFactory $dataObjectFactory)
    {
        $this->dataObjectFactory = $dataObjectFactory;
    }

    public function filter($input)
    {
        $filtered = [];

        // Remove whitespace
        $filtered['name'] = trim($input['name'] ?? '');

        // Strip HTML tags
        $filtered['description'] = strip_tags($input['description'] ?? '');

        // Sanitize email
        $filtered['email'] = filter_var($input['email'] ?? '', FILTER_SANITIZE_EMAIL);

        // Sanitize integer
        $filtered['quantity'] = filter_var($input['quantity'] ?? 0, FILTER_SANITIZE_NUMBER_INT);

        // Sanitize float
        $filtered['price'] = filter_var($input['price'] ?? 0, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);

        return $filtered;
    }
}

Filter Varien Data

use Varien_Object;

$object = new Varien_Object($inputData);

// Apply filters
$object->setData(
    'name',
    preg_replace('/[^a-zA-Z0-9\s]/', '', $object->getName())
);

// Sanitize using Varien_Filter_Input
$filters = [
    'name' => ['stripTags', 'trim'],
    'email' => ['validateEmail'],
    'price' => ['validateNumber', 'greaterThanZero'],
];

Key Points

  • Filter input before processing, not after
  • Use appropriate filters for each data type
  • Preserve original data for error messages
  • Log filtered input for debugging

Magento Built-in Validators

EAV Attribute Validation

// Add validation to EAV attribute
$eavSetup->addAttribute(
    \Magento\Catalog\Model\Product::ENTITY,
    'custom_field',
    [
        'type' => 'varchar',
        'label' => 'Custom Field',
        'input' => 'text',
        'required' => true,
        'validate_rules' => json_encode([
            'max_text_length' => 255,
            'min_text_length' => 1,
        ]),
    ]
);

Form Validation

<!-- app/code/Vendor/Module/view/adminhtml/templates/form.phtml -->
<form id="custom-form" action="<?= $block->getFormAction() ?>" method="post">
    <?= $block->getBlockHtml('formkey') ?>
    
    <div class="field required">
        <label for="name">Name</label>
        <input type="text" name="name" id="name"
               class="input-text required-entry"
               maxlength="255" />
    </div>
    
    <div class="field required">
        <label for="email">Email</label>
        <input type="email" name="email" id="email"
               class="input-text required-entry validate-email" />
    </div>
    
    <div class="field required">
        <label for="price">Price</label>
        <input type="text" name="price" id="price"
               class="input-text required-entry validate-number validate-greater-than-zero" />
    </div>
</form>

<script>
    require(['jquery', 'mage/validation'], function($) {
        $('#custom-form').validation();
    });
</script>

Custom Validator

namespace Vendor\Module\Model\Validation;

use Magento\Framework\Validation\ValidatorInterface;
use Magento\Framework\Validation\Result;

class CustomValidator implements ValidatorInterface
{
    public function validate($entity)
    {
        $result = new Result();

        if (strlen($entity->getData('sku')) < 3) {
            $result->addError(__('SKU must be at least 3 characters'));
        }

        if (!preg_match('/^[A-Z]{2}\d{6}$/', $entity->getData('sku'))) {
            $result->addError(__('SKU format must be XX######'));
        }

        return $result;
    }
}

Key Points

  • Use Magento's built-in validators for common patterns
  • Custom validators implement ValidatorInterface
  • Combine client-side and server-side validation
  • Validate early in the request lifecycle

Data Sanitization

PHP Sanitization Functions

// String sanitization
$name = trim($input);
$name = preg_replace('/[^a-zA-Z0-9\s\-]/', '', $name);
$name = htmlspecialchars($name, ENT_QUOTES, 'UTF-8');

// Email sanitization
$email = filter_var($input, FILTER_SANITIZE_EMAIL);

// URL sanitization
$url = filter_var($input, FILTER_SANITIZE_URL);

// Integer sanitization
$quantity = filter_var($input, FILTER_SANITIZE_NUMBER_INT);

// Float sanitization
$price = filter_var($input, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);

// Strip HTML
$clean = strip_tags($input, '<p><br><strong><em>');

// Remove null bytes
$clean = str_replace(chr(0), '', $input);

Magento Data Sanitization

use Magento\Framework\Escaper;

class DataSanitizer
{
    private $escaper;

    public function __construct(Escaper $escaper)
    {
        $this->escaper = $escaper;
    }

    public function sanitize($data)
    {
        return [
            'name' => $this->escaper->escapeHtml($data['name']),
            'url' => $this->escaper->escapeUrl($data['url']),
            'css' => $this->escaper->escapeCss($data['css']),
            'js' => $this->escaper->escapeJs($data['js']),
        ];
    }
}

Key Points

  • Sanitize before storing in database
  • Use context-appropriate sanitization
  • Preserve data integrity while removing threats
  • Log sanitization for security auditing

Practice Problems

0 / 2 solved
Custom Validation Rule

Create a custom validator for product SKUs that enforces a specific format (2 letters + 6 digits).

Solution
<?php
namespace Vendor\Module\Model\Validation;

use Magento\Framework\Validation\ValidatorInterface;
use Magento\Framework\Validation\Result;

class SkuFormatValidator implements ValidatorInterface
{
    public function validate($entity)
    {
        $result = new Result();
        $sku = $entity->getData('sku');
        
        if (empty($sku)) {
            $result->addError(__('SKU is required'));
            return $result;
        }
        
        if (!preg_match('/^[A-Z]{2}\d{6}$/', $sku)) {
            $result->addError(__('SKU must be format XX######'));
        }
        
        return $result;
    }
}
Input Sanitization Pipeline

Build a sanitization pipeline that processes user input through multiple stages.

Solution
public function sanitize($input) {
    $sanitized = $input;
    $log = [];
    
    // Trim whitespace
    $sanitized = trim($sanitized);
    if ($sanitized !== $input) {
        $log[] = 'Trimmed whitespace';
    }
    
    // Remove HTML tags
    $stripped = strip_tags($sanitized);
    if ($stripped !== $sanitized) {
        $log[] = 'Removed HTML tags';
        $sanitized = $stripped;
    }
    
    // Escape HTML entities
    $escaped = htmlspecialchars($sanitized, ENT_QUOTES, 'UTF-8');
    if ($escaped !== $sanitized) {
        $log[] = 'Escaped HTML entities';
        $sanitized = $escaped;
    }
    
    if (!empty($log)) {
        $this->logger->info('Input sanitized', ['actions' => $log]);
    }
    
    return $sanitized;
}

Quiz

1. Why validate on the server side?

Question 1 options

2. Which function sanitizes email addresses?

Question 2 options

3. What does strip_tags() do?

Question 3 options

4. How should validation errors be handled?

Question 4 options

Flashcards

Question

Server-side validation reason?

Answer

Client-side validation can be bypassed

Question

Email sanitization?

Answer

filter_var($email, FILTER_SANITIZE_EMAIL)

Question

Magento validation classes?

Answer

ValidatorInterface, Escaper, Varien_Filter_Input

Question

Input validation rule?

Answer

Validate early, sanitize before storage, never trust client

Revision Notes

Key Takeaways

  • 1. Always validate on the server side
  • 2. Use PHP's filter_var() for input sanitization
  • 3. Magento provides Escaper class for output escaping
  • 4. Combine client-side UX with server-side security

Interview Tips

  • Explain why client-side validation is insufficient
  • Discuss different sanitization methods
  • Know Magento's validation framework

Cheat Sheet

Input Validation

  • Always validate server-side
  • Sanitize: filter_var(), strip_tags()
  • Magento: Escaper, ValidatorInterface
  • Validate types, lengths, formats