Java沙箱的实现

点击打开链接Java沙箱实现是重写类加载器和安全管理器,通过设置的全局安全管理器来控制执行程序的权限

 

说明: 安全策略只对安装安全管理器之后的类生效,之前的类不再此管理范围之内,利用这一点可以预先设置我们需要的操作,而对某个点之后的所有非法操作进行权限设置.

 

类加载器重写

[java]  view plain copy
  1. /** 
  2.  *  [重写的类加载器] 
  3.  *  沙箱程序类加载器,可根据指定路径加载制定类class文件. 
  4.  *   
  5.  *  [说明] 
  6.  *  仅包内可见 
  7.  *  
  8.  *  @author 刘金鑫 
  9.  *  @version 1.0 
  10.  * */  
  11. package org.hljoj.core.judge.sandbox;  
  12.   
  13. import java.io.File;  
  14. import java.io.FileInputStream;  
  15.   
  16. import org.hljoj.core.judge.util.ConstantParam;  
  17.   
  18. class SandboxClassLoader extends ClassLoader{  
  19.     /**默认classPath*/  
  20.     private String _classPath;  
  21.       
  22.     /** 
  23.      *  构造函数 
  24.      * @param classPath 类加载器默认classPath 
  25.      * */  
  26.     public SandboxClassLoader(String classPath) {  
  27.         this._classPath = classPath;  
  28.     }  
  29.   
  30.     @Override  
  31.     protected Class<?> findClass(String className) throws ClassNotFoundException {  
  32.        return loadClass(_classPath, className);  
  33.     }  
  34.       
  35.     /** 
  36.      *  更改类加载器加载类的classpath,在制定路径下加载制定的类class文件 
  37.      * @param       classPath   要加载的类路径 
  38.      * @param       className   要加载的类名 
  39.      *              最为限定,只能加载不含包的类. 
  40.      * */  
  41.     public Class<?> loadClass(String classPath, String className) throws ClassNotFoundException{  
  42.         if(className.indexOf('.') >= 0) {  
  43.             throw new ClassNotFoundException(className);  
  44.         }  
  45.           
  46.         File classFile = new File(classPath + ConstantParam.SEPARATOR + className + ".class");  
  47.         byte[] mainClass = new byte[(int) classFile.length()];  
  48.         try {  
  49.             FileInputStream in = new FileInputStream(classFile);  
  50.             in.read(mainClass);  
  51.             in.close();  
  52.         } catch (Exception e) {  
  53.             //e.printStackTrace();  
  54.             throw new ClassNotFoundException(className);  
  55.         }  
  56.           
  57.         return super.defineClass(className, mainClass, 0, mainClass.length);  
  58.     }  
  59.       
  60.     /** 
  61.      *  获取classPath 
  62.      * @return String       classPath 
  63.      * */  
  64.     public String getClassPath(){  
  65.         return _classPath + ConstantParam.SEPARATOR;  
  66.     }  
  67. }  

 

 

 

重写安全管理器

 

