Open7
Jarファイルから安全にクラスをロードしたい
Jarから安全にクラスをロードしたいと思ったがSecurityManagerは削除予定だそうだ。(今更)
に、SecurityManager廃止後のセキュリティについて書いてあるのでこれを読んでみる。ASMを依存関係に追加し、
ClassVisitor
を継承したクラスを実装する。
class SecureClassChecker : ClassVisitor(ASM5) {
var native = false
private set
var file = false
private set
override fun visitMethod(
access: Int, name: String?, descriptor: String?, signature: String?, exceptions: Array<out String>?
): MethodVisitor {
//メゾットの定義と中身を確認
//例 ネイティブな呼び出しをチェック
if (access and ACC_NATIVE > 0){
native = true
}
return object : MethodVisitor(ASM5) {
override fun visitMethodInsn(
opcode: Int, owner: String, name: String?, descriptor: String?, isInterface: Boolean
) {
//メゾットの呼び出しを確認
//例 java.io.Fileのメゾットの使用をチェック
if (owner == Type.getInternalName(File::class.java)){
file = true
}
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
}
}
}
}
フィールドにはvisitField
、visitFieldInsn
などが使用できる。詳しくはClassVisitor (ASM 9.4)を参考にして頂きたい。
続いてClassLoaderを実装する。
class CustomJarClassLoader(private val jarFile: File, parent: ClassLoader = getSystemClassLoader()) : ClassLoader(parent) {
@Throws(ClassNotFoundException::class)
override fun findClass(name: String): Class<*> {
try {
val path = name.replace('.', '/') + ".class";
val jarInputStream = URL("jar:" + jarFile.toURI().toURL() + "!/" + path).openStream()
val allClassBytes: ByteArray = jarInputStream.readAllBytes()
val classReader = ClassReader(allClassBytes)
val secureClassChecker = SecureClassChecker()
// クラスにデバッグ情報がある場合、それを無視する
classReader.accept(
secureClassChecker, ClassReader.SKIP_DEBUG
)
if (secureClassChecker.file||secureClassChecker.native) {
throw ClassNotFoundException(
"Class cannot be loaded - contains illegal code"
)
} else {
return defineClass(
null, allClassBytes, 0,
allClassBytes.size
)
}
} catch (e: IOException) {
throw ClassNotFoundException(
"Error finding and opening class", e
)
}
}
}
この場合、読み込んだコードにネイティブメゾット又はjava.io.File
の関数を呼び出しているものがあった場合、ClassNotFoundException
でクラスのロードに失敗する。
val clazz = CustomJarClassLoader(File("Plugin.jar")).loadClass("Main")
val instance = clazz.getConstructor().newInstance()
のようにして使うことができる。
ただ、これだけで安全と言えるかどうか怪しいので他の方法も探していく