franco
franco

Reputation: 2047

Encrypt URL Query Parameters

I work with liferay 5.2 ans extjs 3.4

I have a problem in the parameter passing in the URL I have reports that are generated through parameters passed in the url but the problem is that we can make a manual change in this parameters so in this time another report will be generated which is prohibited. my goal is to find a solution to encrypt this parameters and in java class we will decrypt these parameters to generate the reports. So in this case the parameters remain the same even make a manual change in the url. So we will make the encryption in the time of calling this url (in javascript ) and decryption in java to read the parameters. but I think that we should make an unique key in both encrypted and decrypted the parameters

this is my code :

in javascript :

var numDec = numDec_decsion.getValue();     

var yearDec = yearCorresp_decsion.getValue();       

    var url = "<c:url value='/printer'/>?method=genreport&numDec="+ numDec + 
                        "&yearDec=" + yearDec ;
                window.open(url);

this is the java code :

public void createReport(HttpServletRequest request,
    HttpServletResponse response) throws Exception {

    try {
        ServletContext context = request.getSession().getServletContext();
        String contextPath = context.getRealPath("/");
        contextPath += ((contextPath.endsWith("/") ||
            contextPath.endsWith("\\")) ? "" : "/");
        String root_dir = contextPath + "WEB-INF\\Template\\report\\";
        String reportFile = root_dir + request.getParameter("report") +
            ".jasper";
        ApplicationContext appContext = WebApplicationContextUtils.getWebApplicationContext(context);
        Connection connection = getSession(appContext).connection();
        Map parameters = new HashMap();
        Iterator iter = request.getParameterMap().keySet().iterator();
        while (iter.hasNext()) {
            String paramName = (String) iter.next();
            parameters.put(paramName, request.getParameter(paramName));
            System.out.println(paramName + ":" + request.getParameter(paramName));
        }
        parameters.put("root_dir", root_dir);
        createPDFReport(response, reportFile, parameters, connection);
    } catch (Exception e) {
        e.printStackTrace();
    }
}




 public void createPDFReport(HttpServletResponse response,
        String reportFile, Map parameters, Connection connection) {


        byte[] bytes = null;

        try {
            System.out.println("-------start run report-------");
            bytes = JasperRunManager.runReportToPdf(reportFile, parameters,
                    connection);
        } catch (JRException e) {
            e.printStackTrace();
        }



        if ((bytes != null) && (bytes.length > 0)) {
            response.setContentType("application/pdf");
            response.setContentLength(bytes.length);

            try {
                ServletOutputStream ouputStream = response.getOutputStream();
                ouputStream.write(bytes, 0, bytes.length);
                ouputStream.flush();
                ouputStream.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }

this is my url which is generated :

http://com.supcom:8080/SupCom/printer?method=genreport&numDec=265&yearDec=1435

Updated :

I use SHA1 function and in my url I make this change :

var numDec = numDec_decsion.getValue();

var yearDec = yearCorresp_decsion.getValue();       

    var url = "<c:url value='/printer'/>?method=genreport&numDec="+ SHA1(numDec) + 
                        "&yearDec=" + yearDec ;
                window.open(url);

the parametre will be generated like this :

http://com.supcom:8080/SupCom/printer?method=genreport&numDec=6216f8a75fd5bb3d5f22b6f9958cdede3fc086c2&yearDec=1435

but I didn't find the way to make the decryption in java class normally in createReport method

this is the code of SHA1 function

/**
*  Secure Hash Algorithm (SHA1)
*  http://www.webtoolkit.info/
**/
function SHA1(msg) {
  function rotate_left(n,s) {
    var t4 = ( n<<s ) | (n>>>(32-s));
    return t4;
  };
  function lsb_hex(val) {
    var str="";
    var i;
    var vh;
    var vl;
    for( i=0; i<=6; i+=2 ) {
      vh = (val>>>(i*4+4))&0x0f;
      vl = (val>>>(i*4))&0x0f;
      str += vh.toString(16) + vl.toString(16);
    }
    return str;
  };
  function cvt_hex(val) {
    var str="";
    var i;
    var v;
    for( i=7; i>=0; i-- ) {
      v = (val>>>(i*4))&0x0f;
      str += v.toString(16);
    }
    return str;
  };
  function Utf8Encode(string) {
    string = string.replace(/\r\n/g,"\n");
    var utftext = "";
    for (var n = 0; n < string.length; n++) {
      var c = string.charCodeAt(n);
      if (c < 128) {
        utftext += String.fromCharCode(c);
      }
      else if((c > 127) && (c < 2048)) {
        utftext += String.fromCharCode((c >> 6) | 192);
        utftext += String.fromCharCode((c & 63) | 128);
      }
      else {
        utftext += String.fromCharCode((c >> 12) | 224);
        utftext += String.fromCharCode(((c >> 6) & 63) | 128);
        utftext += String.fromCharCode((c & 63) | 128);
      }
    }
    return utftext;
  };
  var blockstart;
  var i, j;
  var W = new Array(80);
  var H0 = 0x67452301;
  var H1 = 0xEFCDAB89;
  var H2 = 0x98BADCFE;
  var H3 = 0x10325476;
  var H4 = 0xC3D2E1F0;
  var A, B, C, D, E;
  var temp;
  msg = Utf8Encode(msg);
  var msg_len = msg.length;
  var word_array = new Array();
  for( i=0; i<msg_len-3; i+=4 ) {
    j = msg.charCodeAt(i)<<24 | msg.charCodeAt(i+1)<<16 |
    msg.charCodeAt(i+2)<<8 | msg.charCodeAt(i+3);
    word_array.push( j );
  }
  switch( msg_len % 4 ) {
    case 0:
      i = 0x080000000;
    break;
    case 1:
      i = msg.charCodeAt(msg_len-1)<<24 | 0x0800000;
    break;
    case 2:
      i = msg.charCodeAt(msg_len-2)<<24 | msg.charCodeAt(msg_len-1)<<16 | 0x08000;
    break;
    case 3:
      i = msg.charCodeAt(msg_len-3)<<24 | msg.charCodeAt(msg_len-2)<<16 | msg.charCodeAt(msg_len-1)<<8  | 0x80;
    break;
  }
  word_array.push( i );
  while( (word_array.length % 16) != 14 ) word_array.push( 0 );
  word_array.push( msg_len>>>29 );
  word_array.push( (msg_len<<3)&0x0ffffffff );
  for ( blockstart=0; blockstart<word_array.length; blockstart+=16 ) {
    for( i=0; i<16; i++ ) W[i] = word_array[blockstart+i];
    for( i=16; i<=79; i++ ) W[i] = rotate_left(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1);
    A = H0;
    B = H1;
    C = H2;
    D = H3;
    E = H4;
    for( i= 0; i<=19; i++ ) {
      temp = (rotate_left(A,5) + ((B&C) | (~B&D)) + E + W[i] + 0x5A827999) & 0x0ffffffff;
      E = D;
      D = C;
      C = rotate_left(B,30);
      B = A;
      A = temp;
    }
    for( i=20; i<=39; i++ ) {
      temp = (rotate_left(A,5) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1) & 0x0ffffffff;
      E = D;
      D = C;
      C = rotate_left(B,30);
      B = A;
      A = temp;
    }
    for( i=40; i<=59; i++ ) {
      temp = (rotate_left(A,5) + ((B&C) | (B&D) | (C&D)) + E + W[i] + 0x8F1BBCDC) & 0x0ffffffff;
      E = D;
      D = C;
      C = rotate_left(B,30);
      B = A;
      A = temp;
    }
    for( i=60; i<=79; i++ ) {
      temp = (rotate_left(A,5) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6) & 0x0ffffffff;
      E = D;
      D = C;
      C = rotate_left(B,30);
      B = A;
      A = temp;
    }
    H0 = (H0 + A) & 0x0ffffffff;
    H1 = (H1 + B) & 0x0ffffffff;
    H2 = (H2 + C) & 0x0ffffffff;
    H3 = (H3 + D) & 0x0ffffffff;
    H4 = (H4 + E) & 0x0ffffffff;
  }
  var temp = cvt_hex(H0) + cvt_hex(H1) + cvt_hex(H2) + cvt_hex(H3) + cvt_hex(H4);

  return temp.toLowerCase();
}

Upvotes: 0

Views: 10188

Answers (1)

Artjom B.
Artjom B.

Reputation: 61892

For encryption and other techniques such as MACs, you need a key on the client. Since the client code is freely visible (it is JavaScript after all), you cannot effectively hide the key. So the user can look into the code and create a valid URL based on it.


You don't need encryption for this since the parameters don't seem to be confidential. You just need a message authentication code (MAC). It means that you hash the number that you want authenticated like yearDec with some secret that is known to client and server (with optional salt):

hYearDec = HASH(HASH(yearDec | key) | key [| salt])

Note that | means concatenation of the byte array or string. Now you generate the url with yearDec and hYearDec. Both parameters are send to the server. The server can check that yearDec wasn't changed by running the same hash function as above, because the key is supposedly secret to client and server. The salt value also needs to be sent to the server as it isn't secret.

Note that if you would encrypt the values, you would still need some kind of integrity check like a MAC.

Although any hash function such as SHA1 would do, you should use PBKDF2 to derive MAC based on the data, because it is much harder to break it.

CryptoJS provides the appropriate functions to do this.

var secretKey = "someSecretRandomString_k345kretiu46kzjnh";
var requestParameters = "yearDec=123456"; // send this
var salt = CryptoJS.lib.WordArray.random(128/8);
var key = CryptoJS.PBKDF2(requestParameters+secretKey, salt, { keySize: 256/32, iterations: 500 });
var saltHex = salt.toString(); // send this
var macHex = key.toString() // send this

And on the server:

String plaintext = "yearDec=123456"; // request parameter
String secretKey = "someSecretRandomString_k345kretiu46kzjnh";
plaintext += secretKey; // add key in the same way as in the client
char[] plaintextChars = new char[plaintext.length()];
plaintext.getChars(0, plaintext.length(), plaintextChars, 0);
int keySize = 256; // during the system setup
int iterations = 500; // during the system setup
String macHex = "bf92577e37627dbdc4a67510510c130aca6cf8e2e8bed0ea218f6cd909e3270d";  // further request parameters
String saltHex = "639a8d66d6a4fac8a39ce7c8b42fe0d8"; // further request parameters

// convert
byte[] mac = hexStringToByteArray(macHex);
byte[] salt = hexStringToByteArray(saltHex);

// derive
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec ks = new PBEKeySpec(plaintextChars, salt, iterations, keySize);
SecretKey s = f.generateSecret(ks);

// check
Arrays.equals(s.getEncoded(), mac);

I use this to convert hex to byte arrays:

public static byte[] hexStringToByteArray(String s) {
    int len = s.length();
    byte[] data = new byte[len / 2];
    for (int i = 0; i < len; i += 2) {
        data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                + Character.digit(s.charAt(i+1), 16));
    }
    return data;
}

Upvotes: 2

Related Questions