PluginProbe
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions / 260917
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions v260917
260917 260913 260909 260829 260814 260805 110710 110731 110812 110815 110912 110913 110915 110926 110927 111002 111003 111011 111017 111029 111105 111206 111216 111220 120213 All 189 releases
← All changes | src/includes/classes/utils-arrays.inc.php +75 -5 260805260917 View file →
@@ -160,8 +160,30 @@
160 160 }
161 161 return $array;
162 162 }
163 163 /**
164 + * Sets specified array elements to an empty string when they are not set according to ``isset()`` (missing or NULL).
165 + *
166 + * @package s2Member\Utilities
167 + * @since 260814
168 + *
169 + * @param array $array An input array.
170 + * @param array $keys Array keys whose unset elements should be set.
171 + * @return array Returns the ``$array`` after setting unset elements to empty strings.
172 + */
173 + public static function set_unset_elements ($array = FALSE, $keys = FALSE)
174 + {
175 + $array = (array)$array;
176 + $keys = (array)$keys;
177 +
178 + //260814 isset() treats both missing keys and null values as unset; initialize either case to an empty string.
179 + foreach ($keys as $key)
180 + if (!isset($array[$key]))
181 + $array[$key] = '';
182 +
183 + return $array;
184 + }
185 + /**
164 186 * Forces string values on each array value *(also supports multi-dimensional arrays)*.
165 187 *
166 188 * @package s2Member\Utilities
167 189 * @since 111101
@@ -229,12 +251,13 @@
229 251 return /* Now return the array. */ $array;
230 252 }
231 253
232 254 /**
233 - * Like maybe_unserialize(), but skips objects.
255 + * Safely unserializes a value when appropriate.
234 256 *
235 257 * @package s2Member\Utilities
236 258 * @since 250801
259 + * @since 260808 Added additional validation before unserializing.
237 260 *
238 261 * @param mixed $value The value to be unserialized.
239 262 * @return mixed The unserialized value, or original if not serialized or blocked.
240 263 */
@@ -244,17 +267,64 @@
244 267 }
245 268
246 269 $value = trim($value);
247 270
271 + //260808 Reject disallowed serialization types without matching harmless text inside serialized strings.
272 + if (self::serialized_contains_disallowed_type($value)) {
273 + do_action('ws_plugin__s2member_security_object_detected', $value);
274 + return null;
275 + }
276 +
248 277 if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
249 278 return @unserialize($value, ['allowed_classes' => false]);
250 279 }
251 280
252 - if (strpos($value, 'O:') !== false) {
253 - do_action('ws_plugin__s2member_security_object_detected', $value);
254 - return null;
281 + return @unserialize($value);
282 + }
283 +
284 + /**
285 + * Checks serialized data for disallowed serialization types.
286 + *
287 + * Serialized string payloads are skipped so object-like text inside a
288 + * normal string does not produce a false positive.
289 + *
290 + * @package s2Member\Utilities
291 + * @since 260808
292 + *
293 + * @param string $value Serialized value to inspect.
294 + * @return bool True if a disallowed serialization type is present.
295 + */
296 + private static function serialized_contains_disallowed_type($value) {
297 + $length = strlen($value);
298 +
299 + for ($offset = 0; $offset < $length; ++$offset) {
300 + $token = $value[$offset];
301 +
302 + //260808 Skip complete serialized strings so harmless text such as "PROMO:" is not mistaken for an object token.
303 + if ($token === 's' && isset($value[$offset + 1]) && $value[$offset + 1] === ':') {
304 + if (!preg_match('/\Gs:([0-9]+):"/', $value, $match, 0, $offset)) {
305 + continue;
306 + }
307 +
308 + $string_length = (int)$match[1];
309 + $string_start = $offset + strlen($match[0]);
310 + $string_end = $string_start + $string_length;
311 +
312 + if ($string_end + 1 >= $length || substr($value, $string_end, 2) !== '";') {
313 + return true;
314 + }
315 +
316 + $offset = $string_end + 1;
317 + continue;
318 + }
319 +
320 + //260808 O=object, C=Serializable object, E=enum object, S=noncanonical string, R/r=references that can create cyclic structures.
321 + if (($token === 'O' || $token === 'C' || $token === 'E' || $token === 'S' || $token === 'R' || $token === 'r')
322 + && isset($value[$offset + 1]) && $value[$offset + 1] === ':') {
323 + return true;
324 + }
255 325 }
256 326
257 - return @unserialize($value);
327 + return false;
258 328 }
259 329 }
260 330 }