# Broken Object Level Authorization (BOLA)

## O que é?

**Broken Object Level Authorization (BOLA)**, também conhecida como **IDOR (Insecure Direct Object Reference)**, é uma vulnerabilidade onde a API não valida adequadamente se o usuário autenticado tem permissão para acessar um objeto específico. O atacante pode manipular identificadores de objetos (IDs) para acessar recursos que pertencem a outros usuários.

## Por que é tão comum?

BOLA é a vulnerabilidade #1 no OWASP API Security Top 10 porque:

* APIs modernas expõem muitos objetos diretamente via IDs
* Desenvolvedores assumem que "se o usuário está autenticado, pode acessar qualquer ID"
* Validação de propriedade do objeto é frequentemente esquecida
* Fácil de explorar: basta mudar um ID na URL

## Como funciona o ataque?

### Cenário vulnerável típico

```
Usuário A (ID: 100) está autenticado
Acessa: GET /api/orders/501 (seu pedido)
API retorna: Dados do pedido 501

Usuário A tenta:
GET /api/orders/502 (pedido do usuário B)
API vulnerável: Retorna dados do pedido 502 ❌

API deveria: Verificar se o pedido 502 pertence ao usuário A ✅
```

### Fluxo do ataque

```
1. Atacante faz login legítimo
2. Recebe seu próprio objeto (ex: /api/profile/123)
3. Muda o ID para outro valor (ex: /api/profile/124)
4. API não verifica propriedade
5. Atacante acessa dados de outro usuário
```

## Exemplos práticos de vulnerabilidades

### 1. Acesso a pedidos de outros usuários

```javascript
// ❌ VULNERÁVEL - Não verifica se o pedido pertence ao usuário
app.get('/api/orders/:orderId', authenticateUser, async (req, res) => {
  const orderId = req.params.orderId;
  
  // Busca o pedido sem verificar o dono
  const order = await Order.findById(orderId);
  
  if (!order) {
    return res.status(404).json({ error: 'Order not found' });
  }
  
  // Retorna o pedido sem verificação de propriedade
  res.json(order);
});

// Ataque:
// GET /api/orders/999 (pedido de outro usuário)
// Retorna dados do pedido 999 ❌
```

```javascript
// ✅ SEGURO - Verifica se o pedido pertence ao usuário autenticado
app.get('/api/orders/:orderId', authenticateUser, async (req, res) => {
  const orderId = req.params.orderId;
  const userId = req.user.id; // ID do usuário autenticado
  
  // Busca o pedido E verifica o dono
  const order = await Order.findOne({
    _id: orderId,
    userId: userId // Verifica propriedade
  });
  
  if (!order) {
    return res.status(404).json({ error: 'Order not found' });
  }
  
  res.json(order);
});

// Ataque:
// GET /api/orders/999 (pedido de outro usuário)
// Retorna 404 porque não pertence ao usuário ✅
```

### 2. Modificação de dados de outros usuários

```javascript
// ❌ VULNERÁVEL - Permite editar qualquer perfil
app.put('/api/users/:userId', authenticateUser, async (req, res) => {
  const userId = req.params.userId;
  const updates = req.body;
  
  // Atualiza usuário sem verificar quem está fazendo a requisição
  const user = await User.update(userId, updates);
  
  res.json({ message: 'User updated', user });
});

// Ataque:
// PUT /api/users/456
// { "email": "hacker@evil.com", "phone": "123456789" }
// Modifica dados do usuário 456 ❌
```

```javascript
// ✅ SEGURO - Apenas permite editar o próprio perfil
app.put('/api/users/:userId', authenticateUser, async (req, res) => {
  const userId = req.params.userId;
  const authenticatedUserId = req.user.id;
  
  // Verifica se está tentando editar o próprio perfil
  if (userId !== authenticatedUserId) {
    return res.status(403).json({ 
      error: 'Forbidden: You can only edit your own profile' 
    });
  }
  
  const updates = req.body;
  const user = await User.update(userId, updates);
  
  res.json({ message: 'User updated', user });
});

// Ataque:
// PUT /api/users/456 (outro usuário)
// Retorna 403 Forbidden ✅
```

### 3. Deleção de recursos de outros usuários

