【问题标题】:How to protect Java variables and methods on server using REST如何使用 REST 保护服务器上的 Java 变量和方法
【发布时间】:2017-03-18 20:37:49
【问题描述】:

我正在使用 JavaFX 和 Java Spark 开发桌面软件,这基本上是用于开发 Web 应用程序的准系统框架,但我正在尝试严格使用它来放置/获取敏感的方法和变量在服务器上,以便用户无法访问它们。 REST 似乎是正确的方法,但我很难理解 2 个相互关联的 REST 概念。 Spark 文档很简单,但我已将几个好的教程集成到下面的工作演示中,但我碰壁了。我简单解释一下:

Postman 的帮助下,我已经能够使用http://localhost:4567/secrets 的路径和以下的正文将put 一些记录放到服务器上:

{
      "title" : "demofield1",
    "content" : "12345"
}

每条记录都包含一个标题作为标识符 (demofield1) 和内容作为敏感数据,应始终对用户隐藏 (12345)。将put 这些字符串放到服务器上然后get 使用title 作为参数,这非常简单,如下所示。演示代码只有一个用于创建和返回记录(秘密)的 Model 类、一个 JSON 转换方法以及一个 getput Spark 方法。秘密暂时存储在本地的 HashMap 中,我假设真正的应用程序会简单地交换服务器数据库。

get 方法按预期工作,返回正确的 JSON 记录并将内容存储为具有以下行的字符串:String secretString = model.getCertainSecret(title).getContent();

话虽如此......

问题(部分答案也非常感谢):

  1. secretString 上面现在包含一个机密值 (12345),它是使用所谓的安全 REST 方法获得的。但是用户不能简单地对我的源代码进行逆向工程并编写System.out.println(secretString) 并显示那个 12345 吗?尽管没有明确显示,但我不明白从服务器检索简单字符串后如何保护它。代码似乎正确,但值很容易获得。我错过了什么?

  2. 你如何在服务器上put 整个 java 方法?我需要保护的很多代码不仅仅是字符串,而是包含TasksPlatform.runLater()-> 的方法,并且需要与其他桌面软件交互。例如,我的一种方法使用 JACOB 来识别第三方软件的某个区域何时被点击。我什至无法理解在这种情况下 get/put 会是什么样子。

我的假设是服务器端数据库会存储来自我的put 请求的所有内容,但我不明白它是如何存储和返回方法的?我应该阅读有关 servlet 或 SaaS 的内容吗?我专注于桌面用户。

代码:

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.Data;
import org.apache.log4j.BasicConfigurator;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import static spark.Spark.get;
import static spark.Spark.put;

public class ServerDemo
{
    private static final int HTTP_BAD_REQUEST = 400;

    @Data
    static class NewSecretPayload {
        private String title;
        private String content;

        public boolean isValid() {
            return title != null && !title.isEmpty();
        }
    }

    public static class Model {
        private int nextId = 1;
        private Map<String, Secret> secrets = new HashMap<>();
        @Data
        class Secret {
            private int id;
            private String title;
            private String content;
        }

        public int createSecret(String title, String content){
            int id = nextId++;
            Secret secret = new Secret();
            secret.setId(id);
            secret.setTitle(title);
            secret.setContent(content);
            secrets.put(title, secret);
            return id;
        }

        public Secret getCertainSecret(String titleToUse){
            if(null != secrets.get(titleToUse)){
                return secrets.get(titleToUse);
            }else{
                return null;
            }
        }
    }

