Add support for safe (un)serialization using (un)serialize()
The unserialization of random objects may be dangerous because the destructor of the object will be called when the unserialized objects are out of scope. However, the person who wrote the class may not be aware of the danger of unserialization. Therefore, we would like to require every users of the unserialize() to provide a whitelist of the class names that are authorized to be unserialized so that we can make sure the object is safe to be unserialized. Add a parameter 'class_whitelist' to unserialize() function to determine whether to raise warnings for unsafe unserialization. If the class to be unserialized is not an instance of Serilizable or not in the whitelist, warnings will be raised. For the detailed reason why we need this, please see http://fburl.com/SafeSerializable for more information. Add a parameter 'all_classes_enabled' to allow those hphp functions that need to unserialize any class. For example, fb_call_user_func_async() will need to serialize and nserialize the given parameters.
Esse commit está contido em:
@@ -4305,6 +4305,9 @@ void EmitterVisitor::emitBuiltinDefaultArg(Emitter& e, Variant& v,
|
||||
not_reached();
|
||||
}
|
||||
break;
|
||||
case KindOfArray:
|
||||
e.Array(v.getArrayData());
|
||||
break;
|
||||
default:
|
||||
not_reached();
|
||||
}
|
||||
|
||||
@@ -437,7 +437,7 @@ int64_t ScalarExpression::getHash() const {
|
||||
Variant ScalarExpression::getVariant() const {
|
||||
if (!m_serializedValue.empty()) {
|
||||
Variant ret = unserialize_from_buffer(
|
||||
m_serializedValue.data(), m_serializedValue.size());
|
||||
m_serializedValue.data(), m_serializedValue.size(), null_array);
|
||||
if (ret.isDouble()) {
|
||||
return m_dval;
|
||||
}
|
||||
|
||||
+2
-3
@@ -415,11 +415,10 @@ function get_serialized_default($s) {
|
||||
if (preg_match('/^".*"$/', $s) ||
|
||||
preg_match('/^[\-0-9.]+$/', $s) ||
|
||||
preg_match('/^0x[0-9a-fA-F]+$/', $s) ||
|
||||
preg_match('/^(true|false|null)$/', $s) ||
|
||||
$s == 'Array()'
|
||||
) {
|
||||
preg_match('/^(true|false|null)$/', $s)) {
|
||||
return serialize(eval("return $s;"));
|
||||
}
|
||||
if ($s == "empty_array") return serialize(array());
|
||||
if (preg_match('/^null_(string|array|object|variant)$/', $s)) {
|
||||
return serialize(null);
|
||||
}
|
||||
|
||||
@@ -972,7 +972,7 @@ DefineFunction(
|
||||
array(
|
||||
'name' => "extra",
|
||||
'type' => StringVec,
|
||||
'value' => "Array()",
|
||||
'value' => "empty_array",
|
||||
'desc' => "An array of extra ascii chars to be encoded.",
|
||||
),
|
||||
),
|
||||
|
||||
@@ -536,6 +536,12 @@ DefineFunction(
|
||||
'type' => String,
|
||||
'desc' => "The serialized string.\n\nIf the variable being unserialized is an object, after successfully reconstructing the object PHP will automatically attempt to call the __wakeup() member function (if it exists).\n\nunserialize_callback_func directive\n\nIt's possible to set a callback-function which will be called, if an undefined class should be instantiated during unserializing. (to prevent getting an incomplete object \"__PHP_Incomplete_Class\".) Use your php.ini, ini_set() or .htaccess to define 'unserialize_callback_func'. Everytime an undefined class should be instantiated, it'll be called. To disable this feature just empty this setting.",
|
||||
),
|
||||
array(
|
||||
'name' => "class_whitelist",
|
||||
'type' => StringVec,
|
||||
'desc' => "The array of the class names that are authorized to be unserialized. The array is default to be empty, which means no class is allowed. If the class name is a super class, all its subclasses are also allowed. Note that primitive types and Serializable classes are not subject to this whitelist. See http://fburl.com/SafeSerializable for the reason why we need this.",
|
||||
'value' => "empty_array"
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
|
||||
@@ -747,15 +747,18 @@ String f_serialize(CVarRef value) {
|
||||
}
|
||||
|
||||
Variant unserialize_ex(const char* str, int len,
|
||||
VariableUnserializer::Type type) {
|
||||
VariableUnserializer::Type type,
|
||||
CArrRef class_whitelist /* = null_array */) {
|
||||
if (str == nullptr || len <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VariableUnserializer vu(str, len, type);
|
||||
VariableUnserializer vu(str, len, type, false, class_whitelist);
|
||||
Variant v;
|
||||
try {
|
||||
v = vu.unserialize();
|
||||
} catch (FatalErrorException &e) {
|
||||
throw;
|
||||
} catch (Exception &e) {
|
||||
raise_notice("Unable to unserialize: [%s]. %s.", str,
|
||||
e.getMessage().c_str());
|
||||
@@ -764,8 +767,10 @@ Variant unserialize_ex(const char* str, int len,
|
||||
return v;
|
||||
}
|
||||
|
||||
Variant unserialize_ex(CStrRef str, VariableUnserializer::Type type) {
|
||||
return unserialize_ex(str.data(), str.size(), type);
|
||||
Variant unserialize_ex(CStrRef str,
|
||||
VariableUnserializer::Type type,
|
||||
CArrRef class_whitelist /* = null_array */) {
|
||||
return unserialize_ex(str.data(), str.size(), type, class_whitelist);
|
||||
}
|
||||
|
||||
String concat3(CStrRef s1, CStrRef s2, CStrRef s3) {
|
||||
|
||||
@@ -483,21 +483,28 @@ Object f_clone(CVarRef v);
|
||||
char const kUnserializableString[] = "\x01";
|
||||
|
||||
/**
|
||||
* Serialize/unserialize a variant into/from a string. We need these two
|
||||
* functions in runtime/base, as there are functions in runtime/base that depend on
|
||||
* these two functions.
|
||||
* Serialize/unserialize a variant into/from a string. We need these
|
||||
* two functions in runtime/base, as there are functions in
|
||||
* runtime/base that depend on these two functions.
|
||||
*/
|
||||
String f_serialize(CVarRef value);
|
||||
Variant unserialize_ex(CStrRef str, VariableUnserializer::Type type);
|
||||
Variant unserialize_ex(CStrRef str,
|
||||
VariableUnserializer::Type type,
|
||||
CArrRef class_whitelist = null_array);
|
||||
Variant unserialize_ex(const char* str, int len,
|
||||
VariableUnserializer::Type type);
|
||||
VariableUnserializer::Type type,
|
||||
CArrRef class_whitelist = null_array);
|
||||
|
||||
inline Variant unserialize_from_buffer(const char* str, int len) {
|
||||
return unserialize_ex(str, len, VariableUnserializer::Serialize);
|
||||
inline Variant unserialize_from_buffer(const char* str, int len,
|
||||
CArrRef class_whitelist = null_array) {
|
||||
return unserialize_ex(str, len,
|
||||
VariableUnserializer::Serialize,
|
||||
class_whitelist);
|
||||
}
|
||||
|
||||
inline Variant unserialize_from_string(CStrRef str) {
|
||||
return unserialize_from_buffer(str.data(), str.size());
|
||||
inline Variant unserialize_from_string(CStrRef str,
|
||||
CArrRef class_whitelist = null_array) {
|
||||
return unserialize_from_buffer(str.data(), str.size(), class_whitelist);
|
||||
}
|
||||
|
||||
String resolve_include(CStrRef file, const char* currentDir,
|
||||
|
||||
@@ -220,6 +220,9 @@ bool RuntimeOption::WhitelistExec = false;
|
||||
bool RuntimeOption::WhitelistExecWarningOnly = false;
|
||||
std::vector<std::string> RuntimeOption::AllowedExecCmds;
|
||||
|
||||
bool RuntimeOption::UnserializationWhitelistCheck = false;
|
||||
bool RuntimeOption::UnserializationWhitelistCheckWarningOnly = true;
|
||||
|
||||
std::string RuntimeOption::TakeoverFilename;
|
||||
int RuntimeOption::AdminServerPort;
|
||||
int RuntimeOption::AdminThreadCount = 1;
|
||||
@@ -773,6 +776,11 @@ void RuntimeOption::Load(Hdf &config, StringVec *overwrites /* = NULL */,
|
||||
WhitelistExecWarningOnly = server["WhitelistExecWarningOnly"].getBool();
|
||||
server["AllowedExecCmds"].get(AllowedExecCmds);
|
||||
|
||||
UnserializationWhitelistCheck =
|
||||
server["UnserializationWhitelistCheck"].getBool(false);
|
||||
UnserializationWhitelistCheckWarningOnly =
|
||||
server["UnserializationWhitelistCheckWarningOnly"].getBool(true);
|
||||
|
||||
server["AllowedFiles"].get(AllowedFiles);
|
||||
|
||||
server["ForbiddenFileExtensions"].get(ForbiddenFileExtensions);
|
||||
|
||||
@@ -219,6 +219,9 @@ public:
|
||||
static bool WhitelistExecWarningOnly;
|
||||
static std::vector<std::string> AllowedExecCmds;
|
||||
|
||||
static bool UnserializationWhitelistCheck;
|
||||
static bool UnserializationWhitelistCheckWarningOnly;
|
||||
|
||||
static std::string TakeoverFilename;
|
||||
static int AdminServerPort;
|
||||
static int AdminThreadCount;
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
namespace HPHP {
|
||||
|
||||
const Array null_array = Array();
|
||||
const Array empty_array = HphpArray::GetStaticEmptyArray();
|
||||
|
||||
void Array::setEvalScalar() const {
|
||||
Array* thisPtr = const_cast<Array*>(this);
|
||||
|
||||
@@ -3089,6 +3089,18 @@ void Variant::unserialize(VariableUnserializer *uns,
|
||||
|
||||
VM::Class* cls = VM::Unit::loadClass(clsName.get());
|
||||
Object obj;
|
||||
if (RuntimeOption::UnserializationWhitelistCheck &&
|
||||
!uns->isWhitelistedClass(clsName)) {
|
||||
String err_msg =
|
||||
"The object being unserialized with class name '%s' "
|
||||
"is not in the given whitelist. "
|
||||
"See http://fburl.com/SafeSerializable for more detail";
|
||||
if (RuntimeOption::UnserializationWhitelistCheckWarningOnly) {
|
||||
raise_warning(err_msg, clsName.c_str());
|
||||
} else {
|
||||
raise_error(err_msg, clsName.c_str());
|
||||
}
|
||||
}
|
||||
if (cls) {
|
||||
obj = VM::Instance::newInstance(cls);
|
||||
if (UNLIKELY(cls == c_Pair::s_cls && size != 2)) {
|
||||
|
||||
@@ -67,6 +67,7 @@ extern const VarNR NEGINF_varNR;
|
||||
extern const VarNR NAN_varNR;
|
||||
extern const String null_string;
|
||||
extern const Array null_array;
|
||||
extern const Array empty_array;
|
||||
|
||||
/*
|
||||
* All TypedValue-compatible types have their reference count field at
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
#include <runtime/base/variable_unserializer.h>
|
||||
#include <runtime/base/complex_types.h>
|
||||
#include <runtime/base/zend/zend_strtod.h>
|
||||
|
||||
#include <runtime/base/array/array_iterator.h>
|
||||
#include <runtime/ext/ext_class.h>
|
||||
|
||||
namespace HPHP {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -68,5 +69,20 @@ Variant &VariableUnserializer::addVar() {
|
||||
return m_vars.back();
|
||||
}
|
||||
|
||||
bool VariableUnserializer::isWhitelistedClass(CStrRef cls_name) const {
|
||||
if (m_type != Serialize || m_classWhiteList.isNull()) {
|
||||
return true;
|
||||
}
|
||||
if (!m_classWhiteList.isNull() && !m_classWhiteList.empty()) {
|
||||
for (ArrayIter iter(m_classWhiteList); iter; ++iter) {
|
||||
CVarRef value(iter.secondRef());
|
||||
if (f_is_subclass_of(cls_name, value) || value.same(cls_name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
@@ -33,17 +33,27 @@ public:
|
||||
};
|
||||
|
||||
public:
|
||||
/**
|
||||
* Be aware that the default class_whitelist is null_array instead of
|
||||
* empty_array here because we do not limit the unserialization of arbitrary
|
||||
* class for hphp internal use
|
||||
*/
|
||||
VariableUnserializer(const char *str, size_t len, Type type,
|
||||
bool allowUnknownSerializableClass = false)
|
||||
bool allowUnknownSerializableClass = false,
|
||||
CArrRef class_whitelist = null_array)
|
||||
: m_type(type), m_buf(str), m_end(str + len),
|
||||
m_unknownSerializable(allowUnknownSerializableClass) {}
|
||||
m_unknownSerializable(allowUnknownSerializableClass),
|
||||
m_classWhiteList(class_whitelist) {}
|
||||
VariableUnserializer(const char *str, const char *end, Type type,
|
||||
bool allowUnknownSerializableClass = false)
|
||||
bool allowUnknownSerializableClass = false,
|
||||
CArrRef class_whitelist = null_array)
|
||||
: m_type(type), m_buf(str), m_end(end),
|
||||
m_unknownSerializable(allowUnknownSerializableClass) {}
|
||||
m_unknownSerializable(allowUnknownSerializableClass),
|
||||
m_classWhiteList(class_whitelist) {}
|
||||
|
||||
Type getType() const { return m_type;}
|
||||
bool allowUnknownSerializableClass() const { return m_unknownSerializable;}
|
||||
bool isWhitelistedClass(CStrRef cls_name) const;
|
||||
|
||||
Variant unserialize();
|
||||
Variant unserializeKey();
|
||||
@@ -123,6 +133,7 @@ public:
|
||||
smart::vector<RefInfo> m_refs;
|
||||
smart::list<Variant> m_vars;
|
||||
bool m_unknownSerializable;
|
||||
CArrRef m_classWhiteList; // classes allowed to be unserialized
|
||||
|
||||
void check() {
|
||||
if (m_buf >= m_end) {
|
||||
|
||||
@@ -2145,7 +2145,7 @@ TypedValue * fg1_fb_htmlspecialchars(TypedValue* rv, HPHP::VM::ActRec* ar, int64
|
||||
tvCastToStringInPlace(args-0);
|
||||
}
|
||||
String defVal2 = "ISO-8859-1";
|
||||
Array defVal3 = Array();
|
||||
Array defVal3 = empty_array;
|
||||
fh_fb_htmlspecialchars((&rv->m_data), &args[-0].m_data, (count > 1) ? (int)(args[-1].m_data.num) : (int)(k_ENT_COMPAT), (count > 2) ? &args[-2].m_data : (Value*)(&defVal2), (count > 3) ? &args[-3].m_data : (Value*)(&defVal3));
|
||||
if (rv->m_data.num == 0LL) rv->m_type = KindOfNull;
|
||||
return rv;
|
||||
@@ -2159,7 +2159,7 @@ TypedValue* fg_fb_htmlspecialchars(HPHP::VM::ActRec *ar) {
|
||||
if ((count <= 3 || (args-3)->m_type == KindOfArray) && (count <= 2 || IS_STRING_TYPE((args-2)->m_type)) && (count <= 1 || (args-1)->m_type == KindOfInt64) && IS_STRING_TYPE((args-0)->m_type)) {
|
||||
rv.m_type = KindOfString;
|
||||
String defVal2 = "ISO-8859-1";
|
||||
Array defVal3 = Array();
|
||||
Array defVal3 = empty_array;
|
||||
fh_fb_htmlspecialchars((&rv.m_data), &args[-0].m_data, (count > 1) ? (int)(args[-1].m_data.num) : (int)(k_ENT_COMPAT), (count > 2) ? &args[-2].m_data : (Value*)(&defVal2), (count > 3) ? &args[-3].m_data : (Value*)(&defVal3));
|
||||
if (rv.m_data.num == 0LL) rv.m_type = KindOfNull;
|
||||
frame_free_locals_no_this_inl(ar, 4);
|
||||
|
||||
@@ -90,7 +90,7 @@ String f_htmlspecialchars(CStrRef str, int quote_style = k_ENT_COMPAT,
|
||||
bool double_encode = true);
|
||||
String f_fb_htmlspecialchars(CStrRef str, int quote_style = k_ENT_COMPAT,
|
||||
CStrRef charset = "ISO-8859-1",
|
||||
CArrRef extra = Array());
|
||||
CArrRef extra = empty_array);
|
||||
String f_quoted_printable_encode(CStrRef str);
|
||||
String f_quoted_printable_decode(CStrRef str);
|
||||
Variant f_convert_uudecode(CStrRef data);
|
||||
|
||||
@@ -182,8 +182,8 @@ void f_debug_zval_dump(CVarRef variable) {
|
||||
vs.serialize(variable, false);
|
||||
}
|
||||
|
||||
Variant f_unserialize(CStrRef str) {
|
||||
return unserialize_from_string(str);
|
||||
Variant f_unserialize(CStrRef str, CArrRef class_whitelist /* = empty_array */) {
|
||||
return unserialize_from_string(str, class_whitelist);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -958,21 +958,33 @@ TypedValue* fg_debug_zval_dump(HPHP::VM::ActRec *ar) {
|
||||
|
||||
|
||||
/*
|
||||
HPHP::Variant HPHP::f_unserialize(HPHP::String const&)
|
||||
_ZN4HPHP13f_unserializeERKNS_6StringE
|
||||
HPHP::Variant HPHP::f_unserialize(HPHP::String const&, HPHP::Array const&)
|
||||
_ZN4HPHP13f_unserializeERKNS_6StringERKNS_5ArrayE
|
||||
|
||||
(return value) => rax
|
||||
_rv => rdi
|
||||
str => rsi
|
||||
class_whitelist => rdx
|
||||
*/
|
||||
|
||||
TypedValue* fh_unserialize(TypedValue* _rv, Value* str) asm("_ZN4HPHP13f_unserializeERKNS_6StringE");
|
||||
TypedValue* fh_unserialize(TypedValue* _rv, Value* str, Value* class_whitelist) asm("_ZN4HPHP13f_unserializeERKNS_6StringERKNS_5ArrayE");
|
||||
|
||||
TypedValue * fg1_unserialize(TypedValue* rv, HPHP::VM::ActRec* ar, int64_t count) __attribute__((noinline,cold));
|
||||
TypedValue * fg1_unserialize(TypedValue* rv, HPHP::VM::ActRec* ar, int64_t count) {
|
||||
TypedValue* args UNUSED = ((TypedValue*)ar) - 1;
|
||||
tvCastToStringInPlace(args-0);
|
||||
fh_unserialize((rv), &args[-0].m_data);
|
||||
switch (count) {
|
||||
default: // count >= 2
|
||||
if ((args-1)->m_type != KindOfArray) {
|
||||
tvCastToArrayInPlace(args-1);
|
||||
}
|
||||
case 1:
|
||||
break;
|
||||
}
|
||||
if (!IS_STRING_TYPE((args-0)->m_type)) {
|
||||
tvCastToStringInPlace(args-0);
|
||||
}
|
||||
Array defVal1 = empty_array;
|
||||
fh_unserialize((rv), &args[-0].m_data, (count > 1) ? &args[-1].m_data : (Value*)(&defVal1));
|
||||
if (rv->m_type == KindOfUninit) rv->m_type = KindOfNull;
|
||||
return rv;
|
||||
}
|
||||
@@ -981,25 +993,26 @@ TypedValue* fg_unserialize(HPHP::VM::ActRec *ar) {
|
||||
TypedValue rv;
|
||||
int64_t count = ar->numArgs();
|
||||
TypedValue* args UNUSED = ((TypedValue*)ar) - 1;
|
||||
if (count == 1LL) {
|
||||
if (IS_STRING_TYPE((args-0)->m_type)) {
|
||||
fh_unserialize((&(rv)), &args[-0].m_data);
|
||||
if (count >= 1LL && count <= 2LL) {
|
||||
if ((count <= 1 || (args-1)->m_type == KindOfArray) && IS_STRING_TYPE((args-0)->m_type)) {
|
||||
Array defVal1 = empty_array;
|
||||
fh_unserialize((&(rv)), &args[-0].m_data, (count > 1) ? &args[-1].m_data : (Value*)(&defVal1));
|
||||
if (rv.m_type == KindOfUninit) rv.m_type = KindOfNull;
|
||||
frame_free_locals_no_this_inl(ar, 1);
|
||||
frame_free_locals_no_this_inl(ar, 2);
|
||||
memcpy(&ar->m_r, &rv, sizeof(TypedValue));
|
||||
return &ar->m_r;
|
||||
} else {
|
||||
fg1_unserialize(&rv, ar, count);
|
||||
frame_free_locals_no_this_inl(ar, 1);
|
||||
frame_free_locals_no_this_inl(ar, 2);
|
||||
memcpy(&ar->m_r, &rv, sizeof(TypedValue));
|
||||
return &ar->m_r;
|
||||
}
|
||||
} else {
|
||||
throw_wrong_arguments_nr("unserialize", count, 1, 1, 1);
|
||||
throw_wrong_arguments_nr("unserialize", count, 1, 2, 1);
|
||||
}
|
||||
rv.m_data.num = 0LL;
|
||||
rv.m_type = KindOfNull;
|
||||
frame_free_locals_no_this_inl(ar, 1);
|
||||
frame_free_locals_no_this_inl(ar, 2);
|
||||
memcpy(&ar->m_r, &rv, sizeof(TypedValue));
|
||||
return &ar->m_r;
|
||||
return &ar->m_r;
|
||||
|
||||
@@ -276,15 +276,16 @@ variable => rdi
|
||||
void fh_debug_zval_dump(TypedValue* variable) asm("_ZN4HPHP17f_debug_zval_dumpERKNS_7VariantE");
|
||||
|
||||
/*
|
||||
HPHP::Variant HPHP::f_unserialize(HPHP::String const&)
|
||||
_ZN4HPHP13f_unserializeERKNS_6StringE
|
||||
HPHP::Variant HPHP::f_unserialize(HPHP::String const&, HPHP::Array const&)
|
||||
_ZN4HPHP13f_unserializeERKNS_6StringERKNS_5ArrayE
|
||||
|
||||
(return value) => rax
|
||||
_rv => rdi
|
||||
str => rsi
|
||||
class_whitelist => rdx
|
||||
*/
|
||||
|
||||
TypedValue* fh_unserialize(TypedValue* _rv, Value* str) asm("_ZN4HPHP13f_unserializeERKNS_6StringE");
|
||||
TypedValue* fh_unserialize(TypedValue* _rv, Value* str, Value* class_whitelist) asm("_ZN4HPHP13f_unserializeERKNS_6StringERKNS_5ArrayE");
|
||||
|
||||
/*
|
||||
HPHP::Array HPHP::f_get_defined_vars()
|
||||
|
||||
@@ -61,7 +61,8 @@ void f_var_dump(CVarRef v);
|
||||
void f_var_dump(int _argc, CVarRef expression, CArrRef _argv = null_array);
|
||||
void f_debug_zval_dump(CVarRef variable);
|
||||
String f_serialize(CVarRef value);
|
||||
Variant f_unserialize(CStrRef str);
|
||||
Variant f_unserialize(CStrRef str,
|
||||
CArrRef class_whitelist = empty_array);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// variable table
|
||||
|
||||
@@ -8596,7 +8596,7 @@ const char *g_class_map[] = {
|
||||
(const char *)0x14 /* KindOfString */, (const char *)0x2000, "str", "", (const char *)0x14 /* KindOfString */, "", (const char *)0, "", (const char *)0, NULL,
|
||||
(const char *)0x2000, "quote_style", "", (const char *)0xa /* KindOfInt64 */, "i:2;", (const char *)4, "ENT_COMPAT", (const char *)10, NULL,
|
||||
(const char *)0x2000, "charset", "", (const char *)0x14 /* KindOfString */, "s:10:\"ISO-8859-1\";", (const char *)18, "\"ISO-8859-1\"", (const char *)12, NULL,
|
||||
(const char *)0x2000, "extra", "", (const char *)0x20 /* KindOfArray */, "a:0:{}", (const char *)6, "Array()", (const char *)7, NULL,
|
||||
(const char *)0x2000, "extra", "", (const char *)0x20 /* KindOfArray */, "a:0:{}", (const char *)6, "empty_array", (const char *)11, NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
@@ -9617,8 +9617,9 @@ const char *g_class_map[] = {
|
||||
NULL,
|
||||
NULL,
|
||||
(const char *)0x10006040, "unserialize", "", (const char*)0, (const char*)0,
|
||||
"/**\n * ( excerpt from http://php.net/manual/en/function.unserialize.php )\n *\n *\n * @str string The serialized string.\n *\n * If the variable being unserialized is an object,\n * after successfully reconstructing the object PHP\n * will automatically attempt to call the __wakeup()\n * member function (if it exists).\n *\n * unserialize_callback_func directive\n *\n * It's possible to set a callback-function which will\n * be called, if an undefined class should be\n * instantiated during unserializing. (to prevent\n * getting an incomplete object\n * \"__PHP_Incomplete_Class\".) Use your php.ini,\n * ini_set() or .htaccess to define\n * 'unserialize_callback_func'. Everytime an undefined\n * class should be instantiated, it'll be called. To\n * disable this feature just empty this setting.\n *\n * @return mixed The converted value is returned, and can be a\n * boolean, integer, float, string, array or object.\n *\n * In case the passed string is not unserializeable,\n * FALSE is returned and E_NOTICE is issued.\n */",
|
||||
"/**\n * ( excerpt from http://php.net/manual/en/function.unserialize.php )\n *\n *\n * @str string The serialized string.\n *\n * If the variable being unserialized is an object,\n * after successfully reconstructing the object PHP\n * will automatically attempt to call the __wakeup()\n * member function (if it exists).\n *\n * unserialize_callback_func directive\n *\n * It's possible to set a callback-function which will\n * be called, if an undefined class should be\n * instantiated during unserializing. (to prevent\n * getting an incomplete object\n * \"__PHP_Incomplete_Class\".) Use your php.ini,\n * ini_set() or .htaccess to define\n * 'unserialize_callback_func'. Everytime an undefined\n * class should be instantiated, it'll be called. To\n * disable this feature just empty this setting.\n * @class_whitelist\n * vector The array of the class names that are authorized to\n * be unserialized. The array is default to be empty,\n * which means no class is allowed. If the class name\n * is a super class, all its subclasses are also\n * allowed. Note that primitive types and Serializable\n * classes are not subject to this whitelist. See\n * http://fburl.com/SafeSerializable for the reason why\n * we need this.\n *\n * @return mixed The converted value is returned, and can be a\n * boolean, integer, float, string, array or object.\n *\n * In case the passed string is not unserializeable,\n * FALSE is returned and E_NOTICE is issued.\n */",
|
||||
(const char *)0xffffffff /* KindOfUnknown: $t: Variant */, (const char *)0x2000, "str", "", (const char *)0x14 /* KindOfString */, "", (const char *)0, "", (const char *)0, NULL,
|
||||
(const char *)0x2000, "class_whitelist", "", (const char *)0x20 /* KindOfArray */, "a:0:{}", (const char *)6, "empty_array", (const char *)11, NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
|
||||
@@ -10,8 +10,88 @@ class B extends A {
|
||||
public $b = 0;
|
||||
}
|
||||
|
||||
$a = new B;
|
||||
$b = serialize($a);
|
||||
var_dump($b);
|
||||
$c = unserialize($b);
|
||||
var_dump($c);
|
||||
class C {
|
||||
public $a, $b, $c;
|
||||
function __construct() {
|
||||
$this->a = null;
|
||||
$this->b = acos(1.01);
|
||||
$this->c = log(0);
|
||||
echo "C has a safe constructor.\n";
|
||||
}
|
||||
function __destruct() {
|
||||
echo "C has a safe destrcutor.\n";
|
||||
}
|
||||
function __wakeup() {
|
||||
echo "C wakes up safely.\n";
|
||||
}
|
||||
function __sleep() {
|
||||
echo "C sleeps safely.\n";
|
||||
return array('a', 'b', 'c');
|
||||
}
|
||||
}
|
||||
|
||||
class DangerousClass {
|
||||
public $danger = "DangerousString";
|
||||
function __construct() {
|
||||
echo "I have dangerous constructor.\n";
|
||||
}
|
||||
function __destruct() {
|
||||
echo "I have dangerous destructor.\n";
|
||||
}
|
||||
function __wakeup() {
|
||||
echo "I wake up dangerously.\n";
|
||||
}
|
||||
function __sleep() {
|
||||
echo "I sleep dangerously.\n";
|
||||
return array('danger');
|
||||
}
|
||||
}
|
||||
|
||||
class E {
|
||||
public $dangerousClass;
|
||||
function __construct() {
|
||||
$this->dangerousClass = new DangerousClass;
|
||||
}
|
||||
}
|
||||
|
||||
class F implements Serializable {
|
||||
public function serialize() {
|
||||
return "SerializedData";
|
||||
}
|
||||
public function unserialize($serialized) {
|
||||
echo $serialized;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
class G extends DangerousClass {
|
||||
}
|
||||
|
||||
function test_serialization($obj, $class_whitelist) {
|
||||
$str = serialize($obj);
|
||||
var_dump($str);
|
||||
$new_obj = unserialize($str, $class_whitelist);
|
||||
var_dump($new_obj);
|
||||
unset($obj);
|
||||
unset($new_obj);
|
||||
echo "========================\n";
|
||||
}
|
||||
|
||||
function main(int $argc, array $argv) {
|
||||
// null will be autmatically translated into empty array (see idl definition)
|
||||
// So it should still not allow any class.
|
||||
test_serialization(new A, null);
|
||||
test_serialization(new B, array('A'));
|
||||
test_serialization(new C, array('C'));
|
||||
test_serialization(new DangerousClass, array());
|
||||
test_serialization(new E, array('E'));
|
||||
test_serialization(new F, array());
|
||||
test_serialization(new G, array('G'));
|
||||
test_serialization(array("Hello World<>$%", acos(1.01), log(0), 50), array());
|
||||
test_serialization(
|
||||
array( new A, array(new B, array(new C, array(new E, array(new F))))),
|
||||
array('abc' => 'A', 5 => 'C', 'E')
|
||||
);
|
||||
}
|
||||
|
||||
exit(main($argc, $argv));
|
||||
|
||||
Arquivo binário não exibido.
Arquivo binário não exibido.
@@ -0,0 +1 @@
|
||||
-vServer.UnserializationWhitelistCheck=true -vServer.UnserializationWhitelistCheckWarningOnly=true
|
||||
Referência em uma Nova Issue
Bloquear um usuário