```javascript
// ❌ VULNERÁVEL - Deleta qualquer documento
app.delete('/api/documents/:docId', authenticateUser, async (req, res) => {
  const docId = req.params.docId;
  
  await Document.delete(docId);
  
  res.json({ message: 'Document deleted' });
});

// Ataque:
// DELETE /api/documents/789
// Deleta documento de outro usuário ❌
```

```javascript
// ✅ SEGURO - Verifica propriedade antes de deletar
app.delete('/api/documents/:docId', authenticateUser, async (req, res) => {
  const docId = req.params.docId;
  const userId = req.user.id;
  
  // Busca documento E verifica dono
  const document = await Document.findOne({
    _id: docId,
    ownerId: userId
  });
  
  if (!document) {
    return res.status(404).json({ 
      error: 'Document not found or you do not have permission' 
    });
  }
  
  await Document.delete(docId);
  
  res.json({ message: 'Document deleted' });
});
```

### 4. Acesso a recursos aninhados

```javascript
// ❌ VULNERÁVEL - Não verifica propriedade do recurso pai
app.get('/api/companies/:companyId/invoices/:invoiceId', 
  authenticateUser, 
  async (req, res) => {
    const { companyId, invoiceId } = req.params;
    
    // Busca invoice sem verificar se a company pertence ao usuário
    const invoice = await Invoice.findOne({
      _id: invoiceId,
      companyId: companyId
    });
    
    res.json(invoice);
  }
);

// Ataque:
// GET /api/companies/999/invoices/123
// Acessa invoice de company que não pertence ao usuário ❌
```

```javascript
// ✅ SEGURO - Verifica propriedade em cascata
app.get('/api/companies/:companyId/invoices/:invoiceId', 
  authenticateUser, 
  async (req, res) => {
    const { companyId, invoiceId } = req.params;
    const userId = req.user.id;
    
    // Primeiro verifica se a company pertence ao usuário
    const company = await Company.findOne({
      _id: companyId,
      ownerId: userId
    });
    
    if (!company) {
      return res.status(404).json({ error: 'Company not found' });
    }
    
    // Depois busca a invoice da company verificada
    const invoice = await Invoice.findOne({
      _id: invoiceId,
      companyId: companyId
    });
    
    if (!invoice) {
      return res.status(404).json({ error: 'Invoice not found' });
    }
    
    res.json(invoice);
  }
);
```

### 5. Compartilhamento de recursos

```javascript
// ❌ VULNERÁVEL - Não trata compartilhamento adequadamente
app.get('/api/files/:fileId', authenticateUser, async (req, res) => {
  const fileId = req.params.fileId;
  const userId = req.user.id;
  
  // Apenas verifica se é o dono
  const file = await File.findOne({
    _id: fileId,
    ownerId: userId
  });
  
  if (!file) {
    return res.status(404).json({ error: 'File not found' });
  }
  
  res.json(file);
});

// Problema: Usuário não consegue acessar arquivos compartilhados com ele
```

```javascript
// ✅ SEGURO - Trata propriedade E compartilhamento
app.get('/api/files/:fileId', authenticateUser, async (req, res) => {
  const fileId = req.params.fileId;
  const userId = req.user.id;
  
  // Verifica se é dono OU tem acesso compartilhado
  const file = await File.findOne({
    _id: fileId,
    $or: [
      { ownerId: userId },
      { sharedWith: { $in: [userId] } }
    ]
  });
  
  if (!file) {
    return res.status(404).json({ 
      error: 'File not found or you do not have permission' 
    });
  }
  
  res.json(file);
});
```

## Técnicas de exploração

### 1. Enumeração sequencial de IDs

```bash
# IDs numéricos sequenciais (mais fácil)
GET /api/orders/1
GET /api/orders/2
GET /api/orders/3
...
GET /api/orders/1000

# Script de enumeração
for i in {1..1000}; do
  curl -H "Authorization: Bearer $TOKEN" \
    "https://api.example.com/orders/$i"
done
```

### 2. IDs não-sequenciais (UUIDs, hashes)

```bash
# Mesmo com UUIDs, ainda é vulnerável se não verificar propriedade
GET /api/documents/a3f2b1c4-5678-90ab-cdef-1234567890ab
GET /api/documents/b4e3c2d5-6789-01bc-def0-234567890abc

# Atacante pode:
# - Interceptar IDs em responses
# - Encontrar IDs em logs, URLs compartilhadas
# - Usar IDs vazados de outros lugares
```

