英文:
Is limitation of accessibility for classes and their members a valid practice for more secure code?
问题
根据 Oracle安全编码准则 中的Guideline 4-1/EXTEND-1和Guideline 4-5/EXTEND-5,您应该限制类及其成员的可访问性,作为防范恶意攻击者进行恶意重写的安全控制。
> 设计类和方法以支持继承,或将它们声明为final。如果一个类或方法不是final的,那么它可以被恶意攻击者进行恶意重写。一个不允许派生类的类更容易实现并验证其安全性。
在现实世界的场景中,攻击者如何实际利用下面的不安全类?即使该类已在运行中的JVM中加载,他/她是否仍然可以这样做?
public class PasswordVerifier {
private String regex;
public PasswordVerifier(String regex) {
this.regex = regex;
}
public boolean isPasswordValid(String password) {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(password);
return matcher.find();
}
public String getRegex() {
return regex;
}
public void setRegex(String regex) {
this.regex = regex;
}
}
如果我们讨论的是一个能够访问源代码的恶意内部人员,他不是可以在第一时间移除final修饰符(如果之前存在的话),或以任何方式修改类本身吗?
英文:
According to Oracle Secure Coding Guideline Guideline 4-1/EXTEND-1 and Guideline 4-5/EXTEND-5
you should limit the accessibility of classes and their members as a security control against malicious override from an attacker.
> Design classes and methods for inheritance or declare them final. Left
> non-final, a class or method can be maliciously overridden by an
> attacker. A class that does not permit subclassing is easier to
> implement and verify that it is secure.
How could an attacker actually exploit the below insecure class in real world scenario?
Could he/she do it even if the class is already loaded in a running JVM?
public class PasswordVerifier {
private String regex;
public PasswordVerifier(String regex) {
this.regex = regex;
}
public boolean isPasswordValid(String password) {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(password);
return matcher.find();
}
public String getRegex() {
return regex;
}
public void setRegex(String regex) {
this.regex = regex;
}
}
If we are talking about a malicious insider who has access to the source code, couldn't he just remove the final modifier(if one was there already) or alter the class itself by any means in the first place?
答案1
得分: 0
代码不安全,无论是否有final
关键字。它还极大地取决于它运行的环境。
如果JVM已在运行:
要困难得多。我注意到一些通过C++进行注入的事情,这可能会为诸如在类加载时使用ASM进行运行时类生成/修改之类的事情打开可能性。这将需要将ASM作为依赖项包含在内,而大多数项目不会这样做。使用此方法,类是否为final都无关紧要。从那时起,攻击者只需修改isPasswordValid()
方法以始终返回true。
如果类已加载,我认为这是不可能的。在此处查看更多信息:
https://stackoverflow.com/questions/55286883/c-call-a-function-inside-a-running-jvm
这也提供了一个有趣的阅读:
https://stackoverflow.com/questions/1880929-is-code-injection-possible-in-java
如果代码被用作库:
要简单得多。攻击者可以像你说的那样删除final
修饰符,或者使用类似ASM的工具生成一个一开始就没有final
修饰符的新类。也可以使用sun.misc.Unsafe
,就像这个存储库中展示的那样这里。这将允许他们生成一个继承不再是final类的新类。
另一种方法是在加载类时使用ASM来修改类,这将更容易。
可能还有更多的方法,我实际上还没有测试过这些方法,但我认为在不同环境下都有可能实现。
编辑: 我忘了提到有些东西在较新版本的Java中可能无效,在那里类操作和依赖项访问受到了更严格的限制。我基于您使用的是Java 8进行所有这些操作,大多数人仍在使用Java 8。
编辑2: 我刚刚找到了这篇文章,其中提供了一种通过调试模式执行任意代码的方法,这可以用于操作类。链接
英文:
That code isn't safe, whether there's a final
keyword or not. It also depends wildly on the environment it's running in.
If the JVM is already running:
Much more difficult. I did notice some things with injection through C++, which could open up possibilities to things like runtime class generation/modification with ASM as the class is loading. This would require ASM to be included as a dependency, which most projects won't have. Using this method, it wouldn't matter if the class was final or not. From there, the attacker can just modify the isPasswordValid()
method to always return true.
If the class is already loaded, I don't think it's possible. See more here:
https://stackoverflow.com/questions/55286883/c-call-a-function-inside-a-running-jvm
This also provides an interesting read:
https://stackoverflow.com/questions/1880929/is-code-injection-possible-in-java
If the code is being used as a library:
Much easier. The attacker could either remove the final
modifier like you said, or generate a new class with something like ASM that doesn't have the final
modifier in the first place. One could also just use sun.misc.Unsafe
, as shown in this repo here. This would allow them to generate a new class that extends the no longer final class.
Another method is to just use ASM to modify the class as it's loading, which would be easier.
There are probably more ways of doing it, and I haven't actually tested any of these myself, but I think that it's possible in both situations depending on the environment.
EDIT: I forgot to mention that some of this stuff might not work in later versions of Java, where class manipulation and dependency access is much more restricted. I'm basing all of this off of you using Java 8, which most people still do.
EDIT 2:
I just found this article here which provides a method for executing arbitrary code through debug mode, which can be used to manipulate the class also. Link
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论