    public static String dataToJson(Object data) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            mapper.enable(SerializationFeature.INDENT_OUTPUT);
            StringWriter sw = new StringWriter();
            mapper.writeValue(sw, data);
            return sw.toString();
        } catch (IOException e) {
            throw new RuntimeException("IOException from a StringWriter?");
        }
    }

    public static void main(String[] args) {
        Model model = new Model();
        BasicConfigurator.configure();
        put("/secrets", (request, response) -> {
            try {
                ObjectMapper mapper = new ObjectMapper();
                NewSecretPayload creation = mapper.readValue(request.body(), NewSecretPayload.class);
                if (!creation.isValid()) {
                    response.status(HTTP_BAD_REQUEST);
                    return "";
                }
                int id = model.createSecret(creation.getTitle(), creation.getContent());
                response.status(200);
                response.type("application/json");
                return id;
            } catch (JsonParseException jpe) {
                response.status(HTTP_BAD_REQUEST);
                return "";
            }
        });

        get("/secrets/:title", (req, res) -> {
            String title = req.params(":title");
            if (model.getCertainSecret(title) != null) {
                res.status(200);
                res.type("application/json");
                String secretString = model.getCertainSecret(title).getContent();
                return dataToJson(model.getCertainSecret(title));
            }
            res.status(400);
            return new ResponseError("No user with title "+title+" was found", title);
        });
    }
}

【问题讨论】:

  • 我对这个问题中的大多数假设感到困惑:是什么让您相信 JSON 响应中的任何内容对用户隐藏?或者你如何通过 JSON 发送方法? JSON 是一种数据格式,我不确定您打算如何将 Java 方法嵌入其中。
  • 我也不确定,很清楚:) 为了回答你的问题,我假设我最初会将我的所有敏感代码(方法、变量)put 到服务器上,然后分发我的应用程序。然后,用户将拥有软件的“外壳”,该软件在需要时通过get 方法从服务器请求某些代码。我错误地认为 JSON 隐藏了这个……如果 JSON 没有在请求时隐藏值,你如何正确地从服务器检索数据,使其永远不会暴露?整个方法都需要保护,并且只在需要时调用。这可能吗?谢谢。
  • 这根本不是网络通信的工作方式。可以读取通过发送的任何数据(在某些情况下可能会有点困难)。如果您不希望用户访问某些内容,那么您将无法发送它。你的整个方法对我来说似乎很奇怪。为什么要发送方法和变量?与将它们放在用户正在执行的二进制程序中相比,它应该提供什么优势?
  • 在我发布的另一个问题here 中,有 2 个值得注意的引语来自其他 SO 答案。“将服务的最关键部分移出应用程序,进入 Web 服务,隐藏在像 PHP 这样的服务器端语言。移动算法并让它在远程服务器上处理数据,然后使用应用程序简单地为其提供数据。”以及“设置一个服务器来响应来自您的应用程序的请求,“使用”资产,然后将结果发送回应用程序。”
  • 这些是我对我的方法的影响,我如何完成他们的建议?我想隐藏某些方法/变量的代码,因为它们可能对反编译它们的人非常有用。 ProGuard 还不够,所以我想将它们移到服务器端。我该如何做到这一点,以便它们的功能仍然可以像集成在源代码中一样使用?我很迷茫

标签: java json rest javafx server


【解决方案1】:

让我们深入了解您的第一个问题“保持字符串秘密”:--

  • 限制:最简单的方法是不向恶意用户提供数据。
  • 屏蔽:屏蔽您提供给最终用户的数据。您将原始数据映射到屏蔽数据。您将向最终用户提供屏蔽数据。在这里,最终用户永远无法获得原始数据,因为这是一个单向过程。当最终用户发送屏蔽数据时,您始终可以从中检索原始数据。
  • 加密:如果最终用户需要查看数据,您可以对其进行加密并发送。您可以在解密数据之前对代码进行完整性检查。完整性检查可以让您了解代码是否被修改过。如果代码未通过完整性检查,您可以随时退出该过程。

【讨论】:

    猜你喜欢
    • 2011-01-08
    • 1970-01-01
    • 2014-08-16
    • 2017-11-17
    • 2014-04-13
    • 2015-06-16
    • 2014-10-20
    • 2023-03-11
    • 2017-07-12
    相关资源
    最近更新 更多