How to verify that a URL parameter has not been tampered with? - java

Imagine I have a some web page implemented in Java, which is available at http://mycompany.com/page1.xhtml?trafficSource=someTrafficSourceIdentifier&checkSum=....
I want to keep track of how many page visits are generated by different traffic sources (such as different advertising campaigns). For this purpose, I have the trafficSource parameter. The set of traffic source IDs is limited to 5 possible values (e. g. Google organic, Google AdWords, YouTube, Facebook, Twitter).
Imagine, I want to make sure that trafficSource has not been tampered with (nobody has placed there a different value). For this purpose, I introduce the checkSum parameter.
What is the easiest way to create a check sum for a string X (checkSum = f(X)), such that different input strings are converted to different check sums?
The purpose of this measure is a basic check of my parameters.
I tried to use the following, but it produces a byte array with non-alphanumeric characters, which I can't put into the URL.
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class CheckSumGenerator {
public String calculateCheckSum(final String aInput) throws NoSuchAlgorithmException {
final MessageDigest md = MessageDigest.getInstance("SHA");
final byte[] checkSumBytes = md.digest(aInput.getBytes());
final String result = new String(checkSumBytes);
return result;
}
}

You can get the referrer on your server side from the http request header, which is not visible to the user. This could identify visitors coming from youtube or facebook.
To solve your problem of converting the hash to a alphanumeric string, you could convert the resulting bytes to hex code.
final String result = DatatypeConverter.printHexBinary(checkSumBytes);

Related

How to detect XSS in Java using OWASP

I have this thing which I'm dealing with right now, XSS.
I need to detect if a string contains XSS or not. In order to solve it I used that link. And this is the code I'm using:
public static boolean containsXSS(String value) {
if (StringUtils.isEmpty(value)) {
return false;
}
String stripXss = stripXSS(value);
return !value.equals(stripXss);
}
public static String stripXSS(String value) {
if (StringUtils.isBlank(value))
return value;
// Use the ESAPI library to avoid encoded attacks.
Encoder encoder = ESAPI.encoder();
value = encoder.canonicalize(value);
// Avoid null characters
value = value.replaceAll("\0", "");
// Clean out HTML
Document.OutputSettings outputSettings = new Document.OutputSettings();
outputSettings.escapeMode(Entities.EscapeMode.xhtml);
outputSettings.prettyPrint(false);
value = Jsoup.clean(value, "", Whitelist.none(), outputSettings);
return value;
}
Using the code above I do succeed to catch things like: <script>alert('xss')</script>
My problem is that I identify the following string as containing XSS although it's not:
{"item" :5}
It's because jsoup.clean turns it into {"item" :5}
I have tried to solve but with no success.
It makes me wonder if my algorithm is completely wrong (if so where can I find the algorithm to detect XSS), perhaps I don't need to compare to the original String?
I would very appreciate if you could help me.
thanks
You cannot detect if a string contains XSS. XSS in an output issue, not an input issue. Data benign in one context, can cause malicious behaviour in another.
Validate data using white lists to ensure data is valid in your domain (numbers are numbers, names do not contain unwantef characters etc.). This will stop some but definitely not all attacks.
Contextually encode user provided output as explained in the OWASP XSS prevention cheat sheet
Dont mix client side and server side templates
Be careful when using unsanitized data in javascript (see DOM-based XSS)

coldfusion calculating HMAC256 of a getHTTPRequestData

I'm working with Shopify at the moment and using their webhook notifications so I can save stuff to our database.
Within their webhook headers, they provide a header of: X-Shopify-Hmac-Sha256
which is:
Each Webhook request includes a X-Shopify-Hmac-SHA256 header which is generated using the app's shared secret (looks like: '267bb1719a8e6ff75c4f2d709be0ca11'), along with the data sent in the request.
This is jolly wonderful; However, I'm really struggling to calculate the value of the X-Shopify-Hmac-Sha256.
I have a .cfm page which the webhook hits and I pass through the getHTTPRequestData to a function like thus:
<cfscript>
variables.stArgs = {};
variables.stArgs.stWebHookData = getHTTPRequestData();
application.stObj.stShopify.oShopifyWebHookBusiness.receiveWebHook(argumentCollection=variables.stArgs);
</cfscript>
I then stole some code from StackOverflow and Ben Nadel, but neither seem to end up giving me the value that I want. Currently I'm using Ben Nadels code like thus:
local.data = arguments.stWebHookData.toString();
local.macClass = createObject( "java", "javax.crypto.Mac" );
local.secretkeySpec = createObject( "java", "javax.crypto.spec.SecretKeySpec" ).init(toBinary(toBase64(arguments.key)),'HmacSHA256');
local.mac = local.macClass.getInstance('HmacSHA256');
local.mac.init(local.secretkeySpec );
local.hashedBytes = local.mac.doFinal(toBinary(toBase64(local.data)));
return lcase( binaryEncode( local.hashedBytes, "base64" ) );
(arguments.key is the shared secret)
Somewhere along the way, I am going wrong. Have I completely misunderstood what I am meant to be doing. This looks so easy in PHP.
So, getHTTPRequestData() returns a struct with a number of members. The one we're interested is content, which will be a byte array.
The MAC classes' doFinal() method expects an array of bytes (in our case the HTTP request's content) and returns an array of bytes (the HMac of the content)
The returned byte array needs to be base-64 encoded in order to compare it to the one Shopify puts in the webhook's headers. toBase64() will do that for us.
Putting it all together, you get this:
toBase64(local.mac.doFinal(getHTTPRequestData().content))

