CyberMed

Secure Coding Practices

Secure Development & Testing · 5 min read

Secure coding means writing software that holds up when someone feeds it hostile input: validating everything that comes in, managing memory carefully, using proven cryptography, and failing to a safe state when something goes wrong. Most medical device vulnerabilities trace back to a handful of well-known mistakes, the kind cataloged in the OWASP Top 10 and the CWE Top 25.

4.3.1 The Foundation: Understanding Common Vulnerabilities

Before you can write secure code, you need to understand what makes code vulnerable. The most common medical device software vulnerabilities include:

Buffer Overflows

  • Writing beyond allocated memory
  • Can crash devices or execute malicious code
  • Especially dangerous in C/C++ code

Injection Attacks

  • SQL injection in database queries
  • Command injection in system calls
  • Script injection in web interfaces

Authentication Weaknesses

  • Hardcoded passwords
  • Weak password requirements
  • Missing authentication

Insecure Communications

  • Unencrypted data transmission
  • Missing certificate validation
  • Protocol downgrade attacks

4.3.2 Input Validation: Your First Line of Defense

The golden rule: Never trust input. Whether it comes from users, other systems, or sensors, validate everything.

Validation Best Practices

Use Allowlists, Not Denylists

// Bad: Denying known bad inputs
if (input.contains("DROP TABLE") || input.contains("'; DELETE")) {
    reject();
}

// Good: Only allowing known good inputs
if (input.matches("^[0-9]{1,3}\\.[0-9]{1}$")) {  // e.g., "120.5"
    accept();
}

Validate Type, Range, and Format

  • Type: Is it a number, string, date?
  • Range: Is it within acceptable bounds?
  • Format: Does it match expected pattern?

Example for insulin pump dose validation:

boolean validateDose(double dose) {
    // Type check (handled by parameter type)
    
    // Range check
    if (dose < MIN_DOSE || dose > MAX_DOSE) {
        return false;
    }
    
    // Precision check
    if (dose * 100 != Math.floor(dose * 100)) {
        // More than 2 decimal places
        return false;
    }
    
    return true;
}

Sanitize for Context Different contexts need different sanitization:

  • Database: Parameterized queries
  • Display: HTML encoding
  • System calls: Command escaping
  • File paths: Path traversal prevention

4.3.3 Memory Management: Preventing Buffer Overflows

Buffer overflows remain a leading cause of security vulnerabilities, especially in medical devices using C/C++.

Safe Memory Practices

Use Memory-Safe Functions

// Dangerous functions to avoid
strcpy(dest, src);           // No bounds checking
sprintf(buffer, format, ...); // Can overflow
gets(buffer);                // No size limit

// Safer alternatives
strncpy(dest, src, SIZE);    // Size-limited
snprintf(buffer, SIZE, format, ...); // Bounded
fgets(buffer, SIZE, stdin);   // Size-limited

Check Array Bounds

// Bad: No bounds checking
void processData(int data[], int index) {
    int value = data[index];  // Could access beyond array
}

// Good: Validate index
void processData(int data[], int size, int index) {
    if (index >= 0 && index < size) {
        int value = data[index];
    } else {
        // Handle error safely
    }
}

Consider Memory-Safe Languages For new development, consider:

  • Rust: Memory safety without garbage collection
  • Java/C#: Automatic memory management
  • Go: Built-in bounds checking

But remember: even "safe" languages can have vulnerabilities!

4.3.4 Cryptography: Getting It Right

Cryptography is hard to get right. The JSP emphasizes: use proven libraries and current standards.

Cryptographic Best Practices

Use Established Libraries

  • OpenSSL (with careful configuration)
  • Bouncy Castle
  • Platform cryptographic APIs
  • FIPS 140-2 validated modules

Never Roll Your Own Crypto

// NEVER do this:
int myEncrypt(char* data) {
    for (int i = 0; i < strlen(data); i++) {
        data[i] = data[i] ^ 0x42;  // "encryption"
    }
}

// Use proven algorithms:
// AES-256 for symmetric encryption
// RSA-2048 or ECC-P256 for asymmetric
// SHA-256 or SHA-3 for hashing

Key Management Matters

  • Never hardcode keys
  • Use hardware security modules when possible
  • Implement key rotation
  • Protect keys at rest

Stay Current with Standards According to FIPS publications:

  • Use AES with 128-bit keys minimum
  • RSA keys should be 2048 bits minimum
  • Avoid MD5 and SHA-1 (broken)
  • Follow NIST recommendations

4.3.5 Error Handling: Failing Safely

How your device handles errors can make the difference between a minor issue and a security breach.

Secure Error Handling Principles

Don't Expose Sensitive Information

// Bad: Reveals system details
catch (SQLException e) {
    return "Database error: " + e.getMessage();
    // Might reveal: "Table 'patient_records' not found"
}

// Good: Generic error for users
catch (SQLException e) {
    logSecurely(e);  // Detailed info to secure log
    return "An error occurred. Please try again.";
}

Fail to a Secure State

  • If authentication fails, deny access
  • If encryption fails, don't send plaintext
  • If validation fails, reject input
  • If critical error, enter safe mode

Log Security Events What to log:

  • Authentication attempts (success and failure)
  • Authorization failures
  • Input validation failures
  • System errors
  • Configuration changes

What NOT to log:

  • Passwords (even encrypted)
  • Full credit card numbers
  • Detailed patient data
  • Encryption keys

4.3.6 Authentication and Session Management

Medical devices often have unique authentication challenges - they need to be secure but usable in emergency situations.

Authentication Best Practices

Multi-Factor When Appropriate

  • Something you know (password)
  • Something you have (badge, token)
  • Something you are (biometric)

But consider the clinical context:

// Example: Tiered authentication
if (operation == ROUTINE_CHECK) {
    require(password);
} else if (operation == CONFIGURATION_CHANGE) {
    require(password + badge);
} else if (operation == EMERGENCY_OVERRIDE) {
    require(biometric);  // Fast but secure
    audit(EMERGENCY_ACCESS);
}

Password Requirements That Make Sense FDA doesn't specify exact requirements, but industry best practices suggest:

  • Minimum 8 characters (12+ for privileged accounts)
  • Complexity requirements appropriate to risk
  • Regular rotation for high-privilege accounts
  • Account lockout after failed attempts
  • No default passwords

Session Management

  • Generate cryptographically random session IDs
  • Set appropriate timeouts
  • Invalidate sessions on logout
  • Protect session tokens in transit and storage

4.3.7 Secure Coding Standards

Different languages have different security considerations. Here are key standards:

For C/C++:

  • CERT C/C++ Secure Coding Standards
  • MISRA C (safety-critical systems)
  • CWE/SANS Top 25

For Java:

  • Oracle Secure Coding Guidelines
  • OWASP Java Security Cheat Sheet

For Web Technologies:

  • OWASP Top 10
  • OWASP ASVS (Application Security Verification Standard)

For Python:

  • Python Security Guidelines
  • Bandit security linter

For how to verify these practices through structured review, see How to perform security code review.

See how your device measures up

Take the free FDA 524B readiness assessment and get a personalized gap report covering this topic and more.

Check Your Readiness