PHP

A1- Injections

SQL Injection

  • If the Input is specific Datatype (int,float,..):
    • Typecast the value to the corresponding datatype using functions like int(),float()
    • Validate the variable with corresponding Datatype using is_numeric(),is_float() etc.
          if (is_numeric($id) == True)
          {
              <code>
          }
      
  • Implement PHP Data Objects(PDO)
    • for example
      $stmt = $conn->prepare("INSERT INTO tbl VALUES(:id, :name)");
      $stmt->bindValue(':id', $id);
      $stmt->bindValue(':name', $name);
      $stmt->execute();
      
  • Use Mysqli Prepared Statement
    $stmt = $mysqli->prepare("INSERT INTO table (column1, column2) VALUES (?,?)");
    $stmt->bind_param("is", $integer, $string);
    $stmt->execute();
    
  • Escape Quotes using mysqli_real_escape_string() [for php7] or mysql_real_escape string() [for php5] and put the variable inside the quotes in the query.
  • Disable all the SQL Error in the application.
    • Append the following php code in the Database config file.
       error_reporting(0);
      

HTML Injection

  • Html encode all the special characters using htmlspecialchars() or htmlentities() [if its inside html tags]

      <?php echo htmlspecialchars($string, ENT_QUOTES, 'UTF-8'); ?>
    
     <?php echo htmlentities($string, ENT_QUOTES, 'UTF-8');?> 
    
  • If the Input is specific Datatype (int,float,..):

    • Typecast the value to the corresponding datatype using functions like int(),float()
    • Validate the variable with corresponding Datatype using is_numeric(),is_float() etc.
        if (is_numeric($id) == True)
        {
            <code>
        }

LDAP Injection

  • The special characters ' ‘, ‘#’, ‘"’, ‘+’, ‘,’, ‘;’, ‘<’, ‘>’, ‘' and null must be escaped.
  • Use Frameworks that Automatically Protect from LDAP Injection
  • Minimize the privileges assigned to the LDAP binding account in your environment.
  • Input validation can be used to detect unauthorized input before it is passed to the LDAP query.

Noncompliant Code

$user = $_GET["user"];
$pass = $_GET["pass"];

$filter = "(&(uid=" . $user . ")(userPassword=" . $pass . "))"; // Unsafe

$ds = ...
$basedn = "o=My Company, c=US";

$sr = ldap_list($ds, $basedn, $filter); // Noncompliant

Compliant Code

function sanitize_ldap_criteria($val) {
  $val = str_replace(['\\', '*', '(', ')'], ['\5c', '\2a', '\28', '\29'], $val);
  for ($i = 0; $i<strlen($val); $i++) {
    $char = substr($val, $i, 1);
    if (ord($char)<32) {
      $hex = dechex(ord($char));
      if (strlen($hex) == 1) $hex = '0' . $hex;
      $val = str_replace($char, '\\' . $hex, $val);
    }
  }
  return $val;
}

$user = sanitize_ldap_criteria( $_GET["user"] );
$pass = sanitize_ldap_criteria( $_GET["pass"] );

$filter = "(&(uid=" . $user . ")(userPassword=" . $pass . "))"; // Safe

$ds = ...
$basedn = "o=My Company, c=US";

$sr = ldap_list($ds, $basedn, $filter);

