cancel
Showing results for 
Search instead for 
Did you mean: 

Secure hash function with salt to create a not spoofable PRC (SAP CRM)

gregorw
Active Contributor
0 Kudos

Hello SAP Security Community,

SAP CRM Marketing provides a functionality called Personalized Response Code (PRC, 10 characters). This code can be used in mail, fax, sms or letters to customers. When the customer returns the PRC to the communication initiator, it can be mapped to a campaign and the business partner number of the customer. See also the [SAP Standard Help|http://help.sap.com/saphelp_crm700_ehp01/helpdata/EN/2a/c13463f09c4a1f9c45903e7a0a7230/frameset.htm].

By default this standard implementation of the BAdI CRM_MKT_PRC_CONVERT is called:

METHOD if_ex_crm_mkt_prc_convert~convert_prc.
  DATA lv_no      TYPE  crmt_mkt_icrh_prc_num.
  DATA lv_string  TYPE  string.
  DATA lv_pos     TYPE  int4.
  DATA lv_base31  TYPE  string VALUE '0123456789BCDFGHJKLMNPQRSTVWXYZ'.

**** converting the numeric-base10 into base31
  lv_no = iv_prc.

  CLEAR lv_string.

  DO.
    lv_pos = lv_no MOD 31.
    lv_no  = lv_no DIV 31.

    CONCATENATE lv_base31+lv_pos(1) lv_string INTO lv_string.

    IF lv_no = 0.
      EXIT.
    ENDIF.
  ENDDO.

  MOVE lv_string TO ev_prc.
ENDMETHOD.

As you can see it does a simple base31 encoding of the provided input parameter iv_prc which is a number provided by the number range for PRC's.

I want to use the PRC to make our customers registration process for a trade fair easier. We send out the PRC via a letter to the customers where we don't have an E-Mail address. The letter contains instructions which point the user to a Website that has an input field for the PRC. When the user submits the PRC I'd like to show him/her some personal information (Name, Address, E-Mail) that we lookup using the PRC in the CRM System. This information is then posted to a 3rd party website that has to be used to do the trade fair registration.

If I would use the simple base31 encoding, then the current counter state could be easily decoded, the next number can be chosen and by applying base31 encoding again, the next valid PRC is created. This could then be misused to read personal information of another customer. I think what could solve this problem would be to use a secure hash function that allows also to be salted to create the PRC.

Do you think I'm on the right track? Or would it be OK to use the classes described in [Note 1410294 - Support SHA2-family for Message Digest and HMAC|https://service.sap.com/sap/support/notes/1410294] and before doing the hashing add a random number to the PRC number that I've got from the number range? What problems do I run in as the PRC could not be longer than 12 characters? For sure I have to check that I don't create any PRC twice.

Best regards

Gregor

Accepted Solutions (0)

Answers (1)

Answers (1)

Former Member
0 Kudos

Hi Gregor,

I don't think a message digest will help as this signs the string to verify that it is not change, not that it is not used. As the user only enters the hash string again (right?) there is no body to sign.

I can see two options for you, because if SAP had added some "salt" to the encoding to make it look like real hashing, then this would also be in clear text...

1) Deviate from the sequential number range by randomly generating lv_no and write the number into a table to prevent collisions. You might as well salt the source number because lv_string is in the end only encoding anyway and not 1-way encrypted hashing.

2) If you cannot change the number, then using a part of the sequential number range generated lv_string sent to them by post, generate a second string of your own. The "part of" is your salt as they don't know which part or encoding your BADI used to produce the second string. After entering the PCR number, your website prompts them for the second string which verifies that it belongs to the PCR number. This is a bit like a message digest, and makes the PCR number the body.

In both cases, take note that shortdumps might expose the encoding and salt to the user if they read the dump... so validate all input and encode all output...

Cheers,

Julius

gregorw
Active Contributor
0 Kudos

Hi Julius,

thank you for the idea with the second factor.

What do you think about this coding:

METHOD if_ex_crm_mkt_prc_convert~convert_prc.
  DATA: data TYPE string.
  DATA: rand TYPE REF TO cl_abap_random_int,
        rand_int TYPE i.
  DATA: hashstring TYPE string,
        ex TYPE REF TO cx_root.

  rand_int = iv_prc.

  rand = cl_abap_random_int=>create(
      seed = rand_int     " Initial Value of PRNG
      min  = 0            " Lower Limit for Value Area
      max  = 2147483647   " Upper Limit for Value Area
  ).

  rand_int = rand->get_next( ).
  iv_prc = iv_prc + rand_int.

  data = iv_prc.
  TRY.
      cl_abap_message_digest=>calculate_hash_for_char(
        EXPORTING
          if_algorithm     = 'SHA256'    " Hash Algorithm
          if_data          = data    " Data
        IMPORTING
          ef_hashstring    = hashstring    " Hash value as hex encoded string
      ).
      rand = cl_abap_random_int=>create(
          seed = rand_int " Initial Value of PRNG
          min  = 0        " Lower Limit for Value Area
          max  = 52       " Upper Limit for Value Area
      ).
      rand_int = rand->get_next( ).
      ev_prc = hashstring+rand_int(12).
    CATCH cx_abap_message_digest INTO ex.
  ENDTRY.