Same hashing + encryption in different platforms generating unidentical values

I am writing some web-services for a social networking website. These web-services would be utilized by android for making android-app. As the person who designed the website is no longer under contact, I looked at the whole website code which was written in java with spring framework. I am writing web services in php.
Now, when I tried to send a post request to a php page that would confirm if the given username & pass combination is correct or not and then return a session id. But i'm not being able to get the correct hashing method to get correct hash value that is saved in the database.
Because of this, everytime, I am getting rejected by the php code.
The encryption that I found on the website code is as follows:
public static final synchronized String encrypt(String plaintext, String algorithm, String encoding) throws Exception
{
MessageDigest msgDigest = null;
String hashValue = null;
try
{
msgDigest = MessageDigest.getInstance(algorithm);
msgDigest.update(plaintext.getBytes(encoding));
byte rawByte[] = msgDigest.digest();
hashValue = (new BASE64Encoder()).encode(rawByte);
}
catch (NoSuchAlgorithmException e)
{
System.out.println("No Such Algorithm Exists");
}
catch (UnsupportedEncodingException e)
{
System.out.println("The Encoding Is Not Supported");
}
return hashValue;
}
For example, if i am giving password as monkey123 as password, it is giving hash value encoded in base 64 as: hge2WiM7vlaTTS1qU404+Q==
Now, after struggling to do the same in php for hours, I realised I could do the above procedure in android itself. So, I wrote the following code:
MessageDigest pwdDigest=MessageDigest.getInstance("MD5");
pwdDigest.update(password.getBytes("UTF-16"));
byte rawbyte[]=pwdDigest.digest();
String passwordHash=Base64.encodeToString(rawbyte,Base64.DEFAULT);
URL url = new URL(loginURL);
HttpURLConnection Connection = (HttpURLConnection) url.openConnection();
Connection.setReadTimeout(10000);
Connection.setAllowUserInteraction(false);
Connection.setDoOutput(true);
//set the request to POST and send
Connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
DataOutputStream out = new DataOutputStream(Connection.getOutputStream());
out.writeBytes("username=" + URLEncoder.encode(username, "UTF-8"));
out.writeBytes("&password="+URLEncoder.encode(passwordHash,"UTF-8"));
out.flush();
out.close();
if(Connection.getResponseCode()==200){
String data="Connected";
return data;
} else
return Connection.getResponseCode()+": "+Connection.getResponseMessage();
I expected this would be successful because in both the cases, I am doing same process to encrypt the password, but amazingly, this is not giving the hash value as:
hge2WiM7vlaTTS1qU404+Q== but it's giving : nZlvVe7GSS2Zso1dOwJrIA==
I am really struggling to find out a reason why these two are not the same. Any help would be hugely appreciated.
I don't expect MD5 to differ between platforms. It's stable and well documented and part of the core libraries. If this were broken in some Android version, nothing would work on that phone.
The re-encoding into UTF-8 is harmless, because all Base64 characters fit into the lower ASCII range. Three characters of the base64 alphabet require URL encoding, but you would have seen the %-escapes if something went wrong there.
Base64 is less stable ground (lots and lots of different implementations, no single canonical one), but it's not exactly rocket science either. Again, I don't expect a faulty implementation to really make it out into the wild, but the Base64 step may be where the difference arises.
Personally, I suspect the error is introduced during the password.getBytes("UTF-16") call. A quick way to verify this hunch is to inspect the resulting byte array in a debugger on both platforms. According to java.lang.Charset, the "UTF-16" encoding will use big endian byte order, whereas your PHP code may be defaulting to little endian because it's running on x86 and no byte order mark is present (I don't know PHP well enough to tell if this behaviour is well defined). Try modifying the Java code to use password.getBytes("UTF-16LE") and see if that makes a difference.
Side note: MD5 is no longer considered secure for hashing passwords; you'll want to use something like scrypt or PBKDF2 with plenty of rounds and a random salt, but that's a topic unto itself.

Implementing password digest for ws-security UsernameToken in Java