### 3. Manipulação de query parameters

```bash
# Parâmetros na URL
GET /api/reports?userId=123  # Tenta acessar dados de outro usuário
GET /api/data?accountId=456  # Muda o ID da conta

# Parâmetros no body
POST /api/transfer
{
  "fromAccount": "999",  # Conta de outro usuário
  "toAccount": "123",
  "amount": 1000
}
```

### 4. Mass Assignment combinado com BOLA

```bash
# Atacante tenta modificar campos que não deveria
PUT /api/profile/123
{
  "name": "John",
  "email": "john@example.com",
  "userId": "456"  # Tenta mudar para ID de outro usuário
}
```

### 5. Acesso a recursos relacionados

```bash
# Explorar relacionamentos entre objetos
GET /api/users/123/orders        # Pedidos de outro usuário
GET /api/users/123/addresses     # Endereços de outro usuário
GET /api/users/123/payment-methods  # Métodos de pagamento
GET /api/companies/456/employees    # Funcionários de outra empresa
```

## Impacto do BOLA

### Gravidade: CRÍTICA

**Consequências:**

1. **Vazamento de dados sensíveis**:
   * Informações pessoais (CPF, endereço, telefone)
   * Dados financeiros (cartões, transações)
   * Documentos confidenciais
   * Histórico médico
2. **Modificação não autorizada**:
   * Alterar dados de outros usuários
   * Modificar configurações de contas alheias
   * Deletar recursos de terceiros
3. **Violação de privacidade**:
   * Acesso a conversas privadas
   * Visualização de arquivos pessoais
   * Leitura de mensagens diretas
4. **Impacto financeiro**:
   * Roubo de informações bancárias
   * Manipulação de transações
   * Acesso a dados de pagamento
5. **Conformidade e legal**:
   * Violação de LGPD/GDPR
   * Multas regulatórias
   * Processos judiciais
   * Danos à reputação

### Exemplos reais de impacto

```
Caso 1: Aplicativo de Delivery
- Vulnerabilidade: GET /api/orders/{orderId}
- Atacante enumera IDs de 1 a 100.000
- Obtém: Endereços, telefones, histórico de compras
- Dados de 50.000 clientes vazados

Caso 2: Sistema de Saúde
- Vulnerabilidade: GET /api/patients/{patientId}/records
- Atacante acessa prontuários médicos de outros pacientes
- Violação grave de privacidade médica
- Multa de milhões + processos

Caso 3: Banco Digital
- Vulnerabilidade: GET /api/accounts/{accountId}/balance
- Atacante consegue ver saldos de outras contas
- PUT /api/accounts/{accountId} permite modificar dados
- Fraude financeira + pânico dos clientes

Caso 4: Rede Social
- Vulnerabilidade: GET /api/messages/{conversationId}
- Atacante lê mensagens privadas de outros usuários
- Vazamento de conversas íntimas
- Perda massiva de confiança na plataforma
```

## Como prevenir BOLA

### 1. Sempre validar propriedade do objeto

```javascript
// Padrão de validação
async function validateOwnership(objectId, userId, Model) {
  const object = await Model.findOne({
    _id: objectId,
    userId: userId
  });
  
  if (!object) {
    throw new Error('Object not found or access denied');
  }
  
  return object;
}

// Uso
app.get('/api/documents/:docId', authenticateUser, async (req, res) => {
  try {
    const document = await validateOwnership(
      req.params.docId,
      req.user.id,
      Document
    );
    
    res.json(document);
  } catch (error) {
    res.status(404).json({ error: error.message });
  }
});
```

### 2. Usar contexto do usuário autenticado

```javascript
// ❌ NUNCA confiar em IDs vindos do cliente
app.get('/api/profile', authenticateUser, async (req, res) => {
  const userId = req.query.userId; // Vem do cliente ❌
  const user = await User.findById(userId);
  res.json(user);
});

// ✅ SEMPRE usar ID do token de autenticação
app.get('/api/profile', authenticateUser, async (req, res) => {
  const userId = req.user.id; // Vem do token JWT ✅
  const user = await User.findById(userId);
  res.json(user);
});
```

### 3. Implementar middleware de autorização

