保护 Laravel Reverb

在构建现代应用程序时,Laravel 是 Web 开发的热门选择。凭借其庞大的生态系统,Laravel Reverb 等工具有助于增强开发人员体验,使管理后端流程变得更加容易。但是,与任何工具一样,安全性必须是重中之重。

我将尝试探索保护 Laravel Reverb 的关键实践和可操作步骤,并确保您的实施免受潜在漏洞的影响。

1. 了解 Laravel Reverb 的作用

Laravel Reverb 充当消息代理和事件管理器,促进服务之间的通信。默认情况下,它与 Laravel 的队列和事件系统深度集成。但是,由于它涉及实时数据处理,配置错误可能会使敏感操作暴露于攻击。

潜在风险

  • 未经授权访问排队事件。
  • 处理事件数据。
  • 端点过度曝光。
  • 2. 保护你的队列配置

    Laravel Reverb 依赖于队列驱动程序。配置错误的队列系统可能会导致漏洞。

    **特定于环境的驱动程序**:在生产环境中使用安全驱动程序,如 Redis。避免在生产中使用“数据库”或“同步”。这些驱动程序可能会带来性能和安全问题。“数据库”驱动程序会增加大量的数据库负载,使其容易受到 DoS 攻击,并且如果数据库受到攻击,可能会暴露敏感的作业数据。“同步”驱动程序同步执行作业,增加了通过错误暴露敏感信息的风险,并造成了攻击者可以利用的瓶颈,从而使应用程序过载。

    QUEUE_CONNECTION=redis

    **Redis 身份验证**:对 Redis 连接使用强密码。

    REDIS_PASSWORD=your_secure_password

    **TLS 加密:**如果远程使用基于云的队列,请启用 TLS 以实现安全通信。当 Redis 或其他队列驱动程序托管在外部时,这一点尤为重要。对于安全网络上的内部托管队列,TLS 可能不是必需的。

    3. 验证事件数据

    始终验证在事件和侦听器之间传递的数据。Laravel 提供了验证工具,应在事件调度和侦听器阶段应用。

    例子:

    use Illuminate\Support\Facades\Validator;
    
    class SecureEvent
    {
        public function __construct(array $data)
        {
            Validator::make($data, [
                'user_id' => 'required|integer',
                'action'  => 'required|string|max:255',
            ])->validate();
    
            $this->data = $data;
        }
    }

    4. 安全 API 端点

    Laravel Reverb 经常公开用于管理事件和队列的 API 端点。限制对这些端点的访问。

    **例子:**

    **中间件保护:**使用身份验证和授权中间件。

    Route::middleware(['auth:sanctum', 'verified'])->group(function () {
        Route::post('/reverb/dispatch', [ReverbController::class, 'dispatch']);
    });

    **速率限制**:通过限制 API 请求来防止滥用。

    Route::middleware('throttle:60,1')->group(function () {
        Route::post('/reverb/dispatch', [ReverbController::class, 'dispatch']);
    });

    5. 安全通道配置

    Laravel Reverb 频道决定事件的广播方式以及谁可以访问它们。配置错误的频道可能会暴露敏感数据或允许未经授权的访问。

    **公共频道:**

    任何知道频道名称的人都可以访问公共频道。避免使用公共频道来获取敏感信息。

    例子:

    Broadcast::channel('public-channel', function () {
        return true;  
    });

    仅将公共渠道用于非敏感数据,例如通知或一般更新。

    **私人频道:**

    私人频道要求在加入前进行身份验证。使用这些频道来处理与经过身份验证的用户相关的事件。

    例子:

    Broadcast::channel('private-channel.{userPublicId}', function ($user, $userPublicId) {
        return $user->public_id === $userPublicId && auth()->check(); // Ensure Public ID matches and user is authenticated
    });

    **存在渠道:**

    在线频道通过允许服务器实时跟踪哪些用户在线来扩展私人频道。实施严格的身份验证以防止未经授权的访问。

    例子:

    Broadcast::channel('presence-channel.{roomId}', function ($user, $roomId) {
        return $user->isInRoom($roomId); // Validate room access
    });

    6. 队列存储过载

    队列过载是指一次性添加过多作业,从而导致延迟。使用 Laravel 的 ThrottlesExceptions 中间件来限制作业处理(例如每秒 5 个作业),并使用 Supervisor 等工具管理工作者以确保系统稳定性。

    namespace App\Jobs;
    
    use Log;
    use Illuminate\Bus\Queueable;
    use Illuminate\Queue\Middleware\ThrottlesExceptions;
    use Illuminate\Contracts\Queue\ShouldQueue;
    
    class ProcessNotification implements ShouldQueue
    {
        use Queueable;
    
        public function middleware()
        {
            // Throttle: Allow max 5 jobs per second for this queue
            return [new ThrottlesExceptions(5, 1)];
        }
    
        public function handle()
        {
            // Logic to process the notification
            Log::info('Processing notification');
        }
    }

    7. 事件重放攻击

    重放攻击会重新发送拦截的事件以利用您的系统。为事件添加唯一 ID 和时间戳,在客户端和服务器上对其进行验证,以防止重复并确保仅处理新事件。

    实现唯一令牌:

    namespace App\Events;
    
    use Illuminate\Broadcasting\InteractsWithSockets;
    use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
    use Illuminate\Foundation\Events\Dispatchable;
    use Illuminate\Support\Str;
    
    class ChatMessageSent implements ShouldBroadcast
    {
        use Dispatchable, InteractsWithSockets;
    
        public string $message;
        public string $uniqueId; // Prevent replay attacks
        public int $timestamp;
    
        public function __construct(string $message)
        {
            $this->message = $message;
            $this->uniqueId = Str::uuid();
            $this->timestamp = time();
        }
    
        public function broadcastWith()
        {
            return [
                'message' => $this->message,
                'uniqueId' => $this->uniqueId,
                'timestamp' => $this->timestamp,
            ];
        }
    
        public function broadcastOn()
        {
            return ['chat-room'];
        }
    }

    通过在客户端跟踪uniqueId来防止重复处理同一事件:

    const processedEvents = new Set();
    
    Echo.channel('chat-room')
        .listen('ChatMessageSent', (event) => {
            if (!processedEvents.has(event.uniqueId)) {
                processedEvents.add(event.uniqueId);
                console.log('New message:', event.message);
            }
        });

    使用中间件确保事件时间戳是最新的:

    namespace App\Http\Middleware;
    
    use Closure;
    
    class PreventEventReplay
    {
        public function handle($request, Closure $next)
        {
            $timestamp = $request->header('X-Timestamp');
            if (abs(time() - $timestamp) > 10) { // Allow a 10-second window
                return response()->json(['error' => 'Invalid or stale request.'], 400);
            }
    
            return $next($request);
        }
    }

    8. 安全后端 SSL 连接

    即使您使用 Cloudflare 之类的服务作为代理来处理边缘 SSL,在服务器上的 VirtualHost 内配置 SSL 也很重要。这可确保端到端加密并降低潜在风险。

    **执行:**

    1. 安装 Certbot 并获取证书:

    sudo certbot --apache

    2. 更新您的 VirtualHost 以使用 SSL:

    
        ServerName yourdomain.com
        DocumentRoot /var/www/html
    
        SSLEngine on
        SSLCertificateFile /etc/letsencrypt/live/yourdomain.com/fullchain.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/yourdomain.com/privkey.pem
    

    3. 在 Cloudflare 中启用完整(严格)SSL 模式。

    9. 所有通信均使用 HTTPS

    为了确保 Reverb 与客户端或服务器之间的安全通信,请使用 HTTPS。更新以下环境变量,特别注意设置“​​REVERB_SCHEME”和“REVERB_PORT”,以确保使用 HTTPS 协议和安全端口 443:

    REVERB_HOST=example.com
    REVERB_PORT=443
    REVERB_SCHEME=https
    REVERB_APP_ID=secure_app_id
    REVERB_APP_KEY=secure_app_key
    REVERB_APP_SECRET=secure_app_secret
    
    VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
    VITE_REVERB_HOST=example.com
    VITE_REVERB_PORT=443
    VITE_REVERB_SCHEME=https