如何防止請求的URL被篡改

Web項目聚集地

圖文教程,技術交流

如圖,是我們模擬的一個從瀏覽器發送給服務器端的轉賬請求。久一的ID是 web_resource,正在操作100元的轉賬。

再如圖,因爲是通過瀏覽器 `url` 訪問服務,這個時候金額被篡改成了 200,那麼服務器接受到了200,直接扣除了200怎麼解決?這就是本文要講解的內容。

防止url被篡改的方式有很多種,本文就講述最簡單的一種,通過 secret 加密驗證。

道理很簡單,服務器接收到了 price 和 id,如果有辦法校驗一下他們是否被修改過不就就可以了嗎?

那麼我們傳遞的時候增加一個參數,叫做sign,sign是使用用戶不可見的一個secret和price、id組合加密獲得,然後傳遞給服務器端。當服務器端接收到請求的時候,獲取到price、id,通過同樣的secret加密和sign比較如果相同就通過校驗,不同則被篡改過。

那麼問題來了,如果參數特別多怎麼辦?

所以通用的做法是,把所有需要防止篡改的參數按照字母正序排序,然後順序拼接到一起,再和secret組合加密得到 sign。具體的做法可以參照如下。

  public static String generateSign(Map<String, String> parameters) {
    try {
        List<String> names = new ArrayList<>();
        parameters.forEach((k, v) -> {
            if (v != null && !Objects.equals(v, "") 
                && !Objects.equals(k, "sign")) {
                names.add(k);
            }
        });

        List sortedNames = names.stream().sorted()
            .collect(Collectors.toList());
        StringBuffer sb = new StringBuffer();
        sortedNames.forEach(n -> 
            sb.append(String.format("%s=%s", n, parameters.get(n))));
        String sign = md5(sb.toString());
        return sign;
    } catch (Exception e) {
        return "";
    }
  }

  private static String md5(String inputString)
       throws NoSuchAlgorithmException, UnsupportedEncodingException {

    MessageDigest md = MessageDigest.getInstance("MD5");
    md.update(inputString.getBytes("UTF-8"));

    byte[] digest = md.digest();

    return convertByteToHex(digest);
  }

  private static String convertByteToHex(byte[] byteData) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < byteData.length; i++) {
        sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16)
        .substring(1));
    }
    return sb.toString();
  }

generateSign 就是所有需要加密的參數,包括secret

有的同學擔心,那麼他萬一猜到了我的加密算法怎麼辦,這個不用擔心,你的secret是保持在服務器端的,不會暴漏出去的,所以他知道了算法也不會知道具體加密的內容。

那麼問題又來了,如果小明通過抓包工具獲取到了URL,他是不是可以無限制的訪問這個地址呢?那就出現了“久一”的錢被一百一百的轉空了。

那可怎麼辦?這裏涉及到了另一個話題,接口的冪等,我們後面會詳細講解怎麼通過冪等控制重複扣款。這裏我們要講解的是怎麼控制 URL 失效。

這裏又有一個通用的做法,就是再添加一個參數 timestamp。對的,就是當前的時間戳。服務器獲取到 timestamp 以後檢驗一下是否在5分鐘以內,如果不是直接返回請求失效就可以了?那麼如果timestamp 被篡改了呢?不會的,因爲我們按照上面的做法同樣對 timestamp 做了加密防止篡改。

最簡單的校驗接口被篡改的方式,你學會了嗎?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章