```javascript
// middleware/authorize.js
async function authorizeResource(resourceType) {
  return async (req, res, next) => {
    const resourceId = req.params[`${resourceType}Id`];
    const userId = req.user.id;
    
    let resource;
    
    switch(resourceType) {
      case 'order':
        resource = await Order.findOne({ _id: resourceId, userId });
        break;
      case 'document':
        resource = await Document.findOne({ _id: resourceId, ownerId: userId });
        break;
      case 'invoice':
        resource = await Invoice.findOne({ _id: resourceId, userId });
        break;
      default:
        return res.status(400).json({ error: 'Invalid resource type' });
    }
    
    if (!resource) {
      return res.status(404).json({ 
        error: `${resourceType} not found or access denied` 
      });
    }
    
    // Anexa o recurso verificado ao request
    req.authorizedResource = resource;
    next();
  };
}

// Uso
app.get('/api/orders/:orderId', 
  authenticateUser,
  authorizeResource('order'),
  (req, res) => {
    // req.authorizedResource já foi validado
    res.json(req.authorizedResource);
  }
);

app.put('/api/documents/:documentId',
  authenticateUser,
  authorizeResource('document'),
  async (req, res) => {
    const document = req.authorizedResource;
    await document.update(req.body);
    res.json(document);
  }
);
```

### 4. Usar UUIDs ao invés de IDs sequenciais

```javascript
// ❌ IDs sequenciais facilitam enumeração
// User ID: 1, 2, 3, 4, 5...
// Order ID: 1001, 1002, 1003...

// ✅ UUIDs dificultam (mas não previnem sozinhos!)
const { v4: uuidv4 } = require('uuid');

const userSchema = new Schema({
  _id: {
    type: String,
    default: uuidv4 // a3f2b1c4-5678-90ab-cdef-1234567890ab
  },
  name: String,
  email: String
});

// IMPORTANTE: UUID sozinho NÃO previne BOLA
// Você AINDA precisa validar propriedade!
app.get('/api/orders/:orderId', authenticateUser, async (req, res) => {
  // Mesmo com UUID, precisa validar
  const order = await Order.findOne({
    _id: req.params.orderId,
    userId: req.user.id // Validação de propriedade
  });
  
  if (!order) {
    return res.status(404).json({ error: 'Order not found' });
  }
  
  res.json(order);
});
```

### 5. Implementar rate limiting para enumeração

```javascript
const rateLimit = require('express-rate-limit');

// Limitar requisições por IP
const resourceAccessLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutos
  max: 100, // Máximo 100 requisições
  message: 'Too many requests, please try again later',
  standardHeaders: true,
  legacyHeaders: false,
  // Retornar 429 em vez de 403
  statusCode: 429
});

// Aplicar em endpoints sensíveis
app.get('/api/orders/:orderId', 
  resourceAccessLimiter,
  authenticateUser,
  authorizeResource('order'),
  getOrder
);

// Limitar por usuário (mais rigoroso)
const userAccessLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 50,
  keyGenerator: (req) => req.user.id, // Limita por usuário
  skip: (req) => !req.user,
  message: 'Too many requests from this account'
});
```

### 6. Logging e detecção de ataques

```javascript
// Middleware para detectar tentativas de BOLA
function detectBOLAAttempts(req, res, next) {
  const originalJson = res.json;
  
  res.json = function(data) {
    // Se retornou 404, pode ser tentativa de BOLA
    if (res.statusCode === 404) {
      const logEntry = {
        timestamp: new Date(),
        userId: req.user?.id,
        ip: req.ip,
        endpoint: req.originalUrl,
        method: req.method,
        resourceId: req.params,
        userAgent: req.get('user-agent'),
        suspiciousActivity: 'POSSIBLE_BOLA_ATTEMPT'
      };
      
      SecurityLog.create(logEntry);
      
      // Verificar padrão de ataque (múltiplos 404s)
      checkForAttackPattern(req.user?.id, req.ip);
    }
    
    return originalJson.call(this, data);
  };
  
  next();
}

// Função para detectar padrões de ataque
async function checkForAttackPattern(userId, ip) {
  const last5Minutes = new Date(Date.now() - 5 * 60 * 1000);
  
  const suspiciousRequests = await SecurityLog.countDocuments({
    $or: [{ userId }, { ip }],
    timestamp: { $gte: last5Minutes },
    suspiciousActivity: 'POSSIBLE_BOLA_ATTEMPT'
  });
  
  // Se mais de 10 tentativas em 5 minutos
  if (suspiciousRequests > 10) {
    // Alertar equipe de segurança
    await sendSecurityAlert({
      type: 'BOLA_ATTACK_DETECTED',
      userId,
      ip,
      count: suspiciousRequests,
      timestamp: new Date()
    });
    
    // Considerar bloqueio temporário
    await blockUserTemporarily(userId || ip);
  }
}
```