I am trying to make a call to a ws-security secured webservice from a server which unfortunately does not support this natively. The approach I have taken is to implement a .jsp which acts as reverse proxy to the actual end point URL, in the process adding the element with ws-security elements.
This seems to be working quite well and I am confident I've constructed the XML correctly with the correct namespaces etc. I've verified this by comparing the XML with XML produced by SOAP-UI.
The problem is in implementing the password digest generator. I don't get the same result as what SOAP-UI does using the same inputs for NOnce, xsd:dateTime and password, and the following code.
StringBuffer passwordDigestStr_ = new StringBuffer();
// First append the NOnce from the SOAP header
passwordDigestStr_.append(Base64.decode("PzlbwtWRpmFWjG0JRIRn7A=="));
// Then append the xsd:dateTime in UTC timezone
passwordDigestStr_.append("2012-06-09T18:41:03.640Z");
// Finally append the password/secret
passwordDigestStr_.append("password");
System.out.println("Generated password digest: " + new String(com.bea.xbean.util.Base64.encode(org.apache.commons.codec.digest.DigestUtils.sha(passwordDigestStr_.toString())), "UTF-8"));
I think the problem is with implementing the hashing of the first two elements as explained by http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0.pdf
Note that the nonce is hashed using the octet sequence of its decoded value while the timestamp is hashed using the octet sequence of its UTF8 encoding as specified in the contents of the element.
If anyone could help me solve this problem that would be great because it's beginning to drive me crazy! It would be ideal if you could provide source code.
I'll take a crack at it without SOAP-UI. The input to the hash function is supposed to be bytes, not a string. DigestUtils.sha() will allow you to use a string, but that string must be properly encoded. When you wrote the nonce, you were calling StringBuffer.append(Object) which ends up calling byte[].toString(). That gives you something like [B#3e25a5, definitely not what you want. By using bytes everywhere, you should avoid this problem. Note that the example below uses org.apache.commons.codec.binary.Base64, not the Base64 class you were using. It doesn't matter, that's just the one I had handy.
ByteBuffer buf = ByteBuffer.allocate(1000);
buf.put(Base64.decodeBase64("PzlbwtWRpmFWjG0JRIRn7A=="));
buf.put("2012-06-09T18:41:03.640Z".getBytes("UTF-8"));
buf.put("password".getBytes("UTF-8"));
byte[] toHash = new byte[buf.position()];
buf.rewind();
buf.get(toHash);
byte[] hash = DigestUtils.sha(toHash);
System.out.println("Generated password digest: " + Base64.encodeBase64String(hash));
Apologies for the delay in replying, especially considering your initial quick response. I have now been able to get this to work using the essence of your approach to avoid any character encoding issues. However, java.nio.ByteBuffer caused me issues so I modified the code to use basic byte[]s which I combined using System.arrayCopy(). The problem I faced with java.nio.ByteBuffer was that despite 'buf.position()' returning an appropriate number of bytes, all the bytes injected into byte[] toHash through buf.get(toHash) were 0s!
Thanks very much for your assistance.

Problem in retrieving a decrypted data

I am working on encryption-decryption program.
Program gets an input from the user and encrypts it. Then it stores the encrypted data in ms access database table.
Later, the data is retrieved from the table , decrypted and given back to the user.
I am storing the data as text in the ms access. The encryption algorithm returns a byte array of size 16.
But when i retrieve the data from the database, i am getting a byte array of size 8 only.
Help me to get through this...
I think the problem is that you are using it as text while it isn't (it is binary data). The halving of the length sounds like a Unicode related issue (i.e. the 'text' is stored as wide with two bytes for character, but retrieved as one byte per character).
I have an app that stores encrypted credit card numbers using the MS Crypto interface. I got the code from the MS Knowledge Base, and the key thing is running ByteToString() and StringToByte() conversion in the proper places. I'm storing the actual data in a plain Jet text field and have had no problems whatsoever.
one possible solution is to encode the cipher text as String using Base64 encoding
you can use Appache Commons Library for that:
http://commons.apache.org/codec/apidocs/org/apache/commons/codec/binary/Base64.html
Edited:
i dont know why you want MS-ACCESS Specific solution ! the DMBS may change, the OS also may change.. you must to write general solution that can work in many cases..
here small example for using Base64 Encoder/Decoder:
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import java.io.IOException ;
public class Decoder {
public static void main(String[] args) throws IOException{
byte[] cipherBytes = "stackoverflow".getBytes(); // say this the is encrypted bytes
String encodedBytes = new BASE64Encoder().encode(cipherBytes);
System.out.println("stored as: " + encodedBytes );
byte[] decodedBytes = new BASE64Decoder().decodeBuffer(encodedBytes);
System.out.println("extracted as: " + new String(decodedBytes) );
}
}
Note: this code using Internal Sun Classes (BASE64Encoder/Decoder) and its not recommended to use these classes in your program because it may change in the next version of JDK.
using BASE64 Encoder/Decoder in Appache Commons is better.
if you want the MS-ACCESS solution, try to store the ciphertext in LONGBINARY , see this:
How to specify blob type in MS Access?

Categories