I am trying to create a hash password in coldfusion for our secure payment gateway to accept a transaction.
Unfortunately the payment gateway is refusing to accept my generated hash.
The form sends through all the elements of the transaction and sends a generated hash based on five different fields.
In PHP it is-:
<?php
echo hash('sha256', '
test_site1234
GBP
OrderTotal
OrderID
PASSWORD
');;
?>
Am I right in thinking the code in coldfusion should be -:
<cfset sitesecurity = Hash("test_site1234"&"GBP"&#OrderTotal#&#URL.ThisOrderID#&"PASSWORD", "SHA-256")>
<cfoutput>#sitesecurity#</cfoutput>
I believe the link Miguel-F posted will fix your issue. Coldfusion's hash output is in all uppercase where most (all?) other outputs I've seen are in lowercase. Depending on how your gateway handles case sensitivity you should try passing a lowercase hash.
<cfset sitesecurity = lCase(hash("test_site1234GBP"&OrderTotal&URL.ThisOrderID&"PASSWORD", "SHA-256"))>
The code should have functioned the way it is, but in my opinion it's better to create the value to hash as one big string. Appending to strings is 'costly' because each time you add to a string a new string is created and the old one destroyed. If you're processing one transaction a minute you'd never notice a difference, but it is good practice either way. I would use.
<cfset sitesecurity = Hash("test_site1234GBP#OrderTotal##URL.ThisOrderID#PASSWORD", "SHA-256")>
Now you may have an issue getting a HASH in PHP to match a HASH in ColdFusion, but that's a separate issue.
Sample
<cfset OrderTotal = 10>
<cfset url.ThisOrderID = 50>
<cfset sitesecurity = Hash("test_site1234GBP#OrderTotal##URL.ThisOrderID#PASSWORD", "SHA-256")>
<cfdump var="#sitesecurity#" abort>
Returns
92A14E1D03833CB3FD6932A8E240861CDEC66E46723A544DFBC3C592D5EE7E66
Related
I have a login authentication system, the passwords in the database are stored as SHA-384. The following login script does nothing, when I include the Hash function. Where am I going wrong?
I'm using MSSQL Server 2008 R2, Coldfusion 10.
loginform.cfm
<cfif IsDefined("FORM.email")>
<cfset redirectLoginSuccess="admin.cfm">
<cfset redirectLoginFailed="login.cfm">
<cfquery name="UserAuth" datasource="sql1007539">
SELECT email,userPass FROM customers WHERE email=<cfqueryparam value="#FORM.email#" cfsqltype="cf_sql_clob" maxlength="255">
AND userPass=<cfqueryparam value="#Hash(form.userPassword, "SHA-384")#" cfsqltype="cf_sql_clob" maxlength="255">
</cfquery>
<cfif UserAuth.RecordCount NEQ 0>
<cftry>
<cflock scope="Session" timeout="30" type="Exclusive">
<cfset Session.Username=FORM.email>
<cfset Session.UserAuth="">
</cflock>
<cfif IsDefined("URL.accessdenied") AND true>
<cfset redirectLoginSuccess=URL.accessdenied>
</cfif>
<cflocation url="#redirectLoginSuccess#" addtoken="no">
<cfcatch type="Lock">
</cfcatch>
</cftry>
</cfif>
<cflocation url="#redirectLoginFailed#" addtoken="no">
<cfelse>
<cfset LoginAction=CGI.SCRIPT_NAME>
<cfif CGI.QUERY_STRING NEQ "">
<cfset LoginAction=LoginAction & "?" & XMLFormat(CGI.QUERY_STRING)>
</cfif>
</cfif>
Edit: The script works if no HASH functions are used.
Edit: I can also confirm the passwords are stored in SHA-384. I checked using the following HASH identifier: duncanwinfrey.com/tools/hashid/hash.php
Edit 29/05/13
**Code returns error, when I remove the cfparam tag **
<cfquery name="UserAuth" datasource="sql1007539">
SELECT email,userPass FROM customers WHERE email="#FORM.email#"
AND userPass="#hash(form.userPassword, "sha-384")#"
</cfquery>
Error returned
I would go with the encoding issue. I believe CLOB/BLOB are typically Oracle or DB2 datatypes and not native to MS SQL Server. I don't think you can assign CLOB/BLOB as a datatype in SQL Server. When you're passing a cf_sql_clob into the cfqueryparam, it's using the JDBC driver to try to convert to text or varchar(max) when it talks back to SQL Server. Something may be getting lost in translation. Since you're connecting to a SQL Server, try passing the correct datatype to the cfqueryparam. Look at the properties of the database columns for email and userPass. You should be able to set the cfsqltype to something like cf_sql_char or cf_sql_varchar. I'm kind of surprised the query isn't throwing an error, but the error may be getting masked by the datatype conversions, and it's simply not returning any results.
http://help.adobe.com/en_US/ColdFusion/10.0/CFMLRef/WSc3ff6d0ea77859461172e0811cbec22c24-7f6f.html
http://msdn.microsoft.com/en-us/library/ms378813(v=sql.105).aspx
EDIT:
Try changing your query to:
SELECT email,userPass FROM customers
WHERE email = <cfqueryparam value="#FORM.email#" cfsqltype="cf_sql_varchar" maxlength="255">
AND userPass = <cfqueryparam value="#Hash(form.userPassword, "SHA-384")#" cfsqltype="cf_sql_varchar" maxlength="255">
I managed to get it working with help from (agx) from the Experts Exchange forum. It turns out it was human error on my part. I had an extra space in my insert query of the registration process and also set the encoding to UTF-8:
'(space)#hash(form.password, "sha-384" ,'UTF-8')#'
I changed the password type to char(96), and amended the cfqueryparam, as suggested. Thank you all for your help and guidance. Below is troubleshooting code, I used to help me figure this out:
Entered an email and password of an existing entry, to grab record from the db:
<cfset form.email = "some known email">
<cfset form.userPassword = "real password before hashing">
<!--- ONLY match on email ---->
<cfquery name="qGetData" ....>
SELECT *
FROM yourTable
WHERE email =<cfqueryparam value='#FORM.email#'
cfsqltype="cf_sql_varchar">
</cfquery>
<!--- Checking to see if the password is hashed or is in clear text --->
<cfdump var="#qGetData#">
Hashed the clear text password and compared it to the db value.
<cfset newhash = hash(form.userPassword,'SHA-384')>
<cfif compare(newHash, qGetData.userPass) eq 0>
SAME
<cfelse>
DIFFERENT
</cfif>
At first sight the values looked the same. To make sure both the stored password in db and password from the login form were the same, the following code was used:
<cfoutput>
db |#qGetData.userPass#|<br>
form |#hash(form.userPassword,'SHA-384')#|<br>
</cfoutput>
I then used a handy website to compare the outputs. The results were the same again. After all this hard work, it turned there was an extra space in front of the #hash(...)#.
When using double quotes, the value is parsed as an object (table, columns, etc).
Always stick with the cfqueryparam, which is secure and fast.
Try adding a third argument to the Hash function which forces a different encoding; eg:
<cfquery name="UserAuth" datasource="sql1007539">
SELECT email,userPass FROM customers WHERE email=<cfqueryparam value="#FORM.email#" cfsqltype="cf_sql_clob" maxlength="255">
AND userPass=<cfqueryparam value="#Hash(form.userPassword, "SHA-384", "UTF-8")#" cfsqltype="cf_sql_clob" maxlength="255">
</cfquery>
Note the UTF-8 argument. Common encodings are: ISO-8859-1, ISO-8859-11 (Latin9).
Gl !
Not sure if this is a typeo or this is your code directly copy and pasted:
You are effectively bombing your cfquery tag with the double quotes inside the hash function of the value attribute.
You have this: value="#Hash(form.userPassword, "SHA-384")#"
Replace it with this: value="#Hash(form.userPassword, 'SHA-384')#"
Please note the Single quotes around the SHA-384 value. This should fix your problem.
So I have been using paypal, and sandbox mode everything works fine. Yet when I move over to live paypal with real money, IPN returns INVALID.
I am using a version of the script here ( https://cms.paypal.com/cms_content/US/en_US/files/developer/IPN_PHP_41.txt ) modified to my needs, only bits that are different are the db interactions etc
I have found this issue here ( http://www.hebtech.co.uk/blog/paypal-ipn-invalid-on-live-server-but-valid-on-test-server-fixed/ ) which seems to be the problem I am having, and basically says its to do with a carriage return in the optional second line of the address (which on test accounts in sandbox, isnt filled out). But the solution in the main post and another in the comments dont work, as below
$value = preg_replace('/(.*[^%^0^D])(%0A)(.*)/i','${1}%0D%0A${3}',$value);
OR
$value = preg_replace('/(.*?)(%0D%0A|%0A)(.*)/i','${1}%0D%0A${3}',$value);
Any ideas? Driving me mad. Its not stopping the payment, but it just means on the site, changing the status to paid has to be done manually
You can try this one:
$value = preg_replace('/%0D%0A|%0A/i','%0D%0A',$value);
I have also problem with character ; in custom field. Look in all fields you send to PayPall. Also check in all field any character which is not A-Z or 0-9.
Last thing you can try is set charset in your language encoding options: https://www.paypal.com/cgi-bin/webscr?cmd=_profile-language-encoding
I'm writing a Perl email subscription management app, based on a url containing two keycode parameters. At the time of subscription, a script will create two keycodes for each subscriber that are unique in the database (see below for script sample).
The codes will be created using Digest::SHA qw(sha256_hex). My understanding of it is that one way to ensure that codes are not duplicated in the database is to create a unique prefix in the raw data to be encoded. (see below, also).
Once a person is subscribed, I then have a database record of a person with two "code" fields, each containing values that are unique in the database. Each value is a string of alphanumeric characters that is 64 characters long, using lower case (only?) a-z and 0-9, e.g:
code1: ae7518b42b0514d69ae4e87d7d9f888ad268f4a398e7b88cbaf1dc2542858ba3
code2: 71723cf0aecd27c6bbf73ec5edfdc6ac912f648683470bd31debb1a4fbe429e8
These codes are sent in newsletter emails as parameters in a subscription management url. Thus, the person doesn't have to log in to manage their subscription; but simply click the url.
My question is:
If a subscriber tried to guess the values of the pair of codes for another person, how many possible combinations would there be to not only guess code1 correctly, but also guess code2? I suppose, like the lottery, a person could get lucky and just guess both; but I want to understand the odds against that, and its impact on security.
If the combo is guessed, the person would gain access to the database; thus, I'm trying to determine the level of security this method provides, compared to a more normal method of a username and 8 character password (which generically speaking could be considered two key codes themselves, but much shorter than the 64 characters above.)
I also welcome any feedback about the overall security of this method. I've noticed that many, many email newsletters seem to use similar keycodes, and don't require logging in to unsubscribe, etc. To, the primary issue (besides ease of use) is that a person should not be able to unsubscribe someone else.
Thanks!
Peter (see below for the code generation snippet)
Note that each ID and email would be unique.
The password is a 'system' password, and would be the same for each person.
#
#!/usr/bin/perl
use Digest::SHA qw(sha256_hex);
$clear = `clear`;
print $clear;
srand;
$id = 1;
$email = 'someone#domain.com';
$tag = ':!:';
$password = 'z9.4!l3tv+qe.p9#';
$rand_str = '9' x 15;
$rand_num = int(rand( $rand_str ));
$time = time() * $id;
$key_data = $id . $tag . $password . $rand_num . $time;
$key_code = sha256_hex($key_data);
$email_data = $email . $tag . $password . $time . $rand_num;
$email_code = sha256_hex($email_data);
print qq~
ID: $id
EMAIL: $email
KEY_DATA: $key_data
KEY_CODE: $key_code
EMAIL_DATA: $email_data
EMAIL_CODE: $email_code
~;
exit;
#
This seems like a lot of complexity to guard against a 3rd party unsubscribing someone. Why not generate a random code for each user, and store it in the database alongside the username? The method you are using creates a long string of digits, but there isn't actually much randomness in it. SHA is a deterministic algorithm that thoroughly scrambles bits, but it doesn't add entropy.
For an N bit truly random number, an attacker will only have a 1/(2^N) chance of guessing it right each time. Even with a small amount of entropy, say, 64 bits, your server should be throttling unsubscribe requests from the attacking IP address long before the attacker gets significant odds of succeeding. They'd have better luck guessing the user's email password, or intercepting the unencrypted email in transit.
That is why the unsubscribe codes are usually short. There's no need for a long code, and a long URL is more likely to be truncated or mistyped.
If you're asking how difficult it would be to "guess" two 256-bit "numbers", getting the one specific person you want to hack, that'd be 2^512:1 against. If there are, say, 1000 users in the database, and the attacker doesn't care which one s/he gets, that's 2^512:1000 against - not a significant change in likelihood.
However, it's much simpler than that if your attacker is either in control of (hacked in is close enough) one of the mail servers from your machine to the user's machine, or in control of any of the routers along the way, since your email goes out in plain text. A well-timed hacker who saw the email packet go through would be able to see the URL you've embedded no matter how many bits it is.
As with many security issues, it's a matter of how much effort to put in vs the payoff. Passwords are nice in that users expect them, so it's not a significant barrier to send out URLs that then need a password to enter. If your URL were even just one SHA key combined with the password challenge, this would nearly eliminate a man-in-the-middle attack on your emails. Up to you whether that's worth it. Cheap, convenient, secure. Pick one. :-)
More effort would be to gpg-encrypt your email with the client's public key (not your private one). The obvious downside is that gpg (or pgp) is apparently so little used that average users are unlikely to have it set up. Again, this would entirely eliminate MITM attacks, and wouldn't need a password, as it basically uses the client-side gpg private key password.
You've essentially got 1e15 different possible hashes generated for a given user email id (once combined with other information that could be guessed). You might as well just supply a hex-encoded random number of the same length and require the 'unsubscribe' link to include the email address or user id to be unsubscribed.
I doubt anyone would go to the lengths required to guess a number from 1 to 1e15, especially if you rate limit unsubscribe requests, and send a 'thanks, someone unsubscribed you' email if anyone is unsubscribed, and put a new subsubscription link into that.
A quick way to generate the random string is:
my $hex = join '', map { unpack 'H*', chr(rand(256)) } 1..8;
print $hex, "\n";
b4d4bfb26fddf220
(This gives you 2^64, or about 2*10^19 combinations. Or 'plenty' if you rate limit.)
Is there a way to encode a email address that can be used as a part of a url in codeigniter?. I need to decode back the email address from the url.
What I am trying to do is just a -forgotten password recovery- thing. I send a confirmation link to the user's email address, the link needs to be like ../encodedEmail/forgottenPasswordCode (with the forgottenPasswordCode updated in the db for the user with the submitted email).
When the user visits that link, I decode the email(if the email - forgottenPasswordCode pair is in the table), i allow them to reset their password (and i reset forgottenPasswordCode back to null).
I could just do a loop -checking the table with a select query- (or) -set that forgottenPasswordCode column unique, so i keep generating on a insert failure(would that be a lot faster ?)- until I generate a forgottenPasswordCode that doesn't already exist in the table.
But the guy I do this for would not accept it this way:). He wants the checking be done with the user's email, he thinks its much faster.
I am working with codeigniter, I used its encode() function, it seems to produce characters like '-slashes-' at times that breaks the encoded-email-string.
Any other ideas?
try using bin2hex() and hex2bin() function,
<?php
function hex2bin($str)
{
$bin = "";
$i = 0;
do
{
$bin .= chr(hexdec($str{$i}.$str{($i + 1)}));
$i += 2;
} while ($i < strlen($str));
return $bin;
}
$str = 'email#website.com';
$output = bin2hex($str);
echo $output . '<br/>';
echo hex2bin($output);
?>
Don't put data in the URL that doesn't have some sort of meaning. This leaves two choices:
Send the address as part of a POST. If it's coming from a web form this is the way to go.
Refer to the address in the database using an ID or hashed value. If you need the user to click a link referring to their account, use something that clearly refers to their account. If you need to refer to an instance of a password reset (many systems do this), add a table containing hashes, using that hash in the URL.
Why not just encode it in the URL?
You can see URLs (it's part of the UI), encoded things look weird
URLs represent resources, things in your app (users probably already have IDs)
Encoded email addresses are long (making these URLs harder to work with in things like emails)
Try to keep parameters in URLs to clear references to concepts in your web app (point at one user by ID or plaintext name, for example). Parameters that don't fit in URLs go in POST parameters. If you must use something encoded in a URL, prefer one-way-encoding and database lookups.
Although it may be not optimal design solution to use email as a part URL,
use email as base64 encoded string to avoid any issues with special chars
E.g. Base64 encoded string 'abc-def#example.com' is
YWJjLWRlZkBleGFtcGxlLmNvbQ==
In your case the URL is
../YWJjLWRlZkBleGFtcGxlLmNvbQ==/forgottenPasswordCode
All you need is to decode that string back before usage
I want to pass a serialized Perl data structure as a GET variable to a CGI application. I tried Data::Serializer as my first option. Unfortunately the serialized string is too long for my comfort, in addition to containing options joined by '^' (a caret).
Is there a way I can create short encoded strings from perl data structures so that I can safely pass them as GET variables to a perl CGI application?
I would also appreciate being told that this (serialized, encoded strings) is a bad way to pass complex data structures to web applications and suggestions on how I could accomplish this
If you need to send URL's to your users that contains a few key datapoints and you want to ensure it can't be forged you can do this with a Digest (such as from Digest::SHA) and a shared secret. This lets you put the data out there in your messages without needing to keep a local database to track it all. My example doesn't include a time element, but that would be easy enough to add in if you want.
use Digest::SHA qw(sha1_base64);
my $base_url = 'http://example.com/thing.cgi';
my $email = 'guy#somewhere.com';
my $msg_id = '123411';
my $secret = 'mysecret';
my $data = join(":", $email, $msg_id, $secret);
my $digest = sha1_base64($data);
my $url = $base_url . '?email=' . $email . '&msg_id=' . $msg_id' . '&sign=' . $digest;
Then send it along.
In your "thing.cgi" script you just need to extract the parameters and see if the digest submitted in the script matches the one you locally regenerate (using $email and $msg_id, and of course your $secret). If they don't match, don't authorize them, if they do then you have a legitimately authorized request.
Footnote:
I wrote the "raw" methods into Data::Serializer to make translating between serializers much easier and that in fact does help with going between languages (to a point). But that of course is a separate discussion as you really shouldn't ever use a serializer for exchanging data on a web form.
One of the drawbacks of the approach — using a perl-specific serializer, that is — is that if you ever want to communicate between the client and server using something other than perl it will probably be more work than something like JSON or even XML would be. The size limitations of GET requests you've already run in to, but that's problematic for any encoding scheme.
It's more likely to be a problem for the next guy down the road who maintains this code than it is for you. I have a situation now where a developer who worked on a large system before I did decided to store several important bits of data as perl Storable objects. Not a horrible decision in and of itself, but it's making it more difficult than it should be to access the data with tools that aren't written in perl.
Passing serialized encoded strings is a bad way to pass complex data structures to web applications.
If you are trying to pass state from page to page, you can use server side sessions which would only require you to pass around a session key.
If you need to email a link to someone, you can still create a server-side session with a reasonable expiry time (you'll also need to decide if additional authentication is necessary) and then send the session id in the link. You can/should expire the session immediately once the requested action is taken.