ABAP

CLASSzcl_jerry_singletonDEFINITIONPUBLICFINALCREATEPRIVATE.PUBLICSECTION.INTERFACESif_serializable_object.CLASS-METHODSclass_constructor.CLASS-METHODSget_instanceRETURNINGVALUE(ro_instance)TYPEREFTOzcl_jerry_singleton.PROTECTEDSECTION.PRIVATESECTION.CLASS-DATAso_instanceTYPEREFTOzcl_jerry_singleton.DATAmv_nameTYPEstring.DATAmv_initializedTYPEabap_bool.METHODSconstructor.ENDCLASS.CLASSZCL_JERRY_SINGLETONIMPLEMENTATION.*<SIGNATURE>---------------------------------------------------------------------------------------+*|StaticPublicMethodZCL_JERRY_SINGLETON=>CLASS_CONSTRUCTOR*+-------------------------------------------------------------------------------------------------+*+--------------------------------------------------------------------------------------</SIGNATURE>METHODclass_constructor.so_instance=NEWzcl_jerry_singleton().ENDMETHOD.*<SIGNATURE>---------------------------------------------------------------------------------------+*|InstancePublicMethodZCL_JERRY_SINGLETON->CONSTRUCTOR*+-------------------------------------------------------------------------------------------------+*+--------------------------------------------------------------------------------------</SIGNATURE>METHODconstructor.mv_name='Jerry'.IFmv_initialized=abap_false.mv_initialized=abap_true.ELSE.MESSAGE'youareintrouble!'TYPE'E'DISPLAYLIKE'I'.ENDIF.ENDMETHOD.*<SIGNATURE>---------------------------------------------------------------------------------------+*|StaticPublicMethodZCL_JERRY_SINGLETON=>GET_INSTANCE*+-------------------------------------------------------------------------------------------------+*|[<-()]RO_INSTANCETYPEREFTOZCL_JERRY_SINGLETON*+--------------------------------------------------------------------------------------</SIGNATURE>METHODget_instance.ro_instance=so_instance.ENDMETHOD.ENDCLASS.

通过序列化/反序列化攻击单例模式:

DATA(lo_instance)=zcl_jerry_singleton=>get_instance().DATA:sTYPEstring.CALLTRANSFORMATIONidSOURCEmodel=lo_instanceRESULTXMLs.DATA:lo_instance2TYPEREFTOzcl_jerry_singleton.CALLTRANSFORMATIONidSOURCEXMLsRESULTmodel=lo_instance2.

绕过了单例的限制,构造了第二个实例。

Java

除了用序列化/反序列化攻击外,还可以用反射攻击。

然而我只需要将这个单例类JerrySingleton的构造函数通过反射设置成可以访问Accessible,然后就能通过反射调用该构造函数,进而生成新的对象实例。这样就破坏了单例模式。

第6行代码会打印false。

针对这种攻击,一种可行的防御措施是在单例类的构造函数内定义一个布尔变量,初始化为false。当构造函数执行后,该变量被置为true。如果接下来构造函数再次被执行,则人为抛出异常,避免构造函数重复执行。

这种防御措施无法从根本上杜绝Singleton被攻击,因为攻击者仍旧可以通过反射来修改布尔变量flag的值,从而绕过这个检查。

最理想的不会受到攻击的单例模式实现是借助Java里枚举类Enumeration的特性:

这种实现类型的单例模式的消费代码:

System.out.println("Name:" + JerrySingletonAnotherApproach.INSTANCE.getName());

如果攻击者通过前面介绍的反射代码对这种实现方式的单例进行攻击,JDK会抛出NoSuchMethodException异常:

究其原因,是因为现在我们是通过Java枚举方式实现的单例,枚举类没有传统意义上的构造函数,因此对这种反射攻击免疫。

要获取更多Jerry的原创文章,请关注公众号"汪子熙":