Skip to content

Instantly share code, notes, and snippets.

@benjaminrau
Last active July 30, 2018 18:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benjaminrau/d8fb31515cb562a751550fbb85a3a4c5 to your computer and use it in GitHub Desktop.
Save benjaminrau/d8fb31515cb562a751550fbb85a3a4c5 to your computer and use it in GitHub Desktop.
XSS Protection - will remove all html tags onFlush (before they are persisted) from fields which are ORM type text or string, with option to define allowed html tags by annotation
<?php
namespace AppBundle\Annotation\Xss;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
/**
* @Annotation
* @Target({"PROPERTY"})
*/
class AllowedTags
{
/**
* @var array
*/
private $tagNames = [];
public function __construct(array $data)
{
if (!isset($data['tagNames']) || !is_array($data['tagNames']) || empty($data['tagNames'])) {
throw new InvalidArgumentException(
sprintf('Parameter tagNames of annotation "%s" cannot be empty.',
get_class($this))
);
}
$tagNamePattern = '/^<[a-z]+>$/';
foreach ($data['tagNames'] AS $tagName)
{
if (1 !== preg_match($tagNamePattern, $tagName))
{
throw new InvalidArgumentException(
sprintf('TagName "%s" of annotation "%s" didnt match the pattern "%s".',
$tagName,
get_class($this),
$tagNamePattern
)
);
}
}
$this->tagNames = $data['tagNames'];
}
/**
* @return array
*/
public function getTagNames()
{
return $this->tagNames;
}
}
<?php
namespace AppBundle\EventSubscriber;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\EventSubscriber;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Event\OnFlushEventArgs;
use AppBundle\Annotation\Xss\AllowedTags;
class DoctrineEventSubscriber implements EventSubscriber
{
/**
* @var array
*/
CONST ALLOWED_HTML_TAGS = [];
public function getSubscribedEvents()
{
return array(
Events::onFlush,
);
}
/**
* @param OnFlushEventArgs $args
*/
public function onFlush(OnFlushEventArgs $args) {
foreach ($args->getEntityManager()->getUnitOfWork()->getScheduledEntityUpdates() AS $entity) {
$this->checkEntityForXss($args->getEntityManager(), $entity);
}
foreach ($args->getEntityManager()->getUnitOfWork()->getScheduledEntityInsertions() AS $entity) {
$this->checkEntityForXss($args->getEntityManager(), $entity);
}
}
/**
* @param EntityManager $em
* @param mixed $entity
*/
private function checkEntityForXss(EntityManager $em, $entity) {
$annotationReader = new AnnotationReader();
$entityClass = ClassUtils::getClass($entity);
$fieldMappings = $em->getClassMetadata($entityClass)->fieldMappings;
foreach ($em->getUnitOfWork()->getEntityChangeSet($entity) AS $propertyName => $propertyChangeSet) {
if (isset($fieldMappings[$propertyName]['type']) && in_array($fieldMappings[$propertyName]['type'], array('string', 'text'))) {
$reflectionProperty = new \ReflectionProperty(isset($fieldMappings[$propertyName]['inherited']) ? $fieldMappings[$propertyName]['inherited'] : $entityClass, $propertyName);
/** @var AllowedTags $allowedTagsAnnotation */
$allowedTagsAnnotation = $annotationReader->getPropertyAnnotation(
$reflectionProperty,
AllowedTags::class
);
$allowedTags = $allowedTagsAnnotation ? $allowedTagsAnnotation->getTagNames() : self::ALLOWED_HTML_TAGS;
$propertyGetter = "get" . ucfirst($propertyName);
$propertySetter = "set" . ucfirst($propertyName);
if (method_exists($entity, $propertySetter) && method_exists($entity, $propertyGetter) && $entity->{$propertyGetter}()) {
$entity->{$propertySetter}(strip_tags($entity->{$propertyGetter}(), implode("", $allowedTags)));
}
}
}
}
}
subscriber.doctrine_event_subscriber:
class: AppBundle\EventSubscriber\DoctrineEventSubscriber
tags:
- { name: doctrine.event_subscriber }
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping AS ORM;
use AppBundle\Annotation\Xss as Xss;
/**
* @ORM\Entity
* @ORM\Table(name="SomeEntity")
*/
class SomeEntity
{
/**
* @var string
*
* @ORM\Id
* @ORM\Column(type="guid")
* @ORM\GeneratedValue(strategy="UUID")
*/
private $id;
/**
* @var string
*
* @ORM\Column(type="text", nullable=true)
*/
private $profileTitle;
/**
* @var string
*
* @Xss\AllowedTags(tagNames={"<b>", "<i>", "<u>", "<ol>", "<ul>", "<li">, "<p>"})
* @ORM\Column(type="text", nullable=true)
*/
private $profileDescription;
}
@Shobzling
Copy link

Shobzling commented Jul 30, 2018

Thank you for sharing your ideas, keep posting! 👍
http://phptraininginchennai.in

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment