Security Code Review Checklist

Imagine your market value drops by 80% within nine hours. That’s exactly what happened to Holograph. A recent data breach exposed a hidden weakness in Holograph’s smart contract code, a harsh reminder that cybercrime is booming. This is where a code security audit becomes non-negotiable.

Companies dealing with sensitive data have a responsibility to prioritize robust security measures. Whether you are an e-commerce giant, a government agency, or a financial service provider, this guide will equip you with the knowledge and tools you need to enhance your code security. Many regulations require strong data protection, and audits help you meet those rules.

This article provides a comprehensive checklist for conducting an in-depth code security audit. By following these steps, you can significantly improve your code security and keep your customers and investors’ trust unshattered.

Guide to Comprehensive Code Security Auditing

A code security audit is a deep dive into your app’s code, searching for hidden vulnerabilities. In high-risk industries, even a minor security flaw can have devastating consequences. A code security audit helps identify and address these issues before they become a major problem.

Security Code Review Checklist

Preliminary Checks

Before diving into the nitty-gritty of your code security audit, it’s crucial to understand the potential threats and vulnerabilities lurking within your app. These initial steps help set the stage for a thorough and effective review process.

Understand the Application Context:

  • Gather information about the application’s purpose, architecture, and technology stack
  • Identify key components and modules that require a security review
  • Understand the threat model, including potential attackers and their capabilities
  • Review any existing security documentation and previous security assessments
  • Identify regulatory and compliance requirements relevant to the application

Version Control and Change Management:

  • Ensure that the code is stored in a secure version control system such as Git
  • Review the commit history to understand recent changes and identify potential security issues
  • Verify that code changes are reviewed and approved through a formal process
  • Check for the presence of security-related commit messages and documentation
  • Identify any direct commits to sensitive areas of the codebase without peer review

Build and Deployment Processes:

  • Review the build and deployment processes for security best practices
  • Ensure that build scripts and configuration files are secure and not exposing sensitive information
  • Verify the use of automated security testing tools in the CI/CD pipeline
  • Check for secure handling of secrets and credentials during the build and deployment processes
  • Identify any manual steps in the deployment process that could introduce security risks

Team Collaboration and Communication:

  • Establish clear communication channels and protocols for the security review process
  • Ensure that all team members involved in the review are aware of their roles and responsibilities
  • Schedule regular meetings to discuss findings and coordinate remediation efforts
  • Provide training and resources to team members on secure coding practices and security review techniques

Code Quality and Vulnerability Checks

In this step, we’re all about ensuring the code is tip-top quality and weeding out any weak spots that could get hacked. We’ll be combing through the code to find common security problems and make sure everything is coded following best practices.

Code Readability and Maintainability:

  • Check for clear, concise, and self-explanatory code with appropriate comments
  • Ensure functions and methods are single-purpose and not overly complex
  • Verify consistent use of naming conventions and code formatting across the codebase
  • Identify and refactor the duplicated code to reduce the potential for errors
  • Ensure modular code structure for easier testing and maintenance
// Bad practice
public void ProcessOrder(Order order) {
    ValidateOrder(order);
    SaveOrder(order);
    SendConfirmationEmail(order);
}

// Good practice
public void ProcessOrder(Order order) {
    ValidateOrder(order);
    SaveOrder(order);
    NotifyCustomer(order);
}

private void NotifyCustomer(Order order) {
    SendConfirmationEmail(order);
    LogNotification(order);
}

Monolithic code that’s not divided into smaller parts can lead to code duplication and increases the risk of errors during changes. Refactor large blocks of code into smaller, reusable methods or classes

Static Analysis:

  • Use static analysis tools to scan the code for potential security vulnerabilities
  • Identify common coding issues such as buffer overflows, race conditions, and memory leaks
  • Ensure that the static analysis tools are configured to enforce coding standards and security guidelines
  • Review and address the findings from the static analysis reports
  • Integrate static analysis into the continuous integration (CI) pipeline for ongoing checks

Coding Standards and Best Practices:

  • Verify adherence to language-specific and industry-standard coding practices
  • Check for the use of secure coding libraries and frameworks
  • Ensure that the code adheres to the principle of least privilege
  • Validate that deprecated or insecure functions are not used
  • Promote the use of defensive coding techniques to anticipate and handle unexpected inputs or states
// Bad practice
public void ProcessInput(string input) {
    Console.WriteLine("Processing: " + input.Substring(5));
}

// Good practice
public void ProcessInput(string input) {
    if (string.IsNullOrEmpty(input) || input.Length < 5)
    {
        throw new ArgumentException("Input must be at least 5 characters long.");
    }
    Console.WriteLine("Processing: " + input.Substring(5));
}

Example of defensive coding that makes the code more secure but also easier to debug and maintain

Secure Use of Language Features:

  • Check for proper use of language-specific features, such as exception handling in Java or error checking in Go
  • Ensure that memory management is handled correctly to prevent leaks and corruption (relevant for languages like C/C++)
  • Verify the safe use of concurrent programming constructs to avoid race conditions and deadlocks
  • Ensure that type safety is enforced to prevent type-related vulnerabilities
  • Validate that input/output operations are handled securely to prevent injection attacks
// Bad practice
char* buffer = new char[100];
strcpy(buffer, "This is a buffer.");
// Missing delete[] buffer;

// Good practice
#include 
std::unique_ptr buffer(new char[100]);
strcpy(buffer.get(), "This is a buffer.");
// No need to manually delete, handled by unique_ptr

Example of bad vs. good memory management in C/C++. Memory issues can cause app crashes, performance degradation, and security vulnerabilities

Vulnerability Pattern Recognition:

  • Identify and address common vulnerability patterns, such as hard-coded credentials or secrets within the source code
  • Inspect for insecure use of random number generators (ensure cryptographic RNGs are used where necessary)
  • Ensure there are no unsanitized user inputs that are directly passed to database queries or command-line arguments
  • Document any instances of insecure communication channels without encryption
  • Watch for insufficient access controls within application logic
  • Document and share these patterns with the development team to raise awareness and prevent future occurrences
// Bad practice (SQL Injection risk)
string query = "SELECT * FROM Users WHERE Username = '" + username + "'";

// Good practice
string query = "SELECT * FROM Users WHERE Username = @username";
SqlCommand command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("@username", username);

Direct use of unsanitized inputs can lead to SQL injection, command injection, and other types of injection attacks

Code Complexity Analysis:

  • Use tools to measure code complexity metrics, such as cyclomatic complexity
  • Identify highly complex or “hotspot” areas of the code that are prone to errors and security issues
  • Simplify complex code paths to enhance readability and reduce the likelihood of bugs
  • Ensure that critical security logic is straightforward and easy to review
  • Refactor code where necessary to reduce complexity and improve clarity
// Bad practice
public void ProcessOrder(Order order) {
    ValidateOrder(order);
    if (order.IsValid) {
        SaveOrder(order);
        SendConfirmationEmail(order);
    }
}

// Good practice
public void ProcessOrder(Order order) {
    if (!ValidateOrder(order)) return;
    SaveOrder(order);
    SendConfirmationEmail(order);
}

private bool ValidateOrder(Order order) {
    // Validation logic
    return order.IsValid;
}

Refactor large methods into smaller ones, each performing a single responsibility, which improves testability, readability, and maintainability

Documentation and Commenting:

  • Ensure that security-relevant code is well-documented, explaining the rationale for security controls
  • Verify that comments do not expose sensitive information or security implementation details
  • Ensure that documentation includes guidelines for safe interaction with the code, for example, how to properly call a function that performs sensitive operations
  • Validate that all cryptographic implementations and key management processes are thoroughly documented
  • Ensure that inline comments are used judiciously to clarify complex code segments without cluttering the codebase

Input Validation

Proper input validation is critical to ensure that only valid and expected data is processed by the app. This helps prevent a variety of attacks, such as SQL injection, cross-site scripting (XSS), and buffer overflows. All inputs must be correctly validated and sanitized before being processed, and here’s how to check it.

Client-Side Validation:

  • Verify that client-side validation is implemented to provide immediate feedback to users
  • Ensure that client-side validation is not solely relied upon for security purposes
  • Check for the presence of consistent validation rules between the client-side and server-side

Server-Side Validation:

  • Ensure that all input is validated on the server side, regardless of client-side validation
  • Check if inputs are validated with allowlists
  • Check if deny lists could be implemented as a secondary measure
  • Ensure proper validation of all input types, including strings, numbers, dates, and files
// Bad practice
public bool ValidateUsername(string username) {
    // Checks only if input is not null or empty
    return !string.IsNullOrEmpty(username);
}

//Good practice
public bool ValidateUsername(string username) {
    // Allow only alphanumeric characters and underscores
    Regex allowedPattern = new Regex(@"^\w+$");
    return allowedPattern.IsMatch(username);
}

Using generic and overly permissive validation patterns vs. using allowlists to specify allowed characters or patterns explicitly

Input Sanitization:

  • Verify that HTML, JavaScript, and SQL inputs are properly sanitized to prevent injection attacks
  • Ensure specialized libraries or functions are used to handle common sanitization tasks
  • Ensure that sanitized inputs are used consistently throughout the application

Length and Format Checks:

  • Verify that input lengths are checked to prevent buffer overflows and resource exhaustion
  • Ensure that inputs conform to expected formats, such as email addresses, phone numbers, and URLs
  • Check if regular expressions are implemented for complex format validation, ensuring they are efficient and secure
// Bad practice
public IActionResult SubmitForm(string userInput) {
    // No length check
    Database.Save(userInput); // Risk of buffer overflow or inefficient database use
}

//Good practice
public IActionResult SubmitForm(string userInput) {
    if (userInput.Length > 100) {
        return BadRequest("Input too long.");
    }
    Database.Save(userInput); // Input length is controlled
}

Always check the length of inputs before processing them to ensure they meet your app’s requirements and prevent resource misuse

Boundary Value Analysis:

  • Test input validation against boundary values, such as maximum and minimum allowable values
  • Ensure that out-of-bound values are properly handled and do not cause application crashes
  • Validate numeric inputs to ensure they fall within acceptable ranges and are correctly typed (e.g., integers vs. floating-point numbers)
// Bad practice
public IActionResult CreateAccount(User user) {
    if (user.Age < 18) { // No upper boundary check
        return BadRequest("You must be at least 18 years old.");
    }
    UserRepository.Add(user);
}

//Good practice
public IActionResult CreateAccount(User user) {
    if (user.Age < 18 || user.Age > 99) {
        return BadRequest("Invalid age. Age must be between 18 and 99.");
    }
    UserRepository.Add(user);
}

Ignoring boundary values during validation can lead to unexpected behaviors or errors when extreme values are entered

Type Checking:

  • Ensure that inputs are checked for correct data types, such as strings, integers, booleans
  • Check if type-safe functions and libraries are used to enforce data type constraints
  • Validate JSON and XML inputs to ensure they conform to expected schemas and structures
// Bad practice
public void ProcessJson(string jsonString) {
    var data = JsonConvert.DeserializeObject(jsonString); // No validation against schema
    // Process data
}

// Good practice
public IActionResult ProcessJson(string jsonString) {
    JSchema schema = JSchema.Parse(File.ReadAllText("MyObjectSchema.json"));
    JObject jsonObject = JObject.Parse(jsonString);
    if (!jsonObject.IsValid(schema)) {
        return BadRequest("Invalid JSON format.");
    }
    var data = jsonObject.ToObject();
    // Process data
}

Accepting JSON or XML data without validating against a schema, which could lead to improper data structures passing through

Encoding:

  • Ensure that input data is properly encoded before being processed or stored
  • Check if appropriate encoding techniques are used for different contexts, such as URL encoding, HTML encoding, and base64 encoding
  • Verify that encoded data is correctly decoded and validated before further processing
// Bad practice
public string GetUserProfileHtml(string username) {
    return $"
Hello, {username}!
"; // Risk of XSS if username contains HTML/JS } // Good practice public string GetUserProfileHtml(string username) { return $"
Hello, {HttpUtility.HtmlEncode(username)}!
"; // Safely encode to prevent XSS }

Displaying unencoded user input in HTML, which can lead to Cross-Site Scripting (XSS) vulnerabilities

File Uploads:

  • Validate the file type, size, and content before accepting file uploads
  • Ensure that uploaded files are stored in a secure location with restricted access
  • Check if virus scanning and malware detection are implemented for uploaded files
  • Verify that file names and paths are sanitized to prevent directory traversal attacks

Third-Party Input:

  • Validate and sanitize input received from third-party sources, such as APIs and webhooks
  • Ensure that third-party data is treated with the same level of scrutiny as user input
  • Verify the integrity and authenticity of data received from external sources

Authentication

Authentication is crucial for verifying the user’s identity and ensuring that only legitimate users can access the system. Proper authentication keeps your info and whatever else is inside the system safe from prying eyes. A recent Snowflake data breach confirmed that users with single-factor authentication can be the doorway for hackers to compromise hundreds of other accounts.

Password Management:

  • Ensure passwords are stored using strong hashing algorithms like bcrypt, Argon2, or Scrypt
  • Verify the implementation of password policies, including minimum length, complexity, and expiration requirements
  • Check for secure password reset and recovery mechanisms that do not expose sensitive information
  • Ensure that users are prompted to change default passwords upon first login
// Bad practice
public bool IsValidPassword(string password) {
    return !string.IsNullOrEmpty(password); // Only checks if password is not empty
}

// Good practice
public bool IsValidPassword(string password) {
    var hasMinimum8Chars = password.Length >= 10;
    var hasUpperCase = password.Any(char.IsUpper);
    var hasLowerCase = password.Any(char.IsLower);
    var hasDecimalDigit = password.Any(char.IsDigit);
    var hasSpecialChar = password.Any(p => !char.IsLetterOrDigit(p));

    return hasMinimum8Chars && hasUpperCase && hasLowerCase && hasDecimalDigit && hasSpecialChar;
}

Implement and enforce password policies that require a minimum length and include complexity requirements

Multi-Factor Authentication (MFA):

  • Ensure MFA is implemented and enforced for critical user accounts and operations
  • Verify the use of secure second factors, such as hardware tokens, SMS codes, or authentication apps
  • Ensure MFA mechanisms are resilient to common attacks like phishing and man-in-the-middle
  • Validate the MFA implementation for various authentication flows, such as login and password reset

Secure Login Mechanisms:

  • Validate the implementation of secure login forms, ensuring data is transmitted over HTTPS
  • Ensure login attempts are rate-limited to prevent brute force attacks
  • Verify the secure handling of session tokens and cookies during the authentication process
  • Ensure that login error messages do not reveal whether the username or password was incorrect
// Bad practice
public IActionResult Login(string username, string password) {
    if (Authenticate(username, password)) {
        // User authenticated
    } else {
        // Authentication failed
    }
}

// Good practice
public IActionResult Login(string username, string password) {
    if (!IsAllowedToAttempt(username)) {
        return BadRequest("Too many failed login attempts. Please try again later.");
    }

    if (Authenticate(username, password)) {
        ResetAttemptCounter(username);
        // User authenticated
    } else {
        IncrementAttemptCounter(username);
        // Authentication failed
    }
}

Allowing unlimited login attempts without any checks vs. implementing rate-limiting to prevent brute force attacks

Token-Based Authentication:

  • Ensure the secure generation, storage, and validation of authentication tokens, such as JWT and OAuth
  • Verify that tokens are signed and encrypted using strong algorithms
  • Inspect if short-lived tokens and refresh tokens are implemented to minimize the impact of token theft
  • Ensure tokens are invalidated upon user logout or session expiration

Authorization

After the user is authenticated, authorization checks what they’re allowed to do. This part of the app makes sure only users with the right permissions can access specific things or take certain actions. When we review this, here’s what we look for.

Role-Based Access Control (RBAC):

  • Ensure user roles and permissions are clearly defined and implemented
  • Verify that access control checks are performed on the server side for every sensitive operation
  • Ensure users can only perform actions and access resources that match their roles
// Bad practice
public bool CanAccessResource(string role)
{
    if (role == "Admin" || role == "User") // Unclear distinction
        return true;
    return false;
}

// Good practice
public bool CanAccessResource(string role)
{
    if (role == "Admin")
        return true;
    if (role == "User")
        return false; // Specific access defined per role
}

Ambiguous roles definitions vs. clearly defined roles and permissions

Least Privilege Principle:

  • Verify that the application enforces the principle of least privilege, granting users the minimum permissions necessary
  • Ensure sensitive actions require additional authorization or confirmation
  • Check if the separation of duties is implemented to prevent conflicts of interest
  • Inquire if there’s a regular review and audit of user roles and permissions to identify excessive privileges

Access Control Lists (ACLs):

  • Ensure ACLs are implemented to control access to resources based on user roles and attributes
  • Verify the secure storage and handling of ACLs
  • Check for consistent enforcement of ACLs across the application
  • Inspect if ACLs are regularly updated to reflect changes in user roles and permissions

Attribute-Based Access Control (ABAC):

  • Verify the implementation of ABAC, ensuring access decisions are based on user attributes and environmental conditions
  • Ensure policies for ABAC are clearly defined and regularly reviewed
  • Validate ABAC rules are enforced consistently across the application
  • Ensure policies are flexible to accommodate changes in user roles and environmental conditions

Session Token Generation and Storage:

  • Check for the use of cryptographically secure random number generators for token creation
  • Verify that session tokens are of sufficient length and complexity to prevent guessing attacks
  • Ensure that session tokens are stored securely on the client side, using secure cookies with appropriate attributes, such as HttpOnly, Secure, SameSite
  • Verify that session tokens are not exposed in URLs or other insecure locations
// Bad practice
public string GenerateSimpleToken() {
    return Guid.NewGuid().ToString().Substring(0, 8);  // Too short and not complex enough
}

// Good practice
public string GenerateSecureToken() {
    using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider()) {
        byte[] tokenData = new byte[64]; // 512 bits
        rng.GetBytes(tokenData);
        return Convert.ToBase64String(tokenData);  // Long and complex
    }
}

Ensure session tokens are of sufficient length and complexity to prevent guessing attacks

Session Expiration and Invalidation:

  • Implement session expiration policies to limit the duration of sessions, reducing the risk of hijacking
  • Verify that sessions are invalidated upon user logout or inactivity
  • Ensure that session tokens are invalidated on password changes or other critical security events
  • Implement mechanisms to detect and terminate duplicate or concurrent sessions from the same user
  • Implement idle session timeouts to automatically log users out after a period of inactivity
  • Verify that session timeouts are appropriately configured to balance security and user convenience
// Bad practice
public void ChangePassword(string newPassword) {
    // Password is changed, but session remains valid
    UpdatePassword(newPassword);
}

// Good practice
public void Logout() {
    Session.Abandon();  // Invalidate the session upon logout
}

public void ChangePassword(string newPassword) {
    UpdatePassword(newPassword);
    Session.Abandon();  // Invalidate the session after a password change
}

Invalidate session tokens upon security-sensitive events to prevent session hijacking

Auditing and Logging:

  • Ensure authentication and authorization events are logged for monitoring and auditing purposes
  • Verify logs include details such as user ID, action performed, and timestamp
  • Ensure logs do not contain sensitive information and are stored securely

Security of Admin Functions:

  • Ensure administrative functions and interfaces are protected with strong authentication and authorization mechanisms
  • Verify that admin actions are logged and monitored for suspicious activity
  • Check if additional security measures, such as MFA and IP whitelisting, are implemented for admin accounts
  • Ensure administrative functions are segmented and only accessible to authorized users

Data Protection

Data protection ensures that sensitive information is handled securely throughout its lifecycle, from collection and storage to processing and disposal. Here is how you can safeguard your users’ data to prevent unauthorized access, breaches, and data leaks.

Data Encryption:

  • Ensure sensitive data is encrypted both at rest and in transit using strong encryption algorithms (AES-256, TLS 1.2/1.3) and communication protocols (HTTPS, SSH)
  • Verify that encryption keys are managed securely, with proper key rotation and storage practices
  • Ensure that only authorized personnel have access to encryption keys and other security credentials
  • Ensure that database fields containing sensitive information are encrypted
// Bad practice
string encryptionKey = "1234567890123456";  // Hard-coded encryption key

//Good practice
// Using Azure Key Vault for managing encryption keys
var keyVault = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(securityTokenService));
var secret = await keyVault.GetSecretAsync("https://myvault.vault.azure.net/secrets/mysecret/");

Use a secure key management system, like Azure Key Vault or AWS Key Management Service, to handle encryption keys

Data Masking and Anonymization:

  • Ensure sensitive data is masked or anonymized when displayed in user interfaces or logs
  • Implement data masking techniques for non-production environments to prevent exposure of real data
  • Verify the effectiveness of anonymization techniques to prevent re-identification of individuals
  • Ensure that sensitive data is not exposed in error messages or debugging logs

Secure Data Storage:

  • Verify that sensitive data is stored in secure, access-controlled environments
  • Ensure that databases and file systems are configured with appropriate security settings
  • Examine if backup and disaster recovery plans that include secure storage of backup data are implemented
  • Test backup and recovery processes to ensure data can be restored securely

Data Integrity:

  • Implement checks to ensure data integrity during storage and transmission
  • Verify the use of cryptographic hashes to detect data tampering
  • Ensure that data validation processes include integrity checks
  • Monitor and log data integrity issues for auditing and troubleshooting

