How I can hide some parameters from request body once an error is reported into sentry?

631 Views Asked by At

In a laravel php application I use the sentry to keep error info for example this controller:

class MuController
{
  private function someMethodThatThrowsException()
  {
    throw new \Exception('Told ya');
  }

  public function foo()
  {
    try {
      $this->someMethodThatThrowsException();
      return new JsonResponse(204);
    } catch(\Exception $e) {
      app('sentry')->captureException($e);
      return new JsonResponse(500);
    }
  }
}

I have setup my sentry as documentation says so:

use Sentry\Laravel\Integration;

....
public function register(): void
{
    $this->reportable(function (Throwable $e) {
        Integration::captureUnhandledException($e);
    });
}

And I have exposed the sentry like this:

php artisan sentry:publish --dsn=___PUBLIC_DSN___

But sometimes I want some information from incomming http call to be hidden for security reasponse once reported to sentry. Is there a way to hide information from sentry regarding the http body?

I see that there's the functionality in https://docs.sentry.io/platforms/php/guides/laravel/configuration/filtering/ but Idk where this code should be places upon in my laravel project.

2

There are 2 best solutions below

0
Dimitrios Desyllas On

According to sentry's documentation you can set the following config at config/sentry.php:


return [
  'dsn' => env('SENTRY_LARAVEL_DSN'),
   // misc changed go here,
   'before_send' => function (\Sentry\Event $event, ?\Sentry\EventHint $hint): ?\Sentry\Event {

      $request = $event->getRequest();
     
      // process request body here

      $event->setRequest($request);
      return $event;
   }
];

For example you can remove any field in the body that contains password information:

 return [
  'dsn' => env('SENTRY_LARAVEL_DSN'),
   // misc changed go here,
   'before_send' => function (\Sentry\Event $event, ?\Sentry\EventHint $hint): ?\Sentry\Event {

      $request = $event->getRequest();
     
      // process request body here
      foreach(['pass','password'] as $filtered){
        if(isset($request['body'][$filtered])){
           $request['body'][$filtered] = '[FILTERED]';
        }
      }

      $event->setRequest($request);
      return $event;
   }
];

As you can see I use the $request['body'] and I check for any input, if input parameter matches then I replace the item with [FILTERED] therefore I avoid leaking sensitive info to 3rd party sentry.

0
MingalevME On

That is my workaround for a Laravel-based app without rewriting provider's sentry.php.

Fields to hide can be defined via SENTRY_SANITIZE_DATA-env-var, e.g. SENTRY_SANITIZE_DATA=token,password,password_confirmation,secret,foo,bar.

https://gist.github.com/mingalevme/1beec319c17286df76afad068ee00c76:

App/Helpers/SentrySanitizeDataOnBeforeSendListener.php:

<?php

namespace App\Helpers;

use Sentry\Event;
use Sentry\EventHint;

final class SentrySanitizeDataOnBeforeSendListener
{
    /**
     * @param list<non-empty-string> $secretNameList
     */
    public function __construct(
        private readonly array $secretNameList,
    ) {
    }

    public function onBeforeSend(Event $event, ?EventHint $hint): Event
    {
        /** @var array{query_string?: ?string, data?: array<string, mixed>} $request */
        $request = $event->getRequest();
        if (!empty($request['query_string'])) {
            foreach ($this->secretNameList as $secretName) {
                $request['query_string'] =
                    preg_replace(
                        "/$secretName=([^&?]+)/",
                        "$secretName=%5BFiltered%5D",
                        $request['query_string'],
                    );
            }
        }
        if (!empty($request['data'])) {
            foreach ($this->secretNameList as $secretName) {
                if (!empty($request['data'][$secretName])) {
                    $request['data'][$secretName] = '[Filtered]';
                }
            }
        }
        $event->setRequest($request);
        return $event;
    }

    public static function call(Event $event, ?EventHint $hint): Event
    {
        /** @var SentrySanitizeDataOnBeforeSendListener $self */
        $self = app(self::class);
        return $self->onBeforeSend($event, $hint);
    }
}

App/Providers/AppServiceProvider.php:

<?php

namespace App\Providers;

use App\Helpers\SentrySanitizeDataOnBeforeSendListener;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
use Illuminate\Contracts\Foundation\CachesConfiguration;
use Sentry\Event as SentryEvent;
use Sentry\EventHint as SentryEventHint;
use Sentry\Laravel\ServiceProvider as SentryServiceProvider;

final class AppServiceProvider extends AbstractServiceProvider
{
    public function register(): void
    {
        // Sentry
        if (!($this->app instanceof CachesConfiguration && $this->app->configurationIsCached())) {
            $this->app->when(SentrySanitizeDataOnBeforeSendListener::class)
                ->needs('$secretNameList')
                ->give(
                    fn (): array => array_filter(
                        explode(',', $this->getStrEnv('SENTRY_SANITIZE_DATA') ?: ''),
                    ) ?: ['token', 'password'],
                );
            /** @var ConfigRepository $config */
            $config = $this->app->make('config');
            // [SentrySanitizeDataOnBeforeSendListener::class, 'call']-format is serializable
            $config->set(
                SentryServiceProvider::$abstract,
                array_merge([
                    'before_send' => [SentrySanitizeDataOnBeforeSendListener::class, 'call'],
                ], (array)$config->get(SentryServiceProvider::$abstract, [])),
            );
        }
        // ...
    }

    /**
     * @template T of object
     * @param class-string<T> $id
     * @return T
     */
    protected function getContainerEntry(string $id): object
    {
        /** @var T $entry */
        $entry = $this->app->get($id);
        return $entry;
    }
}

Or via config/sentry.php:

return [
    'dsn' => env('SENTRY_LARAVEL_DSN'),
    // ...
    'before_send' => [\App\Helpers\SentrySanitizeDataOnBeforeSendListener::class, 'call'],
];