ENDMETHOD.

I think that should produce a not guessable PRC. I think that I will combine that with the question to enter the business partner number. Unfortunately the PRC is created after the method where I could add custom values to the marketing export. So requesting the BP-Number is the only way I think it can be solved.

Best regards

Gregor

Former Member
0 Kudos

If iv_prc imported is the BP-number or a part of it, then this means you now have a hash to verify the number sent, which is randomly salted.

Do you know the Mr. Wolf from Pulp Fiction? He also solves problems

From the view of the user this hash to verify the correct BP-number will behave like a password, so the only thing I can still think of is what to do with the hash once it has been used?

- Delete it (shoot Marvin in the face :-)?

- Force it to be changed?

- Generate a new salted one for the next use?

- Leave it as an unchangeable "password"?

Cheers,

Julius

gregorw
Active Contributor
0 Kudos

Hi Julius,

that's the best quality take that I've found: [Pulp Fiction Winston Wolf - I Solve Problems|http://www.youtube.com/watch?v=hsKv5d0sIlU]. ;-).

The iv_prc is just the number coming from the PRC number range. It does not contain the BP-Number.

The PRC is used for one specific campaign step. If there are further steps, then a new PRC will be created. But it is not deleted and behaves like a unchangeable "password".

Best regards

Gregor

Former Member
0 Kudos

When the customer returns the PRC to the communication initiator, it can be mapped to a campaign and the business partner number of the customer.

Ok, but the BP-number is still determined by the PCR-number, and this is "signed" by your randomly salted hash.

Without having been sent the hash with the letter, they cannot verify any other PCR-number by decoding their own and using the previous and next numbers to encode and take a lucky shot at the competitor's bid.

One last question from Jules here: If them seagulls dont even have no MF email, then how are they going to get their dumb asses over to the website?

Cheers and enjoy the weekend,

Julius

martin_voros
Active Contributor
0 Kudos

Hi,

two things. The salt is only 32bits. Not enough for 2011. Also use something else as seed for PRNG. The safest option is to generate random nonce and save mapping from you number to this nonce in bespoke table.

Cheers

Former Member
0 Kudos

On the one hand ...I was assuming that there is no place to verify the hash other than Gregor's website where the hash key for the PCR is kept, and the seed implementation is one which Gregor will not post here either. If that is the case, then it makes no difference to strengthen the seed IMO.

A random seed which would need to be kept in an additional bespoke table (or the same one as the hash) would only slow down the probing a bit because of the program runtime to select it and hash again. Even a WAIT UP TO 1 SECOND would be longer, go unnoticed by a "seagull" but slow down a brute force considerably (more that a random seed IMO).

If the PCRs for the phase have a validity of less than the computation time of a 20 character salted hash collision run against the website (which I see as the only attack option here) then it should be fine?

SAP's own USR02-PASSCODE and BCODEs A, B, D, and G can be hobbled with time if you know the user ID name. But as with Code Version F in this case, the caller does not know the ID which the hash belongs to (it does not matter anymore actually with Code Version F) nor the salt used so you can only probe for input collisions (in this case entered directly as input and not 1-way hashed again).

On the other hand ....your suggestion of a random seed would however protect the hash against most basis and DB admins... That is certainly true as they could simply display the hashusing data browsers.

Also if there was a source leak or the caller could provoke errors and read the variables of the PCR's hash being compared, then a random seed would also be more secure - depending on where the leakage / dump occured.

So I guess the question is : Is there a requirement to protect the hash against admins who can access them (same as with passwords)?

Cheers,

Julius

martin_voros
Active Contributor
0 Kudos

Hi Julius,

to me it seems like a classic problem solvable with random nonce. You can't use generated number because that would be direct object reference bug. Especially, in case you are going to display some personal data (google for "First State Superannuation security" for one recent example of consequences of direct object reference bug). So you need to transform your number to something random that can be easily guessed by attacker. Just adding 32 bits of randomness and hashing is not enough. I am not 100% sure and I am lazy to check that ABAP code but if it's really just PRNG that it returns same sequence of randomness for same seed. Hence that code is really broken.

So IMO the safest solution is to generate RANDOM nonce (length depends on usability and security requirements) and have a bespoke table with mapping from PCR to nonce. This solution supports also things like expiry date and time and invalidating nonce after first use. Obviously, admin who can read this table can get all personal details.But I guess that the same admin can go directly to DB to get personal details.

Cheers

Former Member
0 Kudos

Hi Martin,

I think it is relevant to consider here that there is no verification library or routine transported for the hash so they cannot probe it except on Mr. Wolf's website by attempting a collisionof the PRC number with a valid hash. (note that the ICM also has a caching filter to prevent DoS attacks and that will further slow this down as well).

This means they cannot generate hashes for PCR's using their own optimized environment. This should be considered in comparison to the complexity of the solution.

As a benchmark, SAP thought that base31 encoding is sufficient...

What would IMO still make a difference would be to concatenate the PCR and hash in an order of lengths into one string known only to the website and sent by letter, which then splits and concatenates the entered single string (one part signing the other, without the seagull knowing which part is which with which length).

That'll teach 'em to walk down the valley of the righteous and be bored to death by the inequities of unknown seeds and the tyranny of salted hashes...

Cheers,

Julius

martin_voros
Active Contributor
0 Kudos

Hi Julius,

> As a benchmark, SAP thought that base31 encoding is sufficient...

But the original purpose of PCR is different. It should help you to answer questions like how many people responded to my campaign and so on? Even spammers use this Knowledge of PCR should not reveal any personal information to you.

> What would IMO still make a difference would be to concatenate the PCR and hash in an order of lengths into one string known only to the website and sent by letter, which then splits and concatenates the entered single string (one part signing the other, without the seagull knowing which part is which with which length).

I don't agree with this. The security should NOT be based on hiding how system works. Only key should be secret. In this case it should all depend on quality of PRNG. Check [Kerckhoffs's principle|http://en.wikipedia.org/wiki/Kerckhoffs's_principle]. Whenever I see proprietary algorithm in crypto I start to feel nervous about the system.

Cheers

Former Member
0 Kudos

Knowledge of PCR should not reveal any personal information to you.

OK, but in this case the PCR is mapped to the campaign number and the BP-number. It would reveal the information. Hence a second hash which only allows further processing if it matches. The second hash is a "signature" of the PCR.

I don't agree with this. The security should NOT be based on hiding how system works. Only key should be secret. In this case it should all depend on quality of PRNG. Check Kerckhoffs's principle. Whenever I see proprietary algorithm in crypto I start to feel nervous about the system.

Ok, you convinced me. That is also true, but you will have to save the key or the hash it produces to be able to verify it again when the user returns to the website - and in this case it is in clear text ABAP (unless Mr. Wolf wants to create an external program, like SAP does with C-calls).

From the perspective of the user it is a password and they must be able to transfer it from a snail-mail readable text on paper into a website field.

As Mr. Wolf has noticed, the next PCRs can be obtained by anyone who can decode standard code (knowing that the BADI is activated).

I think a correctly placed split and concatenation does the trick for a 20 character field without knowing which part is the PCR and which is the signature (a human can still enter that into a website field).

I think the big question (appart from the principle - which I agree with you on) is whether the admins and their family members are allowed to bid? Also do the bidders have acces to this system as technical consultants?? (for example to single test methods and function modules in the production system??).

Also how does the process continue and finally get concluded? Typically there is some "horse trading" in the end anyway...

All these factors should influence the strength and complexity of the design, and maintenance of it IMO.

But generally you are correct and I rest my case.

@ Mr. Wolf: Are you enjoying the debate too much or are you going to give us more insight?

Cheers,

Jules

martin_voros
Active Contributor
0 Kudos

Hi Julius,

finally, I think I got what you mean. If I understand you correctly, you want to use HMAC. So your approach is:

Initialization: generate a random secret key that will be used for HMAC and store it somewhere.

Token generation: take PCR and calculate HMAC of it using secret key from initialization step. Concatenate PCR and calculated HMAC and convert it to more human readable code (e.g. base64).

The web front end gets a token on input, extracts PCR, calculates HMAC and checks if it's same as second part of the token. If it is then it displays personal info.

The attacker can easily guess PCR, the problem is that he needs to also generate a valid HMAC for this PCR. To do this he needs to know secret key (Kerckhoffs's principle). It's really important to use HMAC implementation instead of creating your own scheme. Crypto is really tricky and everyone should always try to use high level library.

There is nothing wrong with my approach, it's just based on different problem: an attacker can't guess a sufficiently long random string. Both approaches can be combined. You can generate a random nonce, store it into DB for PCR, calculate HMAC of nonce using secret key and send nonce + HMAC to user in human friendly form. In this case, the attacker needs to guess random nonce and produce a valid HMAC for it. You can also add additional info to DB such as expiry date.

Cheers