skip to Main Content

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 SentryLaravelIntegration;

....
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

Answers


  1. Chosen as BEST ANSWER

    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 (SentryEvent $event, ?SentryEventHint $hint): ?SentryEvent {
    
          $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 (SentryEvent $event, ?SentryEventHint $hint): ?SentryEvent {
    
          $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.


  2. 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 AppHelpers;
    
    use SentryEvent;
    use SentryEventHint;
    
    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 AppProviders;
    
    use AppHelpersSentrySanitizeDataOnBeforeSendListener;
    use IlluminateContractsConfigRepository as ConfigRepository;
    use IlluminateContractsFoundationCachesConfiguration;
    use SentryEvent as SentryEvent;
    use SentryEventHint as SentryEventHint;
    use SentryLaravelServiceProvider 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' => [AppHelpersSentrySanitizeDataOnBeforeSendListener::class, 'call'],
    ];
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search