I had a problem with Coldfusion 8 that I posted on Stack Overflow not too long ago Coldfusion 8 doing both CFIf and the CFElse statement
that I thought I had narrowed down to a mysql problem, but with (much) further investigation, I have narrowed it down to a Multi threading / Multiple requests per session issue. (That may still be a MYSQL problem, but I have no idea how to fix it)
What is really going wrong with the code :
<cfif isValid("email", form.email)>
<cfquery name="check_user" datasource="#request.dsn#">
SELECT var_username, var_password
FROM tbl_users
WHERE var_username = <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#FORM.EMAIL#">
AND var_password = <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#FORM.PASSWORD#">
</cfquery>
<cfif check_user.recordcount EQ 0>
<cfquery datasource="#request.dsn#" name="insertuser">
INSERT INTO tbl_users (var_username, var_password) VALUES
(<cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#FORM.EMAIL#">, <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#FORM.PASSWORD#">)
</cfquery>
<cflogin idletimeout="1800">
<cfloginuser
name = "#FORM.email#"
password ="#FORM.password#"
roles = "0">
</cflogin>
<cflocation addtoken="No" url="#request.secure_url#checkout.cfm">
<cfelse>
<cfset client.error_message = "Your Email Address is already registered.">
<cflocation addtoken="No" url="#request.site_url#New-Account.html">
</cfif>
<cfelse>
<cfset client.error_message = "Your Email Address is not Valid.">
<cflocation addtoken="No" url="#request.site_url#New-Account.html">
</cfif>
is that there are 2 concurrent requests being made by the same user. If the requests are slightly staggered, Request A will insert the user into the database, but before Request A can CFLOGIN and CFLOCATION, Request B gets to the CFIF Notices that there is already a user in the database, and creats the CLIENT.ERROR_MESSAGE and CFLOCATION's to New-Account.html .
However, if the requests are not staggered, what happens is that the code appears to work, and sends the user to the checkout.cfm page, however in the database, the user is inserted twice.
The steps I have taken to try and resolve this:
1: Using different Databases within the same MYSQL Server (by changing the datasource to one of our other sites that have a similar/identical tbl_users). Same results.
2: Putting the website on a different coldfusion 8/windows 2003 server (but used the same MYSQL Server). Same results.
3: Put a
<cflock name="NewUser" timeout="30" type="EXCLUSIVE">
at the beginning of the code and a
</cflock>
at the end of the code. Same Results.
I really thought that putting a CFLOCK on the code would fix the issue, but it didn't, and now I have no idea what to do next (but it could be because I have never used CFLOCK before, and am using it wrong). Does anyone have any Ideas how to fix this issue so that only one request is sent? Or any ideas why 2 requests are being sent? (I don't think its pebkac, because I am the one doing the testing, and I am not hitting the submit button twice)
Also, I am using a windows 2003 web server, with coldfusion 8. And a seperate windows 2003 server with MYSQL 5.
Sorry my rank isn't high enough to just post a comment below your question...
I'm wondering why you are getting two or more concurrent requests for a user for that section of code. Are you accessing other CFM files using AJAX or the ColdFusion AJAX functions?
I think that your solution will be to track down why one user will hit that section of code multiple times.
You could use <cflog> to track how this section of code is called.
You're building a concurrent application. Be happy you found this problem so early in your development cycle. Concurrent applications work best when they use DBMS constraints to manage their concurrency rather than the kind of code you've shown in your example.
I suggest you switch to InnoDB if you aren't using it already, then set up your tbl_users.var_username column as a primary key with a unique constraint in the database.
Then, don't SELECT from your table. Just do the INSERT. You'll get an exception from the <cfquery> insert operation if the username is already there. Handle it appropriately. If the INSERT works correctly, you just added a new user and all is well. If it fails, you tried to add a duplicate user, and you can present an appropriate response on your web user interface.
Related
I'm a beginner so I Want to know how you can check if a name already exist in the database and give an alert or a message saying the name already exist in the database
but i don't want to use jquery or js just cf and the queries.
<cfif isDefined("Button")>
<cfquery name='Insert' datasource='mysql'>
INSERT INTO tbl_products_manager
(Name)
VALUES ('#name#')
</cfquery>
<cfinclude template="pr.cfm">
</cfif>
I want to know what i should write in the next cfquery or to add the message
You can do:
<cfquery name='Insert' datasource='mysql' result='local.stResult'>
INSERT INTO tbl_products_manager
(Name)
VALUES (<cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#name#" />)
WHERE NOT EXISTS (SELECT 1 FROM tbl_products_manager WHERE Name = <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#name#" />)
</cfquery>
Then you can dump local.stResult and figure out the result value that has the modified record count. (Can't remember offhand.) If the value is 0, you know it wasn't inserted.
As you indicate you are new to Coldfusion, please, please look up <cfqueryparam> and use it in every...single...query, every time.
The above is but one solution. You can also do a separate query first. Or you can have an identity (or MySQL equivalent) on the table and also get that in the stResult struct. There are a myriad of ways. Strenghten your sql-fu, and your CFML query/proc usage will get stronger, too!
Our server is changing from Coldfusion to a Lucee server and I'm tasked with updating our code for a couple of web applications. I'm not a guru of Coldfusion but I can often figure my way around things, to that end my query.
The code I'm converting over is throwing this error:
Can't cast Object type [DateTime] to a value of type [Array]
I have been working through all of the queries and making sure that the output is appropriately CAST which has resolved the majority of issues but the small block of code that is stumping me throws the above error. The code is:
<cfset summaryStartDate = ArrayMin( qSummaryData["minHours"] ) />
<cfset summaryMaxDate = ArrayMax( qSummaryData["maxHours"] ) />
<cfset summaryEndDate = DateAdd("d", -(DayofWeek(#summaryMaxDate#))+6, #summaryMaxDate# ) />
minHours and maxHours are both DATETIME format. I know in the coldfusion version they output like so:
summaryStartDate: 41204
summaryMaxDate: 43465
summaryEndDate: {ts '2019-01-04 00:00:00'}
Which, to me, means Coldfusion is doing a conversion in some way and Lucee doesn't do those (or at least from what I've read). The database is mySQL and the minHours and maxHours output as dates with 00:00:00 on the hours, for reference.
I'm probably missing something obvious but I can't see it.
I preface this answer with "it is not the greatest fix" but it does work. Taking my queue from andrewdixon I looked at side stepping the use of an array (which the data wasn't suited for) and looked at alternatives.
I settled on a query of queries, extracting the min value and then setting that in a cfset. Doing the same for max value following that. My two queries were:
<cfquery name="smallestFigure" dbtype="query">SELECT CAST(MIN(minHours) AS DATETIME) as outputMin FROM qSummaryData;</cfquery>
<cfquery name="largestFigure" dbtype="query">SELECT CAST(MAX(maxHours) AS DATETIME) as outputMax FROM qSummaryData;</cfquery>
I cfset these into summaryStartDate and summaryMaxDate so that this line (mentioned in the original post) could run:
<cfset summaryEndDate = DateAdd("d", -(DayofWeek(#summaryMaxDate#))+6, #summaryMaxDate# ) />
Shawn mentioned I didn't need the #'s around summaryMaxDate but I haven't made that change as yet. andrewdixon mentioned using query.reduce() as an alternative and I imagine that would be far more succinct than what I've done so if someone comes up with a better solution, please post as an answer.
Also thank you all for the support and ideas.
I have been trying to encrypt all the user's password in the MySql database using the following logic:
<cfquery datasource="mydatabase" name="userlist">
select userid, password
from mytable
LIMIT 0, 2000
</cfquery>
<cfoutput query="userlist">
<cfset pwd = encrypt(#userlist.password#, mykey, "AES/CBC/PKCS5Padding", "hex")>
<cfquery datasource="mydatabase">
update members
set password = '#pwd#'
where userid = '#userid#'
</cfquery>
</cfoutput>
This seems simply enough just to encrypt 2000 records. But the CF takes over 2 hours to do this! It averages only encrypting 13 records in one minutes. Is it something wrong with the code or is there a problem in CF Admin setting that needs to be tweak to speed up this thing?
p.s. the CF template does run but it will show 504 time-out on the browser in few minutes. However, the 2000 records will still be completed in the background in 2 hours time.
EDITED:
I ran the first set of query on the database directly. It only took 0.05 seconds to retrieve all 2000 rows. The second query to updated the database with already encrypted password took also less than a second. That leaves only the CFOUTPUT query and the CFSET ENCRYPT lines that are causing the the 2.5 hours run. I don't know how else to optimize this.
Working within comments was getting difficult..
In this post, I don't salt hashes, but it's just example stuff, you should definitely salt (add random, but consistent, string data).
Are you familiar with hashing? There is no need to encrypt passwords in a decryptable format. Instead, you can hash the user's entered password each time they enter it (registration, login, matching for password-change).
Among the several reasons hashing is preferred: Users typically insist on using ONE or as few as possible passwords everywhere. By providing their password in password-recovery (email "Your password is p#55word"), or over the phone, you may expose much of their online existence.
I do not use mysql often but you can md5 hash on the database side, as shown here, MySQL MD5 SELECT. Further, if you salt the hash (add your own string (like "sodium") to the hash that is not exposed anywhere, so long as it's the same salt for the user each time), you greatly increase the security as databases of straight hashes of entire dictionaries exist. Cookies md5's to 597b56e53847cd6a4712ac183f61fa68 but Cookiess (one more s) md5's to the very different 16fb9ddb550f238b848f8490854ef792.
You could use the link I referenced above to hash into a new column and experiment for data integrity for your own ease. Something like
update table
set hPwd = md5(password + 'Sodium' + username)
where hPwd = ''
limit(0, 2000)
so that you can still do it in pages if you like. There exist several hash functions, see dev.mysql.com/doc/refman/5.5/en/encryption-functions.html like SHA1.
Jack replied
Thanks. I would definitely use hash if not for the times that we may need to go into an account as a customer sees it, which means we need to know the password
Let me explain
When a user logs in, you would match their criteria like this
select data_you_want
from users_table
where username = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.username#">
and password = <cfqueryparam cfsqltype="cf_sql_varchar" value="#hash(form.password)#">
If that matches, you'd store the hashed password if you want to store the password in session data. Any pages where you want to re-affirm that the logged in user has the correct credentials, you'd use a query like this (notice the lack of hashing this time, vs the login query). Additionally you'd store asAdmin = 0, as a flag indicating that this is a normal user logging into their own account.
select data_you_want
from users_table
where username = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.username#">
and password = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.password#">
So, RE: wanting admins to be able to login. You create an admin gateway page that requires a different set of credentials (I'll get back to that) and if successful, you allow the user to login.
Because you want the user to still have some security, you might create a device like this... If a user is asking for admin assistance, you can give them a page to create an admin key like say #randrange(1,1000000)#
<cfset akey = randrange(1,1000000)>
...
update users_table
set akey = <cfqueryparam cfsqltype="cf_sql_varchar" value="#akey#">
where userID = <cfqueryparam cfsqltype="cf_sql_integer" value="#session.userID#">
...
You've requested some help from customer service and want them to login to your account.
Please give them this key #decimalformat(akey)#.
<!--- Decimal formatting makes the key easier to read, it's not stored that way --->
And then, when an admin logs in from a login page within the admin system, they enter the username and THIS KEY rather than a password.
That looks like this
select data_you_want
from users_table
where username = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.username#">
and akey = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.akey#">
If successful, it stores information similar to the login page that users normally access, no longer worries about the admin key.
Like
<cfscript>
AdminID = session.userID;
structclear(session); // get rid of all admin credentials, we're mimicing the user.
session.asAdmin = AdminID; // Create a flag that we can append to database activity if we want to track whether a user did this or an admin (and which admin!) did this on their behalf. If it's 0, it's the normal user, if it's greater than 0, it signifies which admin user did this.
session.username = GetUserStuffs.username;
session.pwd = GetUserStuffs.password; // because we're pulling a hashed password from the db, we do not rehash it.
session. ...other credentials you want in the session scope, the same things you'd store on normal user login...
</cfscript>
<!--- Now, use a client side redirect to send the admin, now logged in as the user, to the site root, or any normal-member-accessible-page. --->
<script>
window.location = "/";
</script>
You can also reset the akey for the user on this page, so that the key is only good once.
Alternatively you can do this all without an akey but that allows your admins free roam of user accounts and that's grossly insecure.
I think the issue may be because of having lots of individual cfquery calls when you execute the code so it's not very efficient. In the past I've tackled this by just using ColdFusion to create and render the SQL in your browser. You can then take that output and run it directly against the database which should be quicker to execute.
So your code would be something like this:
<cfquery datasource="mydatabase" name="userlist">
select userid, password
from mytable
LIMIT 0, 2000
</cfquery>
<cfoutput>
<pre>
<cfloop query="userlist">
<cfset pwd = encrypt(userlist.password, mykey, "AES/CBC/PKCS5Padding", "hex")>
SET #pwd = '#pwd#';
SET #userid = #userlist.userid#;
UPDATE members
SET password = #pwd
WHERE userid = #userid;
</cfloop>
</pre>
</cfoutput>
This will produce an output something like this:
SET #pwd = '0E8C9C1026C0EB950CA8A398C8E5ED6B';
SET #userid = 1;
UPDATE members
SET password = #pwd
WHERE userid = #userid;
SET #pwd = 'F6B9CE35A39A15D791F7C3711399493A';
SET #userid = 2;
UPDATE members
SET password = #pwd
WHERE userid = #userid;
SET #pwd = '1DB3AA5B3DF622CE905BD4E992B24E6D';
SET #userid = 3;
UPDATE members
SET password = #pwd
WHERE userid = #userid;
You can then run those SQL statements directly against the database server.
Ideally you should be using one-way encryption, but that's another question which is detailed here: https://security.stackexchange.com/questions/211/how-to-securely-hash-passwords
I'm working on an existing multi-language site (Coldfusion/MySQL).
Why is it that on a lot of pages I'm sitting on, some text strings are always hard-coded into the markup like:
<CFIF language = "EN"><p>Hello World</p></CFIF>
while others use the database to update text like so:
<p><cfoutput>#tx_greetings#</cfoutput></p>
What is the best practice here? I thought if I'm going to use a database for translations, it would be easier to store all texts in there (long and small). If I'm not using a database, then all texts should be if-elsed. Mixing it is a little maintenance-heavy, isn't it?
Also, is there a limit on text-string-length, which I'm storing to MySQL? Maybe performance-wise?
Thanks for some inputs!
You shouldn't store strings/translations in your code, that's bad practice if you want a maintainable i18n'd site.
You should store all your string in the same location, db or a properties file per language. It doesn't matter which, but be consistent. Personally I prefer a properties file as its easy to edit.
welcome_message=Hi {0}, Welcome to my site
Load all your translations in one go in onApplicationStart(), then provide a wrapper class to access them and to format the string with supplied arguments
for example
#i18n.getString(user.getLocale(), "welcome_message", [user.getUsername()])#
You can use java.text.MessageFormat[1] to provide powerful formatting
function getString(string locale, string key, array args) {
var mf = createobject("java", "java.text.MessageFormat");
mf.init(variables.strings[arguments.locale][arguments.key]);
return mf.format(javacast("java.lang.Object[]", args));
}
The above is just an example, and you need to provide error catching and caching to this
Hope that helps point you in a productive direction
[1] http://docs.oracle.com/javase/7/docs/api/index.html?java/text/MessageFormat.html
You can use a DB or a ascii file, depends on which you prefer.
If u use a DB you can create a table with the following columns:
country_code : country code for the language (i.e. US for english)
definition_name : name of the definition or message (*i.e. db_error_msg for a generic error message for db action*)
definition value : value of the definition (i.e. Sorry, an error occurred saving your data)
Each record will be a definition.
Depending on the language the user select your app will filter the database and you will get a query of all definitions you need.
I usually use that query to set a session variable structure like:
<cfif IsDefined("session.language") IS FALSE>
<cfquery name="getDefinition" datasource="dsn">
SELECT * FROM tbl_definitions WHERE country_code = "US"
</cfquery>
<cfset session.language = structnew()>
<cfoutput query="getDefinitions">
<cfset session.language["#definition_name#"] = "#definition_value#">
</cfoutput>
</cfif>
In the code I will simply use:
<cfoutput>
<h2>#session.language.db_error_msg#</h2>
</cfoutput>
and I will get the right message for the current language.
You can also use a master definition db to be used by different websites.
Same solution can be used with different configuration files (ie US.cfg. EN.cfg, ES.cfg) where you set your definitions in simple way to get a list.
I usually use the following system:
definition_name = definition_value for each line
db_error_msg = Sorry, an error occured saving your data
db_success_msg = Record saved
Then I read the current language configuration file (i.e. US.cfg for english, ES.cfg for spanish) and get the same result
<cfif IsDefined("session.language") IS FALSE>
<cffile action="read" file="#path#\US.cfg" variable="definitions">
<cfset session.language = structnew()>
<cfloop index="i" list="#definitions#" delimiters="#chr(10)#">
<cfset definition_name = ListGetAt(i,1,"=")>
<cfset definition_value = ListGetAt(i,2,"=")>
<cfoutput>
<cfset session.language["#definition_name#"] = "#definition_value#">
</cfoutput>
<cfloop>
</cfif>
This can be done just when session starts (if you know the language you need) and in both ways your definitions will be available everywhere inside your application for the user session time duration you have defined.
You can use definitions for buttons, messages, table headers, etc. creating a multilanguage ui in a very fast way without creating localized templates or using inline translations.
I hope this will help you.
Just as some generic localization advice, be careful about the variable name you'll use to know which phrase to retrieve. Don't just make it the english phrase, but make it something that is clearly a specific variable because you'll also have to handle contextual phrases that seem the same in English, but are very different in other languages depending on context.
I am migrating my coldfusion9 query code from ms access 2003 to mySql. My MySql knowledge is limited, so this is a beginner's question :)
In my MS Access code, I used simple cfqueries... here's an example:
<cfquery name="catalog" datasource="mydatasource">
SELECT TableID, DateListed, FirstColor, SecondColor
FROM mytable
WHERE FirstColor='blue' OR SecondColor='blue'
ORDER BY DateListed DESC
</cfquery>
I understand from online reading that one needs to use cfqueryparam with mySql to protect from injected malicious code. I'm not sure how the malicious code is injected .... as online website users don't interact with my database via forms, will I still need to use cfqueryparam?
If so, could you give me an example of an way to add cfqueryparam to the above code? -or- suggest a good, simple how-to resource for writing mySql code (in my searches online most of the coding info presupposes a higher level of knowledge than I have)
In addition to protecting from SQL injection, CFQUERYPARAM lets you use bind variables, which can give you performance gains because queries that are the same except for some variables only need to be compiled once and can be cached.
From your sample, this is how you'd used cfqueryparam:
<cfquery name="catalog" datasource="mydatasource">
SELECT TableID, DateListed, FirstColor, SecondColor
FROM mytable
WHERE FirstColor=<cfqueryparam cfsqltype="cf_sql_varchar" value="blue">
OR SecondColor=<cfqueryparam cfsqltype="cf_sql_varchar" value="blue">
ORDER BY DateListed DESC
</cfquery>
That doesn't buy you much, however. The payoff is in situations like this:
<cfquery name="catalog" datasource="mydatasource">
SELECT TableID, DateListed, FirstColor, SecondColor
FROM mytable
WHERE FirstColor=<cfqueryparam cfsqltype="cf_sql_varchar" value="#form.color#">
OR SecondColor=<cfqueryparam cfsqltype="cf_sql_varchar" value="#form.color#">
ORDER BY DateListed DESC
</cfquery>
Now whether the color is blue or red or chartreuse this query is compiled in the database and will be faster on subsequent calls. Further, a malicious user could change form.color to have the value of blue';drop table users; and you'll be protected from the SQL injection.
There are at least three good reasons to use cfqueryparam
1) Security from injection or other bad intent from users.
2) Queries execute faster because you explicitly state their type.
3) Its just a good idea, plus similar to other language features, like
$unsafe_variable = $_POST["user-input"];
$safe_variable = mysql_real_escape_string($unsafe_variable);
We used examples like this all over of DAO objects (these are the cfcs that have the code in them that speak to the database)
<cfqueryparam cfsqltype="CF_SQL_INTEGER" value=#arguments.assessmentID# />
<cfqueryparam cfsqltype="CF_SQL_CHAR" value='%#arguments.searchString#%' />
<cfqueryparam cfsqltype="CF_SQL_DATE" value=#arguments.assessmentDate# />
Where the #arguments.varname# was passed into the method containing the SQL.