Data Retention and Disposal:

  • Ensure compliance with data retention policies, storing data only as long as necessary
  • Check if secure data deletion techniques are in place to permanently remove sensitive data when no longer needed
  • Verify that data disposal processes are thorough and documented

Regulatory Compliance:

  • Ensure the application complies with relevant data protection regulations, such as GDPR, CCPA, and HIPAA
  • Verify the implementation of user consent mechanisms for data collection and processing
  • Ensure that users can exercise their data protection rights, such as access, correction, and deletion
  • Check if data protection practices and policies are documented to demonstrate compliance during audits

Monitoring and Logging:

  • Check if monitoring systems are implemented to detect unauthorized access to sensitive data
  • Review logs for signs of data breaches or other security incidents

Data Breach Response:

  • Ensure that a data breach response plan is in place and regularly tested
  • Verify that the plan includes procedures for identifying, containing, and mitigating data breaches

Third-Party Data Handling:

  • Ensure that third-party service providers comply with data protection requirements
  • Verify that data shared with third parties is encrypted and securely transmitted
  • Check if data sharing agreements that outline responsibilities and security measures are implemented

Error Handling

Proper error handling ensures that the application manages errors gracefully, maintains functionality, and does not expose sensitive information. Does your app have robust error handling mechanisms in place? It’s high time to find out!

Graceful Error Handling:

  • Ensure the application handles errors gracefully without exposing sensitive information
  • Verify user-friendly error messages are displayed, avoiding technical details that could aid attackers
  • Check that error handling mechanisms prevent application crashes and maintain functionality where possible

Input Validation Errors:

  • Validate that all input validation errors are handled appropriately, providing clear feedback to users
  • Ensure validation errors do not reveal sensitive information or application logic details
  • Check for consistent handling of validation errors across the application
// Bad practice
if (!UserExists(username)) {
    return Error("Username not found.");
}
if (!CorrectPassword(username, password)) {
    return Error("Incorrect password.");
}

// Good practice
if (!Authenticate(username, password)) {
    return Error("Invalid username or password.");
}

Use generic error messages that do not disclose which part of the authentication failed

Exception Handling:

  • Verify that all exceptions are caught and handled properly, preventing application crashes
  • Ensure try-catch blocks are used appropriately to manage exceptions without exposing details to the user
  • Check if global exception handlers are implemented to manage unexpected errors and log them securely

Resource Management:

  • Ensure resources such as database connections, files, and network sockets are properly closed and released after an error occurs
  • Verify that resource leaks are prevented by implementing robust error handling for resource management

Specific Error-Handling Scenarios:

  • Validate the application correctly handles specific error scenarios, such as network failures, database errors, and third-party service outages
  • Ensure fallback mechanisms are in place to maintain application functionality during these errors

Logging

Secure logging practices are crucial for monitoring the app’s operation, diagnosing issues, and supporting security investigations. Remember that logs must be detailed, secure, and compliant with regulations.

Log Sensitivity:

  • Ensure sensitive information, such as passwords, API keys, and personal data, is never logged
  • Verify logs are sanitized to remove or mask any sensitive information before storage
  • Check if logging policies that define what information can and cannot be logged are in place

Log Completeness and Detail:

  • Ensure logs capture sufficient detail to diagnose issues and trace activities
  • Verify logs include critical information such as timestamps, user IDs, IP addresses, and error details
  • Ensure log entries are consistent and structured for easy analysis
// Bad practice
Log.Error("Operation failed"); // Bad: No context or details provided

// Good practice
Log.Error($"Operation failed at {DateTime.UtcNow} by UserID {userID}. Error: {errorDetail}");

Logs that lack necessary details make troubleshooting difficult

Log Storage Security:

  • Verify logs are stored in a secure location with restricted access
  • Ensure logs are protected from tampering by implementing integrity checks or cryptographic protections
  • Inspect if retention policies are implemented to securely archive or delete logs after a specified period
// Bad practice
var logFilePath = @"C:\SharedFolder\applog.txt";

// Good practice
var logFilePath = @"C:\SecureLogs\applog.txt";
FileSystemAccessRule rule = new FileSystemAccessRule("LogAccessGroup", FileSystemRights.ReadData, AccessControlType.Allow);

Storing logs in a shared folder vs. storing logs in a secure location with restricted access based on roles

