HIGH insecure direct object referencehmac signatures

Insecure Direct Object Reference with Hmac Signatures

How Insecure Direct Object Reference Manifests in Hmac Signatures

HMAC signatures are designed to authenticate and verify the integrity of API requests, but they can inadvertently enable Insecure Direct Object Reference (IDOR) vulnerabilities when improperly implemented. The core issue arises when HMAC-signed requests contain object identifiers that are predictable or sequentially generated, allowing attackers to modify these identifiers and access unauthorized resources.

A common HMAC IDOR pattern occurs in REST APIs where resource IDs are embedded in signed URLs or headers. Consider a banking API that signs transaction requests:

const hmac = crypto.createHmac('sha256', secretKey);
hmac.update('transaction/' + transactionId);
const signature = hmac.digest('hex');

An attacker who observes a legitimate request for transaction/1001 can simply increment to transaction/1002 and modify the HMAC signature accordingly. Since the signature only validates the message format and not the user's authorization to access that specific resource, the request will be accepted by the server.

This vulnerability becomes more severe when HMAC signatures are used to sign entire query parameters or JSON bodies containing object references. For example:

// Vulnerable pattern - signing entire payload
const payload = JSON.stringify({
  userId: 123,
  documentId: 456
});
const signature = crypto.createHmac('sha256', secretKey)
  .update(payload)
  .digest('hex');

// Attacker modifies documentId to 457 and recalculates signature

The fundamental problem is that HMAC provides message authentication but not authorization. The signature guarantees that the message hasn't been tampered with, but it doesn't verify whether the requester has permission to access the specific resource referenced in the message.

Another HMAC IDOR variant involves predictable nonce or timestamp values. When APIs use sequential nonces or timestamps as part of the signed message, attackers can guess valid values and craft requests for unauthorized resources:

// Predictable nonce pattern
const nonce = Date.now(); // Easily guessable
const message = 'resource/' + resourceId + '/' + nonce;
const signature = crypto.createHmac('sha256', secretKey)
  .update(message)
  .digest('hex');

The server validates the signature and nonce but fails to check if the user owns or has access to the requested resource.

HMAC Signatures-Specific Detection

Detecting HMAC-based IDOR vulnerabilities requires both static analysis and dynamic testing approaches. The most effective detection combines automated scanning with manual review of HMAC implementation patterns.

Static analysis should focus on identifying HMAC signing code that incorporates predictable identifiers. Look for patterns where HMAC signatures are created using:

// Vulnerable patterns to flag
hmac.update(userId);
hmac.update(resourceId);
hmac.update(transactionId);
hmac.update(timestamp);

Tools like middleBrick can automatically scan HMAC implementations by analyzing the API's attack surface. The scanner examines request patterns, identifies HMAC signature usage, and tests for IDOR vulnerabilities by systematically modifying signed object identifiers and verifying if unauthorized access is granted.

Dynamic testing for HMAC IDOR involves creating test requests with modified object identifiers while maintaining valid signatures. This requires understanding the HMAC algorithm and key usage. A basic test script might look like:

async function testHmacIdor(baseUrl, originalRequest) {
  // Extract HMAC parameters from original request
  const { url, headers, body } = originalRequest;
  const originalSignature = headers['x-hmac-signature'];
  
  // Parse the signed message to identify object identifiers
  const urlParts = url.split('/');
  const resourceId = parseInt(urlParts[urlParts.length - 1]);
  
  // Test adjacent resource IDs
  for (let offset = 1; offset <= 10; offset++) {
    const testId = resourceId + offset;
    const testUrl = url.replace(resourceId.toString(), testId.toString());
    
    // Create new request with modified ID
    const testRequest = { ...originalRequest, url: testUrl };
    
    // Send test request and check response
    const response = await fetch(testUrl, {
      method: 'GET',
      headers: headers
    });
    
    if (response.status === 200) {
      console.log(`IDOR found: accessed resource ${testId}`);
    }
  }
}

middleBrick's black-box scanning approach is particularly effective for HMAC IDOR detection because it doesn't require source code access. The scanner sends modified requests to the API endpoint, observes responses, and identifies when unauthorized resource access is granted despite valid HMAC signatures.

During scanning, middleBrick tests 12 security categories including BOLA/IDOR, attempting to access resources by modifying signed identifiers. The scanner maintains a database of observed HMAC patterns and systematically tests variations to identify authorization bypasses.

HMAC Signatures-Specific Remediation

Remediating HMAC-based IDOR vulnerabilities requires implementing proper authorization checks alongside signature validation. The most effective approach combines HMAC authentication with resource-level authorization verification.

