Take the 2-minute tour ×
Information Security Stack Exchange is a question and answer site for Information security professionals. It's 100% free, no registration required.

I'm a programmer working on an application where the only choice/vs/deadline was to implement symmetric encryption on url parameter values. The data is insensitive in nature, but we needed to prevent sales agents from peeking on each other's leads. (Keys are generated on session creation and are cryptographically strong.) Sessions are expected to end frequently.

The role hierarchy was Manager-->Supervisor-->Agents. The data structures don't currently account for these roles in a way to strictly enforce who can see what. Getting this information from the database was NOT anywhere close to straightforward. (Recursive Database.)

I know that this technique is way down on the list as a defense against parameter manipulation. What would have been a better technique?

Role-based checking is not an option.

[Additional information] The urls built and sent to the client before I made any changes looked like:


The specific threat surface here is parameter manipulation against ?agentId=12345. Agent ids are assigned uniquely to each agent. So if Agent A wants to look at Agent B's stats, he could have entered agentId=22222 in order to look at that agent's quotes and current sales statistics.

Again, Role-Based checking was not an option for me: I was unable to make changes to the database OR the persistence tier.

My solution was to use a session-created encryption key (using Java's KeyGenerator class) and encrypting the outbound urls sent to the client. So now, the url looks like:


Now, if someone tries agentId=22222, the server will decrypt what it thinks is ciphertext and will ultimately create an invalid character sequence.

(This leaves open the possibility that an existing agentId could be found, but quite unlikely that it would be relevant to the person performing the attack.

I will stress that this question isn't about optimal security (which would be role-based checking to ensure resource access) and about trying to squeeze some security in a grey area.
===============Update============ The parameter encryption solution here was recommended to me by one of our security guys. I got one takeaway I hadn't considered on this solution--broken urls--and will be using that as well as the maintenance issue created by this solution to argue for the time to enforce the access rules in a less stopgap fashion.

share|improve this question
Is there a reason you're using a home-brew protocol to encrypt the traffic, rather than SSL? –  Polynomial Jul 16 '12 at 15:12
Not a protocol. We're using SSL, but I'm worried about parameter manipulation from the client, not the points between client/server. –  avgvstvs Jul 16 '12 at 21:24
Something sounds very wrong when you say RBAC is not an option. –  Luc Jul 17 at 22:22
@Luc Its mostly academic at 2yrs out, but it was really time-constraint. The web portion of that app was new and was built without any concept of RBAC. It was designed as an ad-hoc extension, not a fully-fledged webapp like it needed to be. The architect who was directing me made this design decision in order to meet the deadline. –  avgvstvs Jul 18 at 16:03
Security was consulted on this as well, and they signed off on this direction, even though I didn't like it. –  avgvstvs Jul 18 at 16:05

3 Answers 3

up vote 3 down vote accepted

Good question! Thanks for elaborating on the threat you are trying to defend against. I have edited my answer accordingly.

Summary. Your primary defense should be access control. You need to limit which users can view which pages. Details below.

Access control in web applications. What you need to do is check that the user is authorized to access the data you're going to show on a page, before allowing them to see that data. This basically comes down to access control: you want controls that limit which users can view which data, based upon some authorization policy.

It sounds like you have a sequence of pages, one for each agent:


where the producerIds (agentIds) are potentially guessable or predictable. You want to ensure that agent 12345 can view http://www.example.com/agent/?producerId=12345 but not any of the other pages. OK.

This is a bog-standard situation, and the bog-standard defense is: access control.

To implement access control, you code the web application so that each page checks whether the user is authorized to view that page before allowing the user to view that page. For instance, for the page listed above, the logic implementing that page would check the identity of the currently-logged in user. If the id of the logged-in user matches the producerId of the page parameter, then you show them the information. If the id does not match, you do not show them the information: if it is some other user, you show them an error page (with information about how to get access), or if the user has not logged in yet, you redirect them to a login page.

This won't break bookmarks. It does not require changes to the database, changes to the persistence layer, or role-based access control. It does require you to have a way to look up the identity of the currently logged-in user and associate that with their provider ID. Also, if you want to allow manager and supervisors to see the data for all other agents, then you need a way to look up the currently logged-in user and determine whether they are a manager or supervisor or not. If you want to allow only the agent's manager/supervisor to view their page (not all other managers/supervisors), then you need to have a way to determine the manager/supervisor of each agent. These are pretty basic, minimal requirements; it is hard to see how you could avoid them.

As @symbcbean properly points out, this is a very common error frequently found in web applications. A typical example might be a site that uses some guessable parameter value to identify a resource, and does not adequately authenticate the user. For instance, suppose orders are assigned a sequential order number:


and suppose that anyone who knows the URL can view the order. That would be bad, because it means that anyone who knows (or guesses) the order number can view the order, even if they are not authorized to do so. This is one of OWASP's Top Ten web application security risks: Insecure Direct Object References. For more information, I recommend reading the resources available on OWASP. OWASP has lots of great resources on web application security.

Other comments. Others have suggested using SSL. While that will not prevent parameter tampering, it is a general good security practice that defends against other kinds of problems. Using SSL is straightforward: just configure your website to use https, instead of http (and ideally, enable HSTS and set the secure bit on all cookies).

Also, it is often better to avoid storing confidential information in URL parameters, all else being equal. You can store the confidential information in session state or in the database.

share|improve this answer
I added some extra information in the original question to help guide you where I'm presently at. At the present state of the application, I have no ability to perform role-based checking as what you suggest here. It requires changing a megalith recursive-DB as well as changes throughout the entire persistence layer. To be blunt, despite my protests, despite the SANS training they sent me to get, they don't care because the data isn't sensitive. So I'm more or less fumbling for something serviceable... –  avgvstvs Jul 16 '12 at 21:56
@avgvstvs, I'm puzzled by your statements. Where did role-based checking come from? I did not suggest role-based checking, and nothing I suggested is in any way tied to role-based access control. Not sure where that appeared from. (continued) –  D.W. Jul 17 '12 at 0:43
Moreover, access control checks do not necessarily require changes to the database or the persistence layer. They require only that you (a) know the identity of the currently logged-in user, and (b) know which users should be allowed to access each page. (a) should be true of almost any web application. It appears that (b) is true here: you want only agent 12345 to be able to view the page associated with ?agentid=12345. Seems like it should be straightforward for you to implement access controls as part of the page logic. –  D.W. Jul 17 '12 at 0:43
@avgvstvs, OK, I've edited my answer in light of the additional information you provided. Hopefully this makes more sense now. –  D.W. Jul 17 '12 at 0:52
I apologize, I extend "roles' to mean: A Manager-->CanSee 1:M Supervisor(s)-->CanSee 1:M Agents. (Note the single direction of the arrows.) Theres no field in these data structures to allow me to make these comparisons. The data model is hypernormalized. (Normal queries can require 9 table joins). My decision was based on "what can I get done by release in 4h?" My favored solution is pretty much exactly what you discuss. It's not an option for me until my DBA gets back and can explain this IAA model. Thank you, btw. At least you validated my original idea was right. –  avgvstvs Jul 17 '12 at 1:50

but we needed to prevent sales agents from peeking on each other's leads

This rather implies that the client is a browser - are you sending the key as cleartext at some point?

Polynomial is correct, you should be using SSL. That's not going to solve the problem of users typing in adjacent values to a URL looking something like:


It's quite possible to generate an authentication token server-side based on the parameters which must be presented to validate the request. Ideally you would use a message authentication code (MAC) for this, but a hash would work too if you are careful. e.g. in PHP...

 print "<a href='show_order.php?id=" . $id . "&valid=" . md5($id . crypto_key()) . "'>...

Which is validated simply by:

if ($_GET['valid'] != md5($_GET['id'] . crypto_key()) {
   die('not authorized');

Here crypto_key() returns a static cryptographic key (generate it by pulling, say, 128 bits from /dev/urandom and storing it in the database).

But you still need to control access to the code which generates the URL.

share|improve this answer
The hash method in the second half of your algorithm is broken and adds no security whatsoever. I know my own session ID (it is in my session cookie) so if I am evil, I can choose any parameter value I want, calculate the appropriate MD5, and ensure that the server-side validation check passes. You probably want a message authentication code (MAC), not a hash function, but the MAC key needs to be stored solely on the server and never sent to clients. –  D.W. Jul 16 '12 at 18:19
I will stress that SSL is already being used. I'm not concerned with what's between client and server, I'm concerned with what the client will be sending in to snoop. –  avgvstvs Jul 16 '12 at 21:51
@D.W.: true - using a static value held server side in place of session_id() solves this and resolves the bookmark issue (with the option of using te username as well as the secret static value to prevent portability of bookmarks). –  symcbean Jul 17 '12 at 16:05
@symcbean, Nice solution. I took the liberty of editing your answer to reflect using a static crypto key. Hope that was OK. +1, nice answer. –  D.W. Jul 17 '12 at 18:16

Here is my solution

$en_id = encrypString( $id);

and I create the url like


the url will look like


and on the other side I decrypt

$en_id= decryptString($_GET['id']);

the functions for crypt and decrypt are

function encrypString($plaintext) {
         # --- ENCRYPTION ---

        $key = pack('H*', "bcb04b7e103a0cd8b54763051cef08bc55abe029fdebae5e1d417e2ffb2a00a3");//change this

        # show key size use either 16, 24 or 32 byte keys for AES-128, 192
        # and 256 respectively
        $key_size =  strlen($key);
        //echo "Key size: " . $key_size . "\n";

        # create a random IV to use with CBC encoding
        $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
        $iv = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM);

        # creates a cipher text compatible with AES (Rijndael block size = 128)
        # to keep the text confidential 
        # only suitable for encoded input that never ends with value 00h
        # (because of default zero padding)
        $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key,
                                     $plaintext, MCRYPT_MODE_CBC, $iv);

        # prepend the IV for it to be available for decryption
        $ciphertext = $iv . $ciphertext;

        # encode the resulting cipher text so it can be represented by a string
        $ciphertext_base64 = base64_encode($ciphertext);

        return  rawurlencode($ciphertext_base64);//important rawurlencode for + symbol in url


decryptString($ciphertext_base64) {
        # --- DECRYPTION ---

        $key = pack('H*', "bcb04b7e103a0cd8b54763051cef08bc55abe029fdebae5e1d417e2ffb2a00a3");//change this

        # show key size use either 16, 24 or 32 byte keys for AES-128, 192
        # and 256 respectively
        $key_size =  strlen($key);
        //echo "Key size: " . $key_size . "\n";

        # create a random IV to use with CBC encoding
        $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
        $iv = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM);

        $ciphertext_dec = base64_decode($ciphertext_base64);

        # retrieves the IV, iv_size should be created using mcrypt_get_iv_size()
        $iv_dec = substr($ciphertext_dec, 0, $iv_size);

        # retrieves the cipher text (everything except the $iv_size in the front)
        $ciphertext_dec = substr($ciphertext_dec, $iv_size);

        # may remove 00h valued characters from end of plain text
        $plaintext_dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key,
                                    $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec);

        return rawurldecode($plaintext_dec);
share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.