All skills
Skillintermediate

Constant-Time Analysis: PHP

Analysis guidance for PHP scripts. Uses the VLD extension or OPcache debug output to analyze Zend opcodes.

Claude Code Knowledge Pack7/10/2026

Overview

Constant-Time Analysis: PHP

Analysis guidance for PHP scripts. Uses the VLD extension or OPcache debug output to analyze Zend opcodes.

Prerequisites

Installing VLD Extension

The VLD (Vulcan Logic Dumper) extension is required for detailed opcode analysis. OPcache fallback is available but provides less detail.

Option 1: PECL Install (Recommended)

# Query latest version from PECL
VLD_VERSION=$(curl -s https://pecl.php.net/package/vld | grep -oP 'vld-\\K[0-9.]+(?=\\.tgz)' | head -1)
echo "Latest VLD version: $VLD_VERSION"

# Install via PECL channel URL (avoids version detection issues)
pecl install channel://pecl.php.net/vld-${VLD_VERSION}

# Or if above fails, install with explicit channel:
pecl install https://pecl.php.net/get/vld-${VLD_VERSION}.tgz

Option 2: Build from Source

# Clone and build from GitHub
git clone https://github.com/derickr/vld.git
cd vld
phpize
./configure
make
sudo make install

# Add to php.ini
echo "extension=vld.so" | sudo tee -a $(php --ini | grep "Loaded Configuration" | cut -d: -f2 | tr -d ' ')

Verify Installation

php -m | grep -i vld
# Should output: vld

macOS with Homebrew PHP

# Homebrew PHP may need manual extension directory setup
PHP_EXT_DIR=$(php -i | grep extension_dir | awk '{print $3}')
echo "PHP extension directory: $PHP_EXT_DIR"

# After building VLD, copy the extension
sudo cp modules/vld.so "$PHP_EXT_DIR/"

Running the Analyzer

# Analyze PHP file
uv run {baseDir}/ct_analyzer/analyzer.py crypto.php

# Include warning-level violations
uv run {baseDir}/ct_analyzer/analyzer.py --warnings crypto.php

# Filter to specific functions
uv run {baseDir}/ct_analyzer/analyzer.py --func 'encrypt|decrypt' crypto.php

# JSON output for CI
uv run {baseDir}/ct_analyzer/analyzer.py --json crypto.php

Dangerous Operations

Opcodes (Errors)

OpcodeIssue
DIVVariable-time execution based on operand values
MODVariable-time execution based on operand values
POWVariable-time execution

Functions (Errors)

FunctionIssueSafe Alternative
chr()Table lookup indexed by secret datapack('C', $int)
ord()Table lookup indexed by secret dataunpack('C', $char)[1]
bin2hex()Table lookups indexed on secret dataCustom constant-time implementation
hex2bin()Table lookups indexed on secret dataCustom constant-time implementation
base64_encode()Table lookups indexed on secret dataCustom constant-time implementation
base64_decode()Table lookups indexed on secret dataCustom constant-time implementation
rand()Predictablerandom_int()
mt_rand()Predictablerandom_int()
array_rand()Uses mt_rand internallyrandom_int()
uniqid()Predictablerandom_bytes()
shuffle()Uses mt_rand internallyFisher-Yates with random_int()

Functions (Warnings)

FunctionIssueSafe Alternative
strcmp()Variable-timehash_equals()
strcasecmp()Variable-timehash_equals()
strncmp()Variable-timehash_equals()
substr_compare()Variable-timehash_equals()
serialize()Variable-length outputFixed-length output
json_encode()Variable-length outputFixed-length output

Safe Patterns

String Comparison

// VULNERABLE: Early exit on mismatch
if ($user_token === $stored_token) { ... }

// SAFE: Constant-time comparison
if (hash_equals($stored_token, $user_token)) { ... }

Random Number Generation

// VULNERABLE: Predictable
$token = bin2hex(random_bytes(16));  // OK - random_bytes is secure
$index = mt_rand(0, count($array) - 1);  // VULNERABLE

// SAFE: Cryptographically secure
$token = bin2hex(random_bytes(16));
$index = random_int(0, count($array) - 1);

Character Operations

// VULNERABLE: Table lookup timing
$byte = ord($secret_char);
$char = chr($secret_byte);

// SAFE: No table lookup
$byte = unpack('C', $secret_char)[1];
$char = pack('C', $secret_byte);

Troubleshooting

VLD Not Loading

# Check if extension is enabled
php -i | grep vld

# Check for loading errors
php -d display_errors=1 -d vld.active=1 -r "echo 'test';" 2>&1

# Common issue: wrong extension directory
php -i | grep extension_dir
ls $(php -r "echo ini_get('extension_dir');") | grep vld

OPcache Fallback

If VLD is unavailable, the analyzer falls back to OPcache debug output:

# Manually test OPcache output
php -d opcache.enable_cli=1 -d opcache.opt_debug_level=0x10000 crypto.php 2>&1

OPcache provides less detailed output than VLD but still detects division/modulo opcodes.