The fundamental fix is to validate that the user has permission to access the specific resource identified in the HMAC-signed request. This requires implementing authorization logic that runs after HMAC verification:

async function handleRequest(req, res) {
  // Step 1: Verify HMAC signature
  const isValidSignature = verifyHmacSignature(req);
  if (!isValidSignature) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
  
  // Step 2: Extract resource identifiers from request
  const resourceId = extractResourceId(req);
  const userId = extractUserId(req);
  
  // Step 3: Authorize resource access
  const hasAccess = await authorizeResourceAccess(userId, resourceId);
  if (!hasAccess) {
    return res.status(403).json({ error: 'Access denied' });
  }
  
  // Step 4: Process request
  return res.json(await processRequest(req));
}

async function authorizeResourceAccess(userId, resourceId) {
  // Check if user owns or has access to the resource
  const resource = await db.collection('resources')
    .findOne({ _id: resourceId, ownerId: userId });
    
  return resource !== null;
}

For APIs that use HMAC to sign entire payloads containing multiple object references, implement granular authorization checks for each referenced resource:

async function processSignedRequest(body, signature) {
  // Verify HMAC signature first
  if (!verifyHmac(body, signature)) {
    throw new Error('Invalid signature');
  }
  
  // Extract and validate all object references
  const { userId, documentIds } = body;
  
  // Check user authorization
  if (!await userExists(userId)) {
    throw new Error('Invalid user');
  }
  
  // Validate each document access
  for (const docId of documentIds) {
    if (!await canAccessDocument(userId, docId)) {
      throw new Error(`Access denied to document ${docId}`);
    }
  }
  
  // Process authorized request
  return await processDocuments(documentIds);
}

Another effective remediation strategy is to include user-specific context in the HMAC message itself. This prevents attackers from simply modifying resource IDs and recalculating signatures:

// Include user ID and resource ownership in signed message
function createSecureHmac(userId, resourceId, secretKey) {
  // Verify resource ownership before signing
  if (!resourceBelongsToUser(userId, resourceId)) {
    throw new Error('Resource does not belong to user');
  }
  
  const message = `${userId}:${resourceId}:${Date.now()}`;
  const signature = crypto.createHmac('sha256', secretKey)
    .update(message)
    .digest('hex');
    
  return { message, signature };
}

// Server-side verification includes ownership check
function verifySecureHmac(message, signature, secretKey) {
  const [userId, resourceId] = message.split(':');
  
  // Verify both signature and resource ownership
  const isValidSignature = crypto.createHmac('sha256', secretKey)
    .update(message)
    .digest('hex') === signature;
    
  const isAuthorized = resourceBelongsToUser(userId, resourceId);
  
  return isValidSignature && isAuthorized;
}

For APIs using predictable nonces or timestamps in HMAC signatures, implement nonce validation that checks for sequential patterns and limits the valid range:

function validateNonce(nonce, userId) {
  const now = Date.now();
  const nonceTime = parseInt(nonce);
  
  // Check if nonce is within acceptable time window
  const isRecent = Math.abs(now - nonceTime) < 5 * 60 * 1000; // 5 minutes
  
  // Check if user has already used this nonce
  const isDuplicate = nonceCache.has(`${userId}:${nonce}`);
  
  if (!isRecent || isDuplicate) {
    return false;
  }
  
  // Store nonce to prevent reuse
  nonceCache.set(`${userId}:${nonce}`, true);
  setTimeout(() => nonceCache.delete(`${userId}:${nonce}`), 5 * 60 * 1000);
  
  return true;
}

Related CWEs: bolaAuthorization

CWE IDNameSeverity
CWE-250Execution with Unnecessary Privileges HIGH
CWE-639Insecure Direct Object Reference CRITICAL
CWE-732Incorrect Permission Assignment HIGH

Frequently Asked Questions

How does HMAC signature IDOR differ from regular API IDOR?
HMAC signature IDOR is more subtle because the request appears properly authenticated - the signature validates correctly. The vulnerability lies in the authorization layer, not authentication. Regular IDOR might involve missing authentication entirely, while HMAC IDOR involves valid authentication but improper authorization checks after signature verification.
Can middleBrick detect HMAC-based IDOR vulnerabilities without access to source code?
Yes, middleBrick's black-box scanning approach can detect HMAC IDOR by analyzing request patterns, identifying HMAC usage, and systematically testing modified object identifiers. The scanner sends requests with adjusted resource IDs while maintaining valid signatures, then observes if unauthorized access is granted based on response patterns and status codes.