[java]  view plain copy
  1. /** 
  2.  *  [重写的安全管理器] 
  3.  *  安全管理器用来限制客户端提交的Java源程序运行的功能, 
  4.  * 对程序读/写本地文件系统,修改系统属性连接网络, 
  5.  * 数据库等一切可能对本地计算机系统造成危害的操作进行屏蔽, 
  6.  * 如有这些操作将抛出SecurityException异常,并终止程序执行. 
  7.  *  
  8.  * [说明]: 
  9.  * 仅包内可见 
  10.  * 不允许提交的源程序执行exit(n)函数-即不允许源程序中途 
  11.  *  终止虚拟机的运行,但是调用源代码端可执行exit(n)函数. 
  12.  *   
  13.  *  @author 刘金鑫 
  14.  *  @version 1.0 
  15.  * */  
  16.   
  17. package org.hljoj.core.judge.sandbox;  
  18.   
  19. import java.io.FilePermission;  
  20. import java.lang.reflect.ReflectPermission;  
  21. import java.security.Permission;  
  22. import java.security.SecurityPermission;  
  23. import java.util.PropertyPermission;  
  24.   
  25. import org.hljoj.core.judge.util.ConstantParam;  
  26.   
  27. class SandboxSecurityManager extends SecurityManager {  
  28.       
  29.     public static final int EXIT = ConstantParam.RANDOM.nextInt();  
  30.   
  31.     /** 
  32.      * 重写强行退出检测 
  33.      * 防止用户自行终止虚拟机的运行,但是调用程序端可以执行退出 
  34.      * */  
  35.     public void checkExit(int status) {  
  36.         if (status != EXIT)  
  37.          throw new SecurityException("Exit On Client Is Not Allowed!");  
  38.     }  
  39.   
  40.     /** 
  41.      *  策略权限查看 
  42.      * 当执行操作时调用,如果操作允许则返回,操作不允许抛出SecurityException 
  43.      * */  
  44.     private void sandboxCheck(Permission perm) throws SecurityException {  
  45.         // 设置只读属性  
  46.         if (perm instanceof SecurityPermission) {  
  47.             if (perm.getName().startsWith("getProperty")) {  
  48.                 return;  
  49.             }  
  50.         } else if (perm instanceof PropertyPermission) {  
  51.             if (perm.getActions().equals("read")) {  
  52.                 return;  
  53.             }  
  54.         } else if (perm instanceof FilePermission) {  
  55.             if (perm.getActions().equals("read")) {  
  56.                 return;  
  57.             }  
  58.         } else if (perm instanceof RuntimePermission || perm instanceof ReflectPermission){  
  59.             return;  
  60.         }  
  61.   
  62.         throw new SecurityException(perm.toString());  
  63.     }  
  64.       
  65.     @Override  
  66.     public void checkPermission(Permission perm) {  
  67.         this.sandboxCheck(perm);  
  68.     }  
  69.   
  70.     @Override  
  71.     public void checkPermission(Permission perm, Object context) {  
  72.         this.sandboxCheck(perm);  
  73.     }  
  74.   
  75. }  

 

 

沙箱操作

 

[java]  view plain copy
  1. /** 
  2.  *  [沙箱] 
  3.  *  模拟沙箱功能,限制执行的Java程序的所有可能对本地机器的危害. 
  4.  * 被执行的Java程序对于本机只有读属性,其他文件操作,更改系统属性, 
  5.  *  Socket网络连接,连接数据库等功能全部禁止. 
  6.  *  
  7.  * 沙箱与主模块之间使用Socket进行通信,完全独立于系统接收执行信 
  8.  * 息类(包含要执行程序的相关信息)后执行,执行完毕后返回给主模块 
  9.  * 结果类(包含执行结果的相关信息) 
  10.  *  
  11.  *  @author 刘金鑫 
  12.  *  @version 1.0 
  13.  * */  
  14.   
  15. package org.hljoj.core.judge.sandbox;  
  16.   
  17. import java.io.BufferedInputStream;  
  18. import java.io.BufferedOutputStream;  
  19. import java.io.ByteArrayInputStream;  
  20. import java.io.ByteArrayOutputStream;  
  21. import java.io.IOException;  
  22. import java.io.ObjectInputStream;  
  23. import java.io.ObjectOutputStream;  
  24. import java.io.PrintStream;  
  25. import java.lang.management.ManagementFactory;  
  26. import java.lang.management.MemoryMXBean;  
  27. import java.lang.reflect.InvocationTargetException;  
  28. import java.lang.reflect.Method;  
  29. import java.lang.reflect.Modifier;  
  30. import java.net.ServerSocket;  
  31. import java.net.Socket;  
  32. import java.util.Timer;  
  33. import java.util.TimerTask;  
  34. import java.util.concurrent.Callable;  
  35. import java.util.concurrent.FutureTask;  
  36.   
  37. import org.hljoj.core.judge.util.ConstantParam;  
  38. import org.hljoj.core.judge.util.JudgeResult;  
  39.   
  40. public final class Sandbox {  
  41.   
  42.     //用来收集线程的内存使用量  
  43.     private static MemoryMXBean _memoryBean = ManagementFactory.getMemoryMXBean();  
  44.       
  45.     //定向输出  
  46.     private static ByteArrayOutputStream _baos = new ByteArrayOutputStream(1024);  
  47.       
  48.     //Socket通信  
  49.     private static Socket _socket = null;  
  50.       
  51.     private static ServerSocket _serverSocket = null;  
  52.       
  53.     private static ObjectInputStream _inputStream = null;  
  54.       
  55.     private static ObjectOutputStream _outputStream = null;  
  56.       
  57.     //执行提交程序线程  
  58.     private static Thread _thread = null;  
  59.       
  60.     //系统提供的默认的classpath  
  61.     private static String _classPath = null;  
  62.       
  63.     private static long     _timeStart = 0;       
  64.     //程序运行时间  
  65.     private static long     _timeUsed = 0;  
  66.     private static int      _baseMemory = 0;  
  67.     //程序运行空间  
  68.     private static int      _memoryUsed = 0;  
  69.       
  70.     //测评结果  
  71.     private static String _result = null;  
  72.       
  73.     /** 
  74.      *  核心执行函数<br> 
  75.      * 用于执行指定Main.class的main函数.<br> 
  76.      * 根据线程ID可获取运行时间. 
  77.      * */  
  78.     private static String process(int runId, final int timeLimit) throws Exception {  
  79.         FutureTask<String> task = null;  
  80.         final Timer timer = new Timer(true);  
  81.   
  82.         try {  
  83.             final Method mainMethod = getMainMethod(runId);  
  84.   
  85.             task = new FutureTask<String>(new Callable<String>() {  
  86.                 public String call() throws Exception {  
  87.                     try {  
  88.                         _timeStart = System.currentTimeMillis();  
  89.                         //启动计时器  
  90.                         timer.schedule(getTimerTask(),timeLimit + 1);  
  91.                         // 执行main方法  
  92.                         mainMethod.invoke(nullnew Object[] { new String[0] });  
  93.                     } catch (InvocationTargetException e) {  
  94.                         Throwable targetException = e.getTargetException();  
  95.                         // 超过内存限制  
  96.                         if (targetException instanceof OutOfMemoryError)  
  97.                             setResult(JudgeResult.MEMORY_LIMIT_EXCEED);  
  98.                         // 非法操作处理  
  99.                         else if (targetException instanceof SecurityException || targetException instanceof ExceptionInInitializerError) {  
  100.                             setResult(JudgeResult.RESTRICT_FUNCTION);  
  101.                         // 运行超时  
  102.                         } else if (targetException instanceof InterruptedException) {  
  103.                             setResult(JudgeResult.TIME_LIMITE_EXCEED);  
  104.                         } else {  
  105.                         // 运行时错误处理  
  106.                             if (e.getCause().toString().contains("Output Limit Exceed"))  
  107.                                 setResult(JudgeResult.OUTPUT_LIMIT_EXCEED);  
  108.                             else  
  109.                                 setResult(JudgeResult.RUNTIME_ERROR, e.getCause().toString());  
  110.                         }  
  111.                           
  112.                         throw new RuntimeException("Runtime Exception");  
  113.                     } finally {  
  114.                         timer.cancel();  
  115.                     }  
  116.                     return _baos.toString();  
  117.                 }  
  118.             });  
  119.         } catch (Exception e) {  
  120.             throw new RuntimeException("Initalization Error");  
  121.         }  
  122.           
  123.         _baseMemory = (int) _memoryBean.getHeapMemoryUsage().getUsed();  
  124.         _thread = new Thread(task);  
  125.         _thread.start();  
  126.         return task.get();  
  127.     }  
  128.       
  129.     /** 
  130.      *  启动计时器,对执行程序进行时间限制.<br> 
  131.      * 若超时,则中断执行线程<br> 
  132.      * @return TimerTask 
  133.      * */  
  134.     private static TimerTask getTimerTask(){  
  135.         return new TimerTask(){  
  136.             public void run() {  
  137.                 if (_thread  != null)  
  138.                     _thread.interrupt();  
  139.             }  
  140.         };  
  141.     }  
  142.       
  143.     private static int _outputSize = 0;  
  144.     /** 
  145.      *  初始化 
  146.      * @param classPath 系统默认classPath路径 
  147.      *  @param  port        socket服务器监听端口 
  148.      * */  
  149.     private static void inita(String classPath, int port) throws Exception{  
  150.         _classPath = classPath;  
  151.           
  152.         _serverSocket = new ServerSocket(port);  
  153.         _socket = _serverSocket.accept();  
  154.           
  155.         _outputStream = new ObjectOutputStream(_socket.getOutputStream());  
  156.         _inputStream = new ObjectInputStream(_socket.getInputStream());  
  157.           
  158.         //重新定向输出流  
  159.         System.setOut(new PrintStream(new BufferedOutputStream(_baos) {  
  160.             public void write(byte[] b, int off, int len) throws IOException {  
  161.                 _outputSize += len - off;  
  162.                 try {  
  163.                     super.write(b, off, len);  
  164.                     if (_outputSize > ConstantParam.OUTPUT_LIMIT)  
  165.                         throw new RuntimeException("Output Limit Exceed" + _outputSize);  
  166.                 } catch (IOException e) {  
  167.                     if(e.getMessage().equals("Output Limit Exceed")){  
  168.                         throw e;  
  169.                     }  
  170.                 }  
  171.             }  
  172.         }));  
  173.     }     
  174.   
  175.     private static SandboxClassLoader _classLoader  = null;  
  176.     /** 
  177.      *  获取指定路径下Main.class类的main入口函数. 
  178.      * @param runId 指定类路径 
  179.      * @return Method 返回的main方法 
  180.      * */  
  181.     private static Method getMainMethod(int runId) throws Exception{  
  182.         _classLoader = new SandboxClassLoader(_classPath);  
  183.         Class<?> targetClass = _classLoader.loadClass(_classLoader.getClassPath() + runId, "Main");  
  184.           
  185.         Method mainMethod = null;  
  186.         mainMethod = targetClass.getMethod("main", String[].class);  
  187.           
  188.         if(!Modifier.isStatic(mainMethod.getModifiers()))   
  189.             throw new Exception("Method Of Main Is Not Static");  
  190.           
  191.         mainMethod.setAccessible(true);  
  192.         return mainMethod ;  
  193.     }  
  194.       
  195.     /** 
  196.      *  测评接口. 
  197.      * 运行接收到的Java程序. 
  198.      *  
  199.      * @param runId 执行id 
  200.      * @param problemId 提交问题id 
  201.      * @param submitId 提交用户id 
  202.      * @param timeLimit 时间限制 
  203.      * @param memoryLimit 空间限制 
  204.      * @param standardInput 程序标准输入字符串 
  205.      * @param standardOutput 程序标准输出字符串 
  206.      * */  
  207.     public static void run(int runId, int timeLimit, int memoryLimit,   
  208.                                                                                                     String standardInput, String standardOutput) {  
  209.         _timeUsed = 0;  
  210.         _memoryUsed = 0;  
  211.         _baos.reset();  
  212.         _outputSize = 0;  
  213.         setResult(JudgeResult.WRONG_ANSWER);  
  214.         //定向输入流  
  215.         System.setIn(new BufferedInputStream(new ByteArrayInputStream(standardInput.getBytes())));  
  216.           
  217.         String output = null;  
  218.           
  219.         try {  
  220.             // 必须在执行前对垃圾回收,否则不准确.  
  221.             System.gc();  
  222.             output = process(runId, timeLimit);  
  223.             // 将程序输出与标准输出作比较  
  224.             setResult(matchOutput(standardOutput.getBytes(), output.getBytes()));  
  225.             // 获取程序运行时间和空间  
  226.             _timeUsed = System.currentTimeMillis() - _timeStart;  
  227.             _memoryUsed = (int) ((_memoryBean.getHeapMemoryUsage().getUsed() - _baseMemory) / 1000);  
  228.         }  catch (Exception e) {  
  229.             if (e.getMessage().equals("Initalization Error")){  
  230.                 setResult(JudgeResult.WRONG_ANSWER);  
  231.             }  
  232.         }  
  233.           
  234.         if (_memoryUsed > memoryLimit)  
  235.             setResult(JudgeResult.MEMORY_LIMIT_EXCEED);  
  236.           
  237.         try {  
  238.             //向主模块返回执行结果  
  239.             sendResult(runId, (int)_timeUsed, _memoryUsed, _result);  
  240.         } catch (IOException e) {  
  241.             e.printStackTrace();  
  242.         }  
  243.     }  
  244.       
  245.     /** 
  246.      *  向主模块发送运行结果. 
  247.      *  
  248.      * @param runId 运行runId 
  249.      *  @param timeUsed 代码运行时间(MS) 
  250.      * @param    memoryUsed 代码运行空间(B) 
  251.      *  @param result    代码执行结果 
  252.      * */  
  253.     private static void sendResult(int runId,int timeUsed, int memoryUsed, String result) throws IOException{  
  254.         _outputStream.writeInt(runId);  
  255.         _outputStream.writeInt(timeUsed);  
  256.         _outputStream.writeInt(memoryUsed);  
  257.         _outputStream.writeUTF(result);  
  258.     }  
  259.       
  260.     /** 
  261.      *  接收运行参数 
  262.      *  
  263.      *  @param runId 运行runId 
  264.      *  @param timeLimit    限制代码运行时间(MS) 
  265.      * @param    memoryLimit    限制代码运行空间(B) 
  266.      * @param standardInput 标准输入 
  267.      * @param standardOutput 标准输出 
  268.      * */  
  269.     private static void receiveMsg() throws IOException{  
  270.         int runId = _inputStream.readInt();  
  271.         int timeLimit = _inputStream.readInt();  
  272.         int memoryLimit = _inputStream.readInt();   
  273.         String standardInput = _inputStream.readUTF();  
  274.         String standardOutput = _inputStream.readUTF();  
  275.           
  276.         run(runId, timeLimit, memoryLimit, standardInput, standardOutput);  
  277.     }  
  278.       
  279.     /** 
  280.      *  比较程序输出和标准输出,并返回比较结果.<br> 
  281.      *  标准返回1.Accepted 2.Wrong Answer 3.Presenting Error结果 
  282.      *  
  283.      *  @param standOutput 标准输出结果 
  284.      *  @param output 程序输出结果 
  285.      *  @return int 比较结果 
  286.      * */  
  287.     private static int matchOutput(byte[] standardOutput, byte[] output){  
  288.         int i = 0;  
  289.         int j = 0;  
  290.           
  291.         do{  
  292.             while (i < standardOutput.length && (standardOutput[i] == ConstantParam.SPACE || standardOutput[i] == '/t' ||   
  293.                                                                 standardOutput[i] == '/r' || standardOutput[i] == '/n'))  
  294.                     i++;  
  295.             while (j < output.length && (output[j] == ConstantParam.SPACE || output[j] == '/t' ||   
  296.                                                 output[j] == '/r' || output[j] == '/n'))  
  297.                     j++;  
  298.             if (i < standardOutput.length && j < output.length && standardOutput[i] != output[j])  
  299.                 return JudgeResult.WRONG_ANSWER;  
  300.   
  301.             i++;  
  302.             j++;  
  303.         }while(j <= i && i < standardOutput.length && j < output.length);  
  304.           
  305.         if (i != j)  
  306.             return JudgeResult.PRESENTING_ERROR;  
  307.           
  308.         return JudgeResult.ACCEPTED;  
  309.     }  
  310.       
  311.     /** 
  312.      *  设置测评结果. 
  313.      *  @param JudgeResult结果类型. 
  314.      * */  
  315.     private static void setResult(int resultType){  
  316.         _result = JudgeResult.toString(resultType);  
  317.     }  
  318.       
  319.     /** 
  320.      *  设置测评结果. 
  321.      *  @param JudgeResult结果类型. 
  322.      * @param remark 测评结果的备注. 
  323.      * */  
  324.     private static void setResult(int resultType, String remark){  
  325.         setResult(resultType);  
  326.           
  327.         if (remark.endsWith("StackOverflowError"))  
  328.             _result += "(" + JudgeResult.toString(JudgeResult.RUNTIME_STACK_OVERFLOW) + ")";  
  329.         else if (remark.endsWith("/ by zero"))  
  330.             _result += "(" + JudgeResult.toString(JudgeResult.RUNTIME_DIVIDE_BY_ZERO) + ")";  
  331.         else if (remark.contains("ArrayIndexOutOfBoundsException"))  
  332.             _result += "(" + JudgeResult.toString(JudgeResult.RUNTIME_ACCESS_VIOLATION) + ")";  
  333.         else  
  334.             _result += "(" + JudgeResult.toString(JudgeResult.RUNTIME_ARRAY_BOUNDS_EXCEEDED) + ")";  
  335.     }  
  336.       
  337.     /** 
  338.      *  关闭网络连接 
  339.      * */  
  340.     private static void close(){  
  341.         try {  
  342.             if (_inputStream != null)  
  343.                 _inputStream.close();  
  344.             if (_outputStream != null)  
  345.                 _outputStream.close();  
  346.             if (_socket != null)  
  347.                 _socket.close();  
  348.         } catch (IOException e) {  
  349.             e.printStackTrace();  
  350.         }  
  351.     }  
  352.       
  353.     /** 
  354.      *  沙盒入口 
  355.      * 传入参数 : <br> 
  356.      *          classPath -- args[0] ------ 保存class的classpath<br> 
  357.      *          port          -- args[1] ------- 监听端口 
  358.      * */  
  359.     public static void main(String[] args) throws Exception{  
  360.         inita(args[0], Integer.parseInt(args[1]));  
  361.           
  362.         SecurityManager security = System.getSecurityManager();  
  363.         if (security == null)  
  364.             System.setSecurityManager(new SandboxSecurityManager());  
  365.           
  366.         while (!_socket.isClosed()){  
  367.             receiveMsg();  
  368.         }  
  369.           
  370.         close();  
  371.     }  

  372.  

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值