Requests_SSL::verify_certificate()   D
last analyzed

Complexity

Conditions 10
Paths 14

Size

Total Lines 42
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 20
nc 14
nop 2
dl 0
loc 42
rs 4.8196
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * SSL utilities for Requests
4
 *
5
 * @package Requests
6
 * @subpackage Utilities
7
 */
8
9
/**
10
 * SSL utilities for Requests
11
 *
12
 * Collection of utilities for working with and verifying SSL certificates.
13
 *
14
 * @package Requests
15
 * @subpackage Utilities
16
 */
17
class Requests_SSL {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
Coding Style introduced by
This class is not in CamelCase format.

Classes in PHP are usually named in CamelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. The whole name starts with a capital letter as well.

Thus the name database provider becomes DatabaseProvider.

Loading history...
18
	/**
19
	 * Verify the certificate against common name and subject alternative names
20
	 *
21
	 * Unfortunately, PHP doesn't check the certificate against the alternative
22
	 * names, leading things like 'https://www.github.com/' to be invalid.
23
	 * Instead
24
	 *
25
	 * @see https://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1
26
	 *
27
	 * @throws Requests_Exception On not obtaining a match for the host (`fsockopen.ssl.no_match`)
28
	 * @param string $host Host name to verify against
29
	 * @param array $cert Certificate data from openssl_x509_parse()
30
	 * @return bool
31
	 */
32
	public static function verify_certificate($host, $cert) {
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
33
		// Calculate the valid wildcard match if the host is not an IP address
34
		$parts = explode('.', $host);
35
		if (ip2long($host) === false) {
36
			$parts[0] = '*';
37
		}
38
		$wildcard = implode('.', $parts);
0 ignored issues
show
Unused Code introduced by
$wildcard is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
39
40
		$has_dns_alt = false;
41
42
		// Check the subjectAltName
43
		if (!empty($cert['extensions']) && !empty($cert['extensions']['subjectAltName'])) {
44
			$altnames = explode(',', $cert['extensions']['subjectAltName']);
45
			foreach ($altnames as $altname) {
46
				$altname = trim($altname);
47
				if (strpos($altname, 'DNS:') !== 0) {
48
					continue;
49
				}
50
51
				$has_dns_alt = true;
52
53
				// Strip the 'DNS:' prefix and trim whitespace
54
				$altname = trim(substr($altname, 4));
55
56
				// Check for a match
57
				if (self::match_domain($host, $altname) === true) {
58
					return true;
59
				}
60
			}
61
		}
62
63
		// Fall back to checking the common name if we didn't get any dNSName
64
		// alt names, as per RFC2818
65
		if (!$has_dns_alt && !empty($cert['subject']['CN'])) {
66
			// Check for a match
67
			if (self::match_domain($host, $cert['subject']['CN']) === true) {
68
				return true;
69
			}
70
		}
71
72
		return false;
73
	}
74
75
	/**
76
	 * Verify that a reference name is valid
77
	 *
78
	 * Verifies a dNSName for HTTPS usage, (almost) as per Firefox's rules:
79
	 * - Wildcards can only occur in a name with more than 3 components
80
	 * - Wildcards can only occur as the last character in the first
81
	 *   component
82
	 * - Wildcards may be preceded by additional characters
83
	 *
84
	 * We modify these rules to be a bit stricter and only allow the wildcard
85
	 * character to be the full first component; that is, with the exclusion of
86
	 * the third rule.
87
	 *
88
	 * @param string $reference Reference dNSName
89
	 * @return boolean Is the name valid?
90
	 */
91
	public static function verify_reference_name($reference) {
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
92
		$parts = explode('.', $reference);
93
94
		// Check the first part of the name
95
		$first = array_shift($parts);
96
97
		if (strpos($first, '*') !== false) {
98
			// Check that the wildcard is the full part
99
			if ($first !== '*') {
100
				return false;
101
			}
102
103
			// Check that we have at least 3 components (including first)
104
			if (count($parts) < 2) {
105
				return false;
106
			}
107
		}
108
109
		// Check the remaining parts
110
		foreach ($parts as $part) {
111
			if (strpos($part, '*') !== false) {
112
				return false;
113
			}
114
		}
115
116
		// Nothing found, verified!
117
		return true;
118
	}
119
120
	/**
121
	 * Match a hostname against a dNSName reference
122
	 *
123
	 * @param string $host Requested host
124
	 * @param string $reference dNSName to match against
125
	 * @return boolean Does the domain match?
126
	 */
127
	public static function match_domain($host, $reference) {
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
128
		// Check if the reference is blacklisted first
129
		if (self::verify_reference_name($reference) !== true) {
130
			return false;
131
		}
132
133
		// Check for a direct match
134
		if ($host === $reference) {
135
			return true;
136
		}
137
138
		// Calculate the valid wildcard match if the host is not an IP address
139
		// Also validates that the host has 3 parts or more, as per Firefox's
140
		// ruleset.
141 View Code Duplication
		if (ip2long($host) === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
142
			$parts = explode('.', $host);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
143
			$parts[0] = '*';
144
			$wildcard = implode('.', $parts);
145
			if ($wildcard === $reference) {
146
				return true;
147
			}
148
		}
149
150
		return false;
151
	}
152
}