### 7. Testes automatizados de autorização

```javascript
// tests/authorization.test.js
describe('BOLA Protection Tests', () => {
  let user1Token, user2Token;
  let user1Order, user2Order;
  
  beforeEach(async () => {
    // Criar dois usuários
    const user1 = await User.create({ email: 'user1@test.com' });
    const user2 = await User.create({ email: 'user2@test.com' });
    
    user1Token = generateToken(user1);
    user2Token = generateToken(user2);
    
    // Criar pedidos
    user1Order = await Order.create({ userId: user1._id, total: 100 });
    user2Order = await Order.create({ userId: user2._id, total: 200 });
  });
  
  describe('GET /api/orders/:orderId', () => {
    it('should allow user to access their own order', async () => {
      const response = await request(app)
        .get(`/api/orders/${user1Order._id}`)
        .set('Authorization', `Bearer ${user1Token}`);
      
      expect(response.status).toBe(200);
      expect(response.body._id).toBe(user1Order._id.toString());
    });
    
    it('should NOT allow user to access another user order', async () => {
      const response = await request(app)
        .get(`/api/orders/${user2Order._id}`)
        .set('Authorization', `Bearer ${user1Token}`);
      
      expect(response.status).toBe(404);
      expect(response.body).not.toHaveProperty('userId');
    });
    
    it('should NOT leak information about order existence', async () => {
      const response = await request(app)
        .get(`/api/orders/${user2Order._id}`)
        .set('Authorization', `Bearer ${user1Token}`);
      
      // Não deve revelar se o pedido existe ou não
      expect(response.body.error).toBe('Order not found');
      // NÃO deve dizer "Order found but you don't have access"
    });
  });
  
  describe('PUT /api/orders/:orderId', () => {
    it('should allow user to update their own order', async () => {
      const response = await request(app)
        .put(`/api/orders/${user1Order._id}`)
        .set('Authorization', `Bearer ${user1Token}`)
        .send({ status: 'cancelled' });
      
      expect(response.status).toBe(200);
    });
    
    it('should NOT allow user to update another user order', async () => {
      const response = await request(app)
        .put(`/api/orders/${user2Order._id}`)
        .set('Authorization', `Bearer ${user1Token}`)
        .send({ status: 'cancelled' });
      
      expect(response.status).toBe(404);
      
      // Verificar que o pedido não foi modificado
      const unchangedOrder = await Order.findById(user2Order._id);
      expect(unchangedOrder.status).not.toBe('cancelled');
    });
  });
  
  describe('DELETE /api/orders/:orderId', () => {
    it('should NOT allow user to delete another user order', async () => {
      const response = await request(app)
        .delete(`/api/orders/${user2Order._id}`)
        .set('Authorization', `Bearer ${user1Token}`);
      
      expect(response.status).toBe(404);
      
      // Verificar que o pedido ainda existe
      const order = await Order.findById(user2Order._id);
      expect(order).toBeTruthy();
    });
  });
});
```

### 8. Auditar código existente

