Clojure中的安全管理器 [英] Security Manager in Clojure
问题描述
我试着从Clojure程序安装一个安全管理器,并且在一个好的开始后卡住。这个对我来说在Clojures简单的REPL, Lein REPL,以及在Emacs的Cider REPL中。它禁止使用(System / exit n)。
(def SM(proxy [SecurityManager] []
(checkPermission
[^ java.security.Permission p]
(when(.startsWith(.getName p)exitVM)
(throw(SecurityException。exit)))))) )
(System / setSecurityManager SM)
但我想这样做安全经理做更多。例如。记录任何访问。令人惊讶的是,许多Clojure函数似乎调用安全管理器,因此,当在其内部使用时,给出一个无限循环,也称为StackOverflowException。
我失败的尝试:
(def security-log(atom()))
(def SM(proxy [SecurityManager]
(checkPermission
([^ java.security.Permission p]
(let [now(.toString(System / currentTimeMillis))
log(str[now] [cp / 1] SM要求p]
(swap!security-log conj))
(当.startsWith(.getName p)exitVM)
(throw(SecurityException。exit))))
([^ java.security.Permission p ^ Object o]
(let [now(.toString(System / currentTimeMillis))
log(str
[now]
[cp / 2] SM被要求输入pono)]
security-log conj log))))))
(System / setSecurityManager SM)
$ b
这两个问题都没有成功,因此我有两个问题。
- 我做错了什么?
- 有一个系统的(和简单的)方式,我可以找到哪些功能,
解决方案添加<$ c $后, c>(println(.getName p))到第一个
checkPermission()
实现,我发现导致循环的权限是createClassLoader
和suppressAccessChecks
(请参阅此处有关可用权限的详细列表)。当评估REPL中的单个表达式时,我得到以下结果:createClassLoader
createClassLoader
createClassLoader
suppressAccessChecks
Clojure创建一个新的
DynamicClassLoader
通过RT.makeClassLoader()
用于clojure.lang.Compiler
类的以下方法:eval
,compile1
和load
。当在REPL上工作时,每个表达式都是eval
ed,以便触发createClassLoader
权限和suppressAccessChecks
由使用反射触发,这在Clojure中很常见。
仍然我无法确定如何生成循环,所以我添加了一个
(Thread / dumpStack)
名称打印后获得 此 作为输出。 gist只表示它第一次开始循环。循环的原因是,当执行反射时,似乎有一个优化(至少在 sun.reflect.DelegatingClassLoader 的初始化,因此许可权限是Oracle的JVM 和OpenJDK)createClassLoader
被检查,(。toString ,,,)
的反映完成,等等...这是有意义的,因为前一次表达式
eval
'ed在一个新的REPL循环不会出现,只有经过几次评估相同的表达式循环开始,这是优化时启动。
我发现的解决方法涉及使用Clojure的东西只有当权限不是
suppressAccessChecks
或createClassLoader
。只过滤一个或另一个导致循环出现,我很确定它可能与相同的问题相关,但我没有检查在这些情况下的堆栈跟踪。(def SM(proxy [SecurityManager] []
pre>
(checkPermission
([^ java.security.Permission p]
;;没有反射或ClassLoader创建
;;涉及使用Clojure代码。
(when-not(#{suppressAccessCheckscreateClassLoader}(.getName p))
(let [now toString(System / currentTimeMillis))
log(str[now][cp / 1] SM要求p]
(swap!security-log conj日志)
(when(.startsWith(.getName p)exitVM)
(throw(SecurityException。exit))))))))
EDIT
是避免在
checkPermission
方法中包含的代码中的反射,这可以通过在(。toString ,, ,)
方法调用!(def SM(proxy [SecurityManager] []
(checkPermission
([^ java.security.Permission p]
(let [now(.toString ^ Object(System / currentTimeMillis))
log(str[now] [cp / 1] SM要求p]
(swap!安全日志conj))
(when(.startsWith(.getName p)exitVM)
(throw(SecurityException。exit)))))))
I try to install a security manager from a Clojure program, and am stuck short after a good start.
This one works for me in Clojures simple REPL, in the Lein REPL, and in the Cider REPL in Emacs. It prevents the use of (System/exit n).
(def SM (proxy [SecurityManager] [] (checkPermission [^java.security.Permission p] (when (.startsWith (.getName p) "exitVM") (throw (SecurityException. "exit")))))) (System/setSecurityManager SM)
But I want to do this security manager to do more. E.g. log any access. Surprisingly many Clojure functions seem to call the security manager, and thus, when used inside of it, give an infinite loop, aka StackOverflowException.
My failed try:
(def security-log (atom ())) (def SM (proxy [SecurityManager] [] (checkPermission ([^java.security.Permission p] (let [now (.toString (System/currentTimeMillis)) log (str "[" now "] " "[cp/1] SM is asked for " p)] (swap! security-log conj log)) (when (.startsWith (.getName p) "exitVM") (throw (SecurityException. "exit")))) ([^java.security.Permission p ^Object o] (let [now (.toString (System/currentTimeMillis)) log (str "[" now "] " "[cp/2] SM is asked for " p " on " o)] (swap! security-log conj log)))))) (System/setSecurityManager SM)
Both failed, putting the log into an atom, and just printing it.
So I have two questions.
- Am I doing something wrong?
- Is there a systematic (and easy) way I can find out which functions I can use inside of a security manager, without putting it into a loop?
解决方案After adding a
(println (.getName p))
to the firstcheckPermission()
implementation, I found that the permissions that cause the loop arecreateClassLoader
andsuppressAccessChecks
(see here for a detailed list on the permissions available). When evaluating a single expression in the REPL I got the following:createClassLoader createClassLoader createClassLoader suppressAccessChecks
Clojure creates a new
DynamicClassLoader
throughRT.makeClassLoader()
which is used in the following methods of theclojure.lang.Compiler
class:eval
,compile1
andload
. When working on the REPL every expression iseval
ed so that triggers thecreateClassLoader
permission and thesuppressAccessChecks
are triggered by the use of reflection, which is common in Clojure.Still I couldn't figure out exactly how the loop was being generated so I added a
(Thread/dumpStack)
after the name printing and got this as the output. The gist represents only the first time it starts looping. The cause of the loop is that when performing reflection there seems to be an optimization (at least in Oracle's JVM and OpenJDK) where the initialization of asun.reflect.DelegatingClassLoader
is involved, so the permissioncreateClassLoader
is checked, the reflection for(.toString ,,,)
is done and so on...This makes sense since the first few times an expression is
eval
'ed in a fresh REPL the loop does not appear, only after a few times of evaluating the same expression the loop starts, which is when the optimization kicks in.The workaround I found involves using Clojure stuff only when the permission is not
suppressAccessChecks
orcreateClassLoader
. Filtering only one or the other causes the loop to appear, I'm pretty sure it might be related to the same issue but I didn't check the stack traces in those cases.(def SM (proxy [SecurityManager] [] (checkPermission ([^java.security.Permission p] ;; When there's no Reflection or ClassLoader creation ;; involved use Clojure code. (when-not (#{"suppressAccessChecks" "createClassLoader"} (.getName p)) (let [now (.toString (System/currentTimeMillis)) log (str "[" now "] " "[cp/1] SM is asked for " p)] (swap! security-log conj log))) (when (.startsWith (.getName p) "exitVM") (throw (SecurityException. "exit")))))))
EDIT
A much better solution is to avoid reflection in the code you include in the
checkPermission
method, which can be accomplished by just adding a type hint in the(.toString ,,,)
method call!(def SM (proxy [SecurityManager] [] (checkPermission ([^java.security.Permission p] (let [now (.toString ^Object (System/currentTimeMillis)) log (str "[" now "] " "[cp/1] SM is asked for " p)] (swap! security-log conj log)) (when (.startsWith (.getName p) "exitVM") (throw (SecurityException. "exit")))))))
这篇关于Clojure中的安全管理器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!