Log Monitoring and Alerting:

  • Ensure logging mechanisms include real-time monitoring to detect and respond to security events
  • Verify alerts are configured for critical errors, security incidents, and suspicious activities
  • Check if monitoring tools are implemented to analyze log data and generate actionable insights

Log Rotation and Maintenance:

  • Check if log rotation policies are implemented to manage log file sizes and maintain performance
  • Ensure old logs are archived securely and can be retrieved for analysis if needed
  • Verify log rotation and archival processes do not disrupt logging functionality

Third-Party Libraries and Dependencies

To build features faster, developers often use third-party libraries. But just as a faulty brick weakens a wall, a vulnerable library or an insecure integration can expose your app. A case in point is Cylance whose data was stolen from a third-party platform. That’s why external components should be properly maintained to minimize such consequences.

Dependency Management:

  • Ensure that all third-party libraries and dependencies are managed using a package manager, such as npm, pip, or Maven
  • Verify that a clear policy is in place for adding new dependencies, including security assessments and approvals
  • Check for the presence of a dependency manifest file (e.g., package.json, requirements.txt) that lists all dependencies and their versions

Version Control and Updates:

  • Check if third-party libraries are updated to the latest secure versions
  • Verify that dependency updates are tested in a staging environment before being deployed to production
  • Monitor security advisories and vulnerability databases (e.g., CVE, NVD) for updates on used dependencies
  • Use automated vulnerability scanning tools, such as Snyk, Dependabot, OWASP Dependency-Check, to detect known vulnerabilities in third-party libraries
  • Check the reputation and maintenance activity of the library’s authors and contributors

License Compliance:

  • Ensure that all third-party libraries comply with the organization’s license policies
  • Verify that licenses for all dependencies are documented and reviewed for legal compliance
  • Watch for libraries with restrictive or incompatible licenses that could lead to legal issues

Isolation and Sandboxing:

  • Check if third-party libraries are isolated in a controlled environment to limit their impact on the app
  • Verify if sandboxing techniques are implemented to run third-party code with restricted permissions
  • Ensure that third-party components do not have unnecessary access to sensitive data or system resources

Code Review and Auditing:

  • Review the source code of critical third-party libraries to identify potential security issues
  • Ensure that third-party libraries undergo regular security audits by their maintainers or third-party auditors
  • Verify that libraries have a transparent process for reporting and addressing security vulnerabilities

Configuration and Customization:

  • Ensure that third-party libraries are securely configured according to best practices
  • If applicable, inspect all customizations and review them for security implications

Monitoring and Alerting:

  • Check if monitoring tools are implemented to track the performance and behavior of third-party libraries in production
  • Check if alerts are set up for abnormal activities or performance issues related to third-party components
  • Review logs and metrics to identify potential security incidents involving third-party libraries

Code Security Audit: What to Expect From This Service

Your product needs a thorough code security audit. You’re comparing vendors, but maybe wondering – what exactly will I get? How do I use the results? We can help! Here at Redwerk, we break down your code audit findings into clear, actionable steps.

1. Easy-to-Understand Report

We provide you with a list of issues with explanations and why they matter. The report is well structured and divided into Architecture Review, Database Review, Code Quality, Test Coverage, and Security Review.

This report becomes your roadmap for fixing problems and improving your product. For instance, we helped Complete Network identify a critical security issue associated with storing sensitive data in a publicly available folder. We also shared three simple ways to improve the app’s performance along with other code quality improvements, which resulted in 80% higher maintainability.

2. Expert Recommendations

We don’t just point out problems; we offer solutions. Our report includes recommendations on how to fix each issue and improve your code’s overall quality. Plus, we categorize vulnerabilities based on severity, so you know what to tackle first.

For example, when reviewing a network mapping app, we identified that the code quality was the main culprit. The architecture was solid, and there were no security issues. However, the backend code was written in a way that would make future development and onboarding of new developers expensive.

3. Fix It Yourself or Let Us Help

You’re in control! Use our report with any developer – your internal team, freelancers, or whoever you trust. But if you’d like a helping hand, we can fix the issues ourselves. Our report even includes an estimate of the time needed to address all the bugs.

Stop code problems before they start. Reach out now to get a clear picture of your app’s health!

See how we audited Complete Network's quote management software Project Science, achieving an 80% increase in code maintainability

Please enter your business email isn′t a business email