```javascript
// Script de auditoria de segurança
const fs = require('fs');
const path = require('path');

// Padrões vulneráveis comuns
const vulnerablePatterns = [
  // Busca por ID sem validação de usuário
  /findById\([^)]+\)(?!.*userId)/g,
  /findOne\({[^}]*_id[^}]*}[^}]*\)(?!.*userId)/g,
  
  // Parâmetros de rota sem autorização
  /app\.(get|put|post|delete)\(['"][^'"]*:.*Id['"].*\)(?!.*authorize)/g,
  
  // Query params usados diretamente
  /req\.query\.userId/g,
  /req\.params\.\w+Id.*findById/g
];

function auditFile(filePath) {
  const content = fs.readFileSync(filePath, 'utf8');
  const issues = [];
  
  vulnerablePatterns.forEach((pattern, index) => {
    const matches = content.match(pattern);
    if (matches) {
      issues.push({
        file: filePath,
        pattern: index,
        matches: matches,
        severity: 'HIGH'
      });
    }
  });
  
  return issues;
}

function auditDirectory(dir) {
  const files = fs.readdirSync(dir);
  const allIssues = [];
  
  files.forEach(file => {
    const filePath = path.join(dir, file);
    const stat = fs.statSync(filePath);
    
    if (stat.isDirectory()) {
      allIssues.push(...auditDirectory(filePath));
    } else if (file.endsWith('.js')) {
      allIssues.push(...auditFile(filePath));
    }
  });
  
  return allIssues;
}

// Executar auditoria
const issues = auditDirectory('./routes');
console.log(`Found ${issues.length} potential BOLA vulnerabilities`);
console.log(JSON.stringify(issues, null, 2));
```

## Checklist de segurança BOLA

**Para cada endpoint que acessa objetos específicos:**

* [ ] Autentica o usuário?
* [ ] Valida se o objeto pertence ao usuário autenticado?
* [ ] Usa o ID do usuário do token (não do cliente)?
* [ ] Trata casos de compartilhamento de recursos?
* [ ] Retorna 404 genérico (não vaza existência)?
* [ ] Implementa rate limiting?
* [ ] Logs tentativas de acesso não autorizado?
* [ ] Usa UUIDs ou identificadores não-sequenciais?
* [ ] Testa com múltiplos usuários?
* [ ] Valida objetos aninhados em cascata?

## Padrões de código seguro

### Pattern: Authorization Service

```javascript
// services/authorization.service.js
class AuthorizationService {
  
  // Verificar propriedade direta
  async canAccessResource(userId, resourceId, resourceType) {
    const Model = this.getModel(resourceType);
    const resource = await Model.findOne({
      _id: resourceId,
      $or: [
        { userId: userId },
        { ownerId: userId },
        { createdBy: userId }
      ]
    });
    
    return !!resource;
  }
  
  // Verificar acesso compartilhado
  async canAccessSharedResource(userId, resourceId, resourceType) {
    const Model = this.getModel(resourceType);
    const resource = await Model.findOne({
      _id: resourceId,
      $or: [
        { userId: userId },
        { sharedWith: { $in: [userId] } },
        { permissions: { userId: userId, level: { $gte: 'read' } } }
      ]
    });
    
    return !!resource;
  }
  
  // Verificar permissão específica
  async hasPermission(userId, resourceId, resourceType, permission) {
    const Model = this.getModel(resourceType);
    const resource = await Model.findOne({
      _id: resourceId,
      $or: [
        { userId: userId }, // Owner tem todas permissões
        { 
          permissions: { 
            $elemMatch: {
              userId: userId,
              actions: { $in: [permission, '*'] }
            }
          }
        }
      ]
    });
    
    return !!resource;
  }
  
  getModel(resourceType) {
    const models = {
      'order': Order,
      'document': Document,
      'invoice': Invoice,
      'file': File
    };
    
    return models[resourceType];
  }
}

module.exports = new AuthorizationService();

// Uso
const authService = require('./services/authorization.service');

app.get('/api/documents/:docId', authenticateUser, async (req, res) => {
  const canAccess = await authService.canAccessSharedResource(
    req.user.id,
    req.params.docId,
    'document'
  );
  
  if (!canAccess) {
    return res.status(404).json({ error: 'Document not found' });
  }
  
  const document = await Document.findById(req.params.docId);
  res.json(document);
});
```

### Pattern: Policy-Based Authorization

```javascript
// policies/OrderPolicy.js
class OrderPolicy {
  
  canView(user, order) {
    // Owner pode ver
    if (order.userId.equals(user._id)) {
      return true;
    }
    
    // Admin pode ver todos
    if (user.role === 'admin') {
      return true;
    }
    
    // Suporte pode ver se atribuído
    if (user.role === 'support' && order.assignedTo.equals(user._id)) {
      return true;
    }
    
    return false;
  }
}

module.exports = new OrderPolicy();
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://0xmorte.gitbook.io/bibliadopentestbr/tecnicas/web/back-end/apis/broken-object-level-authorization-bola.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