OS Command Injection

  • Avoid calling OS commands directly
  • Escape values added to OS commands specific to each OS
  • Sanitized untrusted user input (& | ; $ > < ` \ !) properly
  • Parameterization in conjunction with Input Validation
  • Use structured mechanisms that automatically enforce the separation between data and command.
  • Use escapeshellarg() or escapeshellcmd() rather than exec(), system(), passthru().

Noncompliant Code

	$binary = $_GET["binary"];

	// If the value "/sbin/shutdown" is passed as binary and the web server is running as root,
	// then the machine running the web server will be shut down and become unavailable for future requests

	exec( $binary ); // Noncompliant

Compliant Code

	$binary = $_GET["binary"];

	// Restrict to binaries within the current working directory whose name only contains letters
	$pattern = "[a-zA-Z]++";
	if ( preg_match($pattern, $binary) ) {
		exec( $binary ); // Compliant
	}

XPath Injection

  • Treat all user input as untrusted, and perform appropriate sanitization.
  • When sanitizing user input, verify the correctness of the data type, length, format, and content. For example, use a regular expression that checks for XML tags and special characters in user input. This practice corresponds to input sanitization.
  • In a client-server application, perform validation at both the client and the server sides.
  • Extensively test applications that supply, propagate, or accept user input.

Noncompliant Code

$user = $_GET["user"];
$pass = $_GET["pass"];

$doc = new DOMDocument();
$doc->load("test.xml");
$xpath = new DOMXPath($doc);

$expression = "/users/user[@name='" . $user . "' and @pass='" . $pass . "']";
$xpath->evaluate($expression); // Noncompliant

Compliant Code

$user = $_GET["user"];
$pass = $_GET["pass"];

$doc = new DOMDocument();
$doc->load("test.xml");
$xpath = new DOMXPath($doc);

$user = str_replace("'", "&apos;", $user);
$pass = str_replace("'", "&apos;", $pass);

$expression = "/users/user[@name='" . $user . "' and @pass='" . $pass . "']";
$xpath->evaluate($expression); // Compliant

A2- Broken Authentication & Session Management

Broken Authentication:

  • Where possible, implement multi-factor authentication to prevent automated, credential stuffing, brute force, and stolen credential re-use attacks.

  • Ensure registration, credential recovery, and API pathways are hardened against account enumeration attacks by using the same messages for all outcomes.

  • Limit or increasingly delay failed login attempts. Log all failures and alert administrators when credential stuffing, brute force, or other attacks are detected.

  • Minimum passwords length should be at least eight (8) characters long. Combining this length with complexity makes a password difficult to guess and/or brute force.

  • Do not ship or deploy with any default credentials, particularly for admin users.

  • The application may should different HTTP Error code depending on the authentication attempt response.

  • Use a side-channel to communicate the method to reset their password.

  • Ensure that generated tokens or codes in forgot/reset password are:

    • Randomly genererated using a cryptographically safe algorithm.
    • Sufficiently long to protect against brute-force attacks.
    • Stored securely.
    • Single use and expire after an appropriate period.
  • A stronger password can be enforced using the regex below, which requires at least 8 character password with numbers and both lowercase and uppercase letters.

var PASS_RE =/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}$/;

Session Management

  • Use a server-side, secure, built-in session manager that generates a new random session ID with high entropy after login. Use session_regenerate_id(true) to generate new session ID after authentication.
  • Session IDs should not be exposed in the URL.
  • Session IDs should timeout. User sessions or authentication tokens should get properly invalidated during logout.
  • If a cookie is used to propagate the session ID (default behavior), then the session cookie must be deleted. setcookie() may be used for that.
  • Passwords, session IDs, and other credentials should not be sent over unencrypted connections.
  • Session IDs should never be under the control of users and clients to create. They should all be generated, controlled, and secured centrally by the authentication and authorisation mechanism.
  • Session IDs should not be stored between browser sessions. They should be destroyed when a browser is closed. This prevents an attacker retrieving a valid session by using the history in a Browser or by finding the Session ID on the client’s local storage.

A3- Sensitive Data Exposure

  • Classify data processed, stored or transmitted by an application. Identify which data is sensitive according to privacy laws, regulatory requirements, or business needs.
  • Apply controls as per the classification.
  • Don’t store sensitive data unnecessarily. Discard it as soon as possible or use PCI DSS compliant tokenization or even truncation. Data that is not retained cannot be stolen.
  • Ensure up-to-date and strong standard algorithms, protocols, and keys are in place; use proper key management.
  • Encrypt all data in transit with secure protocols such as TLS with perfect forward secrecy (PFS) ciphers, cipher prioritization by the server, and secure parameters. Enforce encryption using directives like HTTP Strict Transport Security (HSTS).
  • Disable caching for response that contain sensitive data.
  • Store passwords using strong adaptive and salted hashing functions with a work factor (delay factor), such as Argon2, scrypt, bcrypt or PBKDF2.
  • Verify independently the effectiveness of configuration and settings.
  • Make sure to encrypt all sensitive data at rest.
  • Store all the passwords in Salted Hash format. SHA-256 hashing is preferred
  • Remove Sensitive tokens disclosed in Source Code or Git Report.
  • Remove Storing of Sensitive information in Public accessible Directory.

A4- XML External Entities (XXE)

  • Disable XML external entity and DTD processing in all XML parsers in the application.
  • Implement positive (“whitelisting”) server-side input validation, filtering, or sanitization to prevent hostile data within XML documents, headers, or nodes.
  • Verify that XML or XSL file upload functionality validates incoming XML using XSD validation or similar.
  • Patch or upgrade all XML processors and libraries in use by the application or on the underlying operating system
  • Whenever possible, use less complex data formats such as JSON, and avoiding serialization of sensitive data.
  • completely disable Document Type Definitions (DTDs), if its not needed.
    • The following should be set when using the default PHP XML parser:
    libxml_disable_entity_loader(true);

A5- Broken Access Control

  • Model access controls should enforce record ownership, rather than accepting that the user can create, read, update, or delete any record.
  • Unique application business limit requirements should be enforced by domain models.
  • Disable web server directory listing and ensure file metadata (e.g. .git) and backup files are not present within web roots.
  • Log access control failures, alert admins when appropriate (e.g. repeated failures).
  • Rate limit API and controller access to minimize the harm from automated attack tooling.
  • JWT tokens should be invalidated on the server after logout.
  • When a user changes their role to another one, the administrator must make sure that the earlier access is revoked such that at any given point of time, a user is assigned to only those roles on a need to know basis.
  • Access decisions should be made by checking if the current user has the permission associated with the requested application action.
  • The authentication mechanism should deny all access by default, and provide access to specific roles for every function.
  • In a workflow based application, verify the users’ state before allowing them to access any resources.
  • Developers should use only one user or session for indirect object references.
  • It is also recommended to check the access before using a direct object reference from an untrusted source.

A6- Security Misconfiguration

  • A minimal platform without any unnecessary features, components, documentation, and samples. Remove or do not install unused features and frameworks.
  • A task to review and update the configurations appropriate to all security notes, updates and patches as part of the patch management process.
  • An automated process to verify the effectiveness of the configurations and settings in all environments.
  • Ensure that a strong application architecture is being adopted that provides effective, secure separation between components.
  • Review default in HTTP Response headers to prevent internal implementation disclosure.
  • Use generic session cookie names
  • To disable the php version in the HTTP Response Headers set the expose_php to Off 'expose_php = Off' in php.ini file. This will eliminate the X-Powered-By php sent from the HTTP Response Header.
  • To disable server info in HTTP response header, add the following lines in apache2.conf
ServerTokens ProductOnly

ServerSignature Off
  • Put error messages in your server’s error log instead of displaying them to a user with these configuration directives:
log_errors = On
display_errors = Off
  • Set security specific HTTP headers. A sample Apache setting in .htaccess:
<IfModule mod_headers.c>
    ## CSP
    Header set Content-Security-Policy: [values];

    ## General Security Headers
    Header set X-XSS-Protection: 1; mode=block
    Header set Access-Control-Allow-Origin: [values]
    Header set X-Frame-Options: deny
    Header set X-Content-Type-Options: nosniff
    Header set Strict-Transport-Security: max-age=3600; includeSubDomains

    ## Caching rules
    # Don’t cache by default
    Header set Cache-Control no-cache
    Header set Expires: 0

    # Cache static assets for 1 day
    <filesMatch ".(ico|css|js|gif|jpeg|jpg|png|svg|woff|ttf|eot)$">
        Header set Cache-Control "max-age=86400, public"
    </filesMatch>

</IfModule>
  • Nginx Configuration
## CSP
add_header Content-Security-Policy: [values];

## General Security Headers
add_header X-XSS-Protection: 1; mode=block;
add_header Access-Control-Allow-Origin: [values];
add_header X-Frame-Options: deny;
add_header X-Content-Type-Options: nosniff;
add_header Strict-Transport-Security: max-age=3600; includeSubDomains;

## Caching rules
# Don’t cache by default
add_header Cache-Control no-cache;
add_header Expires: 0;

# Cache static assets for 1 day
location ~* \.(?:ico|css|js|gif|jpe?g|png|svg|woff|ttf|eot)$ {
    try_files $uri @rewriteapp;
    add_header Cache-Control "max-age=86400, public";
}

A7- Cross Site Scripting (XSS)

  • Html encode all the special characters using htmlspecialchars() or htmlentities() [if its inside html tags]
    <?php echo htmlspecialchars($string, ENT_QUOTES, 'UTF-8'); ?>
    <?php echo htmlentities($string, ENT_QUOTES, 'UTF-8');?> 
  • Unicode encode all the speicial characters using the following javascript [if its inside javascript]
   function jsEscape(str){
        return String(str).replace(/[^\w. ]/gi, function(c){
            return '\\u'+('0000'+c.charCodeAt(0).toString(16)).slice(-4);
        });
    } 
  • If the Input is specific Datatype (int,float,..):
    • Typecast the value to the corresponding datatype using functions like int(),float()
    • Validate the variable with corresponding Datatype using is_numeric(),is_float() etc.
        if (is_numeric($id) == True)
        {
            <code>
        }
  • Implement Content Security Policy (CSP) to block the inclusion of external javascript.
  default-src 'self'; script-src 'self'; object-src 'none'; frame-src 'none'; base-uri 'none'; 

A8- Insecure Desearialization

  • Use a safe, standard data interchange format such as JSON (via json_decode() and json_encode()) instead of unserialize() function, if you need to pass serialized data to the user.
  • Implementing integrity checks such as digital signatures on any serialized objects to prevent hostile object creation or data tampering.
  • Enforcing strict type constraints during deserialization before object creation as the code typically expects a definable set of classes. Bypasses to this technique have been demonstrated, so reliance solely on this is not advisable.
  • Isolating and running code that deserializes in low privilege environments when possible.
  • Logging deserialization exceptions and failures, such as where the incoming type is not the expected type, or the deserialization throws exceptions.
  • Restricting or monitoring incoming and outgoing network connectivity from containers or servers that deserialize.
  • Monitoring deserialization, alerting if a user deserializes constantly.
  • Use safe libraries that do not allow code execution at deserialization.
  • Not communicate with the outside world using serialized objects
  • Limit access to the serialized source
    • if it is a file, restrict the access to it.
    • if it comes from the network, restrict who has access to the process, such as with a Firewall or by authenticating the sender first.

A9- Using Components with Known Vulnerabilities

  • Update <app> Server version to latest Version (v.<version no.>)
  • Update <cms name> <theme> / <plugins> to latest version (v.<version no.>)
  • Remove unused dependencies, unnecessary features, components, files, and documentation.
  • Continuously inventory the versions of both client-side and server-side components (e.g. frameworks, libraries) and their dependencies using tools like versions, DependencyCheck, retire.js, etc. Continuously monitor sources like CVE and NVD for vulnerabilities in the components. Use software composition analysis tools to automate the process. Subscribe to email alerts for security vulnerabilities related to components you use.
  • Only obtain components from official sources over secure links. Prefer signed packages to reduce the chance of including a modified, malicious component.
  • Monitor for libraries and components that are unmaintained or do not create security patches for older versions. If patching is not possible, consider deploying a virtual patch to monitor, detect, or protect against the discovered issue.
  • Every organization must ensure that there is an ongoing plan for monitoring, triaging, and applying updates or configuration changes for the lifetime of the application or portfolio.

A10- Insufficient Logging and Monitoring

  • Information to be logged includes the following: IP address of the originating Source, Date, Time, Username (No Password), session details, Referrer, Process id, URL, User Agent, Countries if any in addition to other details to be logged in the web application.
  • Logging of Authentication Process which includes number of successful and failed login attempts.
  • To create audit logs use auto numbering so that every logged entry has an un-editable log number. Then if one audit entry is deleted a gap in the numbering sequence will appear.
  • Report of the web application logs to be generated weekly by the administrator to keep track of the web application activities.

Cross-Site Request Forgery (CSRF)

  • Check if your framework has built-in CSRF protection and use it. If framework does not have built-in CSRF protection add CSRF tokens to all state changing requests (requests that cause actions on the site) and validate them on backend.
  • Always use SameSite Cookie Attribute for session cookies
  • Use custom request headers
  • Verify the origin with standard headers
  • Use unpredictable Synchronizer Token Pattern. In this method, the website generates a random token in each form as a hidden value. This token is associated with the users’current session.
  • Use double submit cookies
  • Remember that any Cross-Site Scripting (XSS) can be used to defeat all CSRF mitigation techniques.
  • Do not use GET requests for state changing operations.
  • CSRF can be avoided by creating a unique token in a hidden field which would be sent in the body of the HTTP request rather than in an URL, which is more prone to exposure.
  • Forcing the user to re-authenticate or proving that they are users in order to protect CSRF. For example, CAPTCHA.
  • Include a hidden validated CSRF token in the form, so that the CSRF protection middleware of Laravel can validate the request. The syntax is shown below
<form method = "POST" action="/profile">
   {{ csrf_field() }}
   ...
</form>

For Laravel the VerifyCsrfToken middleware will also check for the X-CSRF-TOKEN request header. For example, store the token in an HTML meta tag:

<meta name="csrf-token" content="{{ csrf_token() }}">

Then, once you have created the meta tag, you can instruct a library like jQuery to automatically add the token to all request headers. This provides simple, convenient CSRF protection for your AJAX based applications:

$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    }
});

Malicious File Upload

  • Whitelist allowed extensions. Only allow safe and critical extensions for business functionality
  • Validate the file type, don’t trust the Content-Type header as it can be spoofed
  • Change the filename to something generated by the application
  • The application should perform filtering and content checking on any files which are uploaded to the server. Files should be thoroughly scanned and validated before being made available to other users. If in doubt, the file should be discarded.
  • It is necessary to have a list of only permitted extensions on the web application. And, file extension can be selected from the list.
  • All the control characters and Unicode ones should be removed from the filenames and their extensions without any exception. Also, the special characters such as “;”, “:”, “>”, “<”, “/” ,”\”, additional “.”, “*”, “%”, “$”, and so on should be discarded as well. If it is applicable and there is no need to have Unicode characters, it is highly recommended to only accept Alpha-Numeric characters and only 1 dot as an input for the file name and the extension; in which the file name and also the extension should not be empty at all (regular expression: [a-zA-Z0-9]{1,200}.[a-zA-Z0-9]{1,10}).
  • Limit the filename length. For instance, the maximum length of the name of a file plus its extension should be less than 255 characters (without any directory) in an NTFS partition.
  • Uploaded directory should not have any “execute” permission and all the script handlers should be removed from these directories.
  • Limit the file size to a maximum value in order to prevent denial of service attacks (on file space or other web application’s functions such as the image resizer).
  • In case of having compressed file extract functions, contents of the compressed file should be checked one by one as a new file.
  • If it is possible, consider saving the files in a database rather than on the filesystem.
  • File uploaders should be only accessible to authenticated and authorised users if possible.
  • Adding the “Content-Disposition: Attachment” and “X-Content-Type-Options: nosniff” headers to the response of static files will secure the website against Flash or PDF-based cross-site content-hijacking attacks.
  • Verifying the user has permissions to upload files before processing the file upload
if (!current_user_can('upload_files')) // Verify the current user can upload files
    wp_die(__('You do not have permission to upload files.'));
  • wp_check_filetype() will verify the file’s extension is allowed to be uploaded, and, by default, WordPress’s list of allowable file uploads prevents any executable code from being uploaded.
$fileInfo = wp_check_filetype(basename($_FILES['wpshop_file']['name']));
if (!empty($fileInfo['ext'])) {
    // This file is valid
} else {
    // Invalid file
}

You can also further limit what is allowed by specifying the mime types allowed. This list allows only images.

// We are only allowing images
$allowedMimes = array(
    'jpg|jpeg|jpe' => 'image/jpeg',
    'gif'          => 'image/gif',
    'png'          => 'image/png',
);
 
$fileInfo = wp_check_filetype(basename($_FILES['wpshop_file']['name']), $allowedMimes);
  • WordPress has a handy built-in function to handle file uploads: wp_handle_upload(). It takes a reference to a single element of the $_FILES super-global and returns an array containing the URL, full path, and mime type of the upload
$fileInfo = wp_check_filetype(basename($_FILES['wpshop_file']['name']));
 
if (!empty($fileInfo['type'])) {
    $uploadInfo = wp_handle_upload($_FILES['wpshop_file'], array(
        'test_form' => false,
        'mimes'     => $allowedMimes,
    ));
}
  • Use getimagesize() attempts to read the header information of the image and will fail on an invalid image.
if (!@getimagesize($_FILES['wpshop_file']['tmp_name']))
    wp_die(__('An invalid image was supplied.'));

Server Side Request Forgery(SSRF)

  • Use a whitelist of allowed domains, resources and protocols from where the web server can fetch resources.
  • Any input accepted from the user should be validated and rejected if it does not match the positive specification expected.
  • If possible, do not accept user input in functions that control where the web server can fetch resources.
  • Disable unused URL schemas.
  • Migrate to IMDSv2 and disable old IMDSv1. IMDSv2 is an additional defence-in-depth mechanism for AWS that mitigates some of the instances of SSRF.

Noncompliant Code

$url = $_GET["url"];
$resp = file_get_contents($url); // Noncompliant
// ...

Compliant Code

$whitelist = array(
  "https://www.sonarsource.com"
);
$url = $_GET["url"];
if (in_array($url, $whitelist)) {
  $resp = file_get_contents($url);
  // ...
}

References