Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What security issues come from calling methods with reflection?

Tags:

java

json

I'm working on a project that has hosts and clients, and where hosts can send commands to clients (via sockets).

I'm determined that using JSON to communicate works the best.

For example:

{
    "method" : "toasty",
    "params" : ["hello world", true]
}

In this example, when this JSON string is sent to the client, it will be processed and a suitable method within the client will be run as such:

public abstract class ClientProcessor {

    public abstract void toasty(String s, boolean bool);
    public abstract void shutdown(int timer);

    private Method[] methods = getClass().getDeclaredMethods();

    public void process(String data) {
        try {
            JSONObject json = new JSONObject(data);
            String methodName = (String) json.get("method");

            if (methodName.equals("process"))
                return;

            for (int i = 0; i < methods.length; i++)
                if (methods[i].getName().equals(methodName)) {
                    JSONArray arr = json.getJSONArray("params");

                    int length = arr.length();
                    Object[] args = new Object[length];
                    for (int i2 = 0; i2 < length; i2++)
                        args[i2] = arr.get(i2);

                    methods[i].invoke(this, args);
                    return;
                }
        } catch (Exception e) {}
    }
}

And using the ClientProcessor:

public class Client extends ClientProcessor {
    @Override
    public void toasty(String s, boolean bool) {
        //make toast here
    }

    @Override
    public void shutdown(int timer) {
        //shutdown system within timer
    }

    public void processJSON(String json) {
        process(json);
    }
}

The JSON is sent by the server to the client, but the server could be modified to send different JSONs.

My questions are:

  • Is this a safe way of running methods by processing JSON?
  • Is there a better way to do this? I'm thinking that using reflection is terribly slow.
like image 581
Dave Chen Avatar asked Aug 10 '15 07:08

Dave Chen


1 Answers

There's a 100 and 1 ways you can process a JSON message so that some processing occurs, but they'll all boil down to:

  • parse message
  • map message to method
  • invoke method
  • send response

While you could use a reflective call (performance-wise it would be fine for most cases) to invoke a method, that, imho, would be a little too open - a malicious client could for example crash your system by issuing wait calls.

Reflection also opens you up to having to correctly map the parameters, which is more complicated than the code you've shown in your question.

So don't use Reflection.

Would you could do is define a simple interface, implementations of which would understand how to process the parameters and have your processor (more commonly referred to as a Controller) invoke that, something like this:

public interface ServiceCall
{
  public JsonObject invoke(JsonArray params) throws ServiceCallException;
}

public class ServiceProcessor
{
  private static final Map<String, ServiceCall> SERVICE_CALLS = new HashMap<>();

  static
  {
    SERVICE_CALLS.put("toasty", new ToastCall());
  }

  public String process(String messageStr) 
  {
    try 
    {
      JsonObject message = Json.createReader(new StringReader(messageStr)).readObject();

      if (message.containsKey("method"))
      {
        String method = message.getString("method");

        ServiceCall serviceCall = SERVICE_CALLS.get(method);

        if (serviceCall != null)
        {
          return serviceCall.invoke(message.getJsonArray("params")).toString();
        }
        else
        {
          return fail("Unknown method: " + method);
        }
      }
      else
      {
        return fail("Invalid message: no method specified");
      }
    } 
    catch (Exception e) 
    {
      return fail(e.message);
    }
  }

  private String fail(String message)
  {
    return Json.createObjectBuilder()
      .add("status", "failed")
      .add("message", message)
      .build()
      .toString();
  }      

  private static class ToastCall implements ServiceCall
  {
    public JsonObject invoke(JsonArray params) throws ServiceCallException  
    {
      //make toast here
    }
  }
}
like image 194
Nick Holt Avatar answered Oct 07 '22 11:10

Nick Holt