So I just had a random, stray thought. One thing I've done in the past is sniff the current server to determine behavior aspects of my site. So for example, my error handler may do something like this:
<cfif findNoCase("dev.", cgi.server_name)>
<cfdump var="#exception#">
<cfelse>
<p>
Something went wrong. Quick - reboot the Tandy.
</p>
</cfif>
While this works nice, I just ran across a problem with it. If you hit this file on my site:
http://www.coldfusionjedi.com/test4.cfm
You will see I print out cgi.server_name. Now if you go into your HOSTS file and do:
67.59.153.214 baloneypants.com
And then hit the file with:
http://baloneypants.com/test4.cfm
You will notice that the CGI variable now says baloneypants. No big surprise there I guess. But obviously if I knew your code did this, and if I added dev.x.com for x.com, I could use it as a way to "sniff" out differences based on that CGI variable.
Unfortunately I can't think of a nice solution. Sniffing the servername is a handy way to dynamically load settings based on environment. If you can't trust cgi.server_name than you need to do something else - like perhaps sniff something on the machine itself.
Any suggestions?
Archived Comments
Our older sites used that to determine what to show to the user. Our newer sites are using different configuration files for live and dev.
On a Windows host cfregistry can get you the machine name, which is alright if you can guarantee your code will always run on a windows box.
Additionally, I think there are commands that you can execute from the command line that will tell you what host you are on but they too are dependent on OS.
Do you use source control for the config files?
You can leverage Java here to get the local ip and use rDNS to get the host name:
<cfset inet = CreateObject("java", "java.net.InetAddress") />
<cfset hostName = inet.getByName(inet.getLocalHost().getHostAddress()).getHostName()>
<cfoutput>#hostName#</cfoutput>
What happens if you are host headers and hosting multiple sites on the same IP?
@Gary,
IIRC, you'll return the machine name back. I just tried it on a server that uses Apache virtual hosts and it returned servername.host.com which is the machine name.
cgi.server_name is also problematic if you're running on a server cluster. It will return the name of the actual server that is executing the current template. Say you have http://www.baloneypants.com load balanced across 2 servers named baloney1 and baloney2. If you use cgi.server_name, you'll get either baloney1 or baloney2 as a result, but not www.baloneypants.com.
I ran into this issue when using SSL on a cluster. I wanted to switch to https when doing a user login, then back to http upon success, for performance reasons. I figured I could construct the URL using cgi.server_name, cgi.script_name, etc, but no dice. Eventually, I stored the server name that I wanted to use in an XML file and loaded it as part of the configuration, then constructed the URL using that name.
You could prevent this kind of URL spoofing in your web server config by only responding to specific host names on the IP address.
I think I read somewhere a while back that as a security practice, you should prevent direct access to your web site's IP address if you can.
nicholas - Ah, thats a good idea. So don't allow *.x.com to hit your production box, only x.com and www.x.com. Yeah, that would do the trick, wouldn't it?
What does HTTP_HOST give you? This is what I have always used (it includes the port number also), but I don't know how it would act based on your change.
We've run into the same thing, clustered servers. We resolved it by putting the server name into a text file and reading the text file into a variable.
<cfsavecontent variable="application.ServerName"><cfinclude template="/shared/server_name.txt"></cfsavecontent>
I've have a directory named 'sites' on my server located BELOW the root directory that contains named ini files.
So expandPath('/../sites/orders.ini') goes down below the site root and then back up into the sites folder.
This lets me place sever/site specific information there where it won't get clobbered by site file updates, syncs, and so on. It's also outside of the site root so it can't get downloaded by potentially malicious users.
@Ray - Correct... at my past employer, we had a series of Windows servers, all running CF. Each server was assigned one static external IP address. In IIS, we would assign the IP address and * to a default web site with only a blank html page and no access permissions.
This way, if you tried to access the server by IP or a spoofed URL, you would get the standard browser login and would be denied access.
Then, we would create separate web sites with explicit host names defined which would respond only to the valid domains.
This worked rather well, and I noticed a decrease in hack attempts since most malicious attacks are targeted at IP addresses.
I know this approach can be done in Apache and Sun One as well.
Whereas I also have done the cgi.server_name sniffing method in the past, I now exclusively do config files like JohnEric mentioned.
I use source control on the config files, but only for a _template_ of the config file, with sensitive details (like passwords, etc) that I wouldn't want in the repository, removed.
So if my config file is config.xml, I'd have a config-template.xml in the repo. Then server backups keep me safe from losing the exact settings on the server.
This is nice, because usually right before I deploy to production, I want to run the app completely in production mode, but before I put it ON the production server.
And a nice compromise between the two approaches... I have many times had a "Mode" setting as one of my configs, with either a "Production" or "Development" value, so I just have a cfif application.mode is "Development" condition for places where I want my cfdumps.
Just some thoughts.
To get the local machine name we just use this:
serverName = CreateObject("java", "java.net.InetAddress").getLocalHost().getHostName();
+1 @Jason
@Jason, @Scott P, Thats exactly what I suggested in comment #4. ;-)
Sorry Peter - it was surrounded by massive amounts of text and I overlooked it.
@Peter +2
:)
@Peter
Yours has an extra couple method calls I don't think you need
-
On a Windows host cfregistry can get you the machine name, which is alright if you can guarantee your code will always run on a windows box.
-
Maybe I'm in the minority, but I can't imagine cfregistry being enabled on a production environment? That scares me heh.
If you run CF in multiserver mode, you can get the name of the specific CF instance you're running on. That might help a bit, especially if you use unique names across a cluster (which I think was required in MX, but not in 7 or 8).
At any rate, it's an easy way for me to tell what environment I'm in without having to rely on URLs.
<cfscript>
host = CreateObject("java", "java.net.InetAddress").getLocalHost().getHostName();
cfserv = createObject("java","jrunx.kernel.JRun").getServerName();
</cfscript>
if cfserv contains 'dev' or 'test' or 'staging' I know I'm not on a production box; if host contains 1 or 2 I know which box in the cluster I'm on.
Yes, our config files are in SVN.
We've generally been using coldspring and modelglue for our newer sites. Our controllers are managed by CS and use the ModelGlue.Bean.CommonBeans.SimpleConfig class. In the application.cfc we set up a parent bean factory for MG. Then, in the controllers, there is a setBeanFactory method which also pulls the configuration bean so it is available throughout the application.
We also use ANT build scripts that can deploy to different environments.
Maybe I just the simplistic way out of things sometimes but, I simply do a search for a specific file on on the server. If that particular file exists, then it's the DEV server and display the dump. I normally make it some really screwy name that has nothing to do with the site I am building (like C64.TXT for working on a Mac site - grin). Then I run something like below as part of my error handling:
<code>
<cfif FileExists("C:\dev.txt")>
<cfdump var="#exception#">
<cfelse>
<p>
Something went wrong. Quick - do a 'LOAD "*",8,1'.
</p>
</cfif>
</code>
I think someone else further up the list mentioned this as well.
These are all great suggestions for a certain problem. But what if you're trying to write code that's independent of the OS the code is running on? What if you're proxying the request to another port on a CF dev server?
In my code, I don't want to have to setup the dev server and the production server differently. I want them to be the same so that I don't hit and unexpected error when deploying code.
@Michael Long's idea seems the most promising, but it needs something - independence of conf files.
I found this thread, googling "spoof coldfusion CGI" - which is interesting because I'm doing it for the sake of setting up a dev server, and it seems you're all talking about it for the sake of keeping hackers out.
From what I've tried, I can't alter the CGI scope from a simple cfset... I'm hoping there's a solution in java that would allow me to set the script_name.
Currently, since I'm reverse proxying the request with apache, I get a url with a port and an ugly repository path in it, that breaks some of the app logic [generous term for the bad code I'm fixing]. I've been able to clean it up in most cases, but my using CGI.SCRIPT_NAME are still breaking.