Posted May 1, 2008 at
9:30 AM
Tags:
ColdFusion
Yesterday, Steve Stout brought it to my attention that my CFHttpSession.cfc ColdFusion component does not use any Referer spoofing. When I read that, I almost couldn't believe my eyes; I have spent a good amount of my time playing around with CFHttp and CFHttpParam and the fact that I forgot to put in referral spoofing blows my mind a little bit. So, this morning, I went back in and added the functionality. When you launch a new request with a given URL, you have the option to pass in a referer spoof as the second argument:
CFHttpSession.NewRequest( URL [, Referer ] )
But, I doubt that you will ever need it since I have also added in automatic referer usage based on previous URLs in the request flow. Everytime you make a new request, it checks to see if you have made a previous request with the same CFHttpSession.cfc instance. If you have, it uses the previous target URL and the current referer. This way, your page requests are really mimicing the action of a real browser.
In order to mimic a real browser, you have to think like a real user. How would a real user work? A real user wouldn't just jump directly to a form processing page. No, a real user would go to the form page first, then submit the form. This is the same mentality you have to use when using the CFHttpSession.cfc ColdFusion component; when you want to log into a site, you have to view the login page first, then submit the form with a subsequent request. This way, you can set up all the session cookies and live within the session rules set forth by the site for real live users.
Steve Stout also pointed out that he was getting connection issues when connecting to a HTTPS page on pb.com (which is actually how the discussion of referral spoofing came up). I don't have a login to pb.com, but I do have a login for PayPal.com which also uses HTTPS for their secure pages (as do most all secure pages). So, I ran some tests on logging into PayPal.com to make sure that the referral spoofing was working properly:
Launch code in new window » Download code as text file »
- <cfset objHttpSession = CreateObject(
- "component",
- "CFHTTPSession"
- ).Init()
- />
-
-
- <cfset objResponse = objHttpSession
- .NewRequest( "http://www.paypal.com" )
- .Get()
- />
-
-
- <cfset objResponse = objHttpSession
- .NewRequest( "https://www.paypal.com/us/cgi-bin/webscr" )
- .AddUrl( "cmd", "_login-submit" )
- .AddFormField( "login_email", "fergie@blackeyedpeas.com" )
- .AddFormField( "login_password", "myLadyLumps" )
- .AddFormField( "submit.x", "Log In" )
- .AddFormField( "form_charset", "UTF-8" )
- .Post()
- />
-
-
- <cfset objResponse = objHttpSession
- .NewRequest( "https://www.paypal.com/us/cgi-bin/webscr" )
- .AddUrl( "cmd", "_account" )
- .AddUrl( "nav", "0" )
- .Get()
- />
-
-
- <cfoutput>
-
- <h1>
- From My Server:
- </h1>
-
- <br />
-
- <div style="width: 500px ; height: 400 ; border: 4px solid gold ; overflow: auto ;">
- #objResponse.FileContent#
- </div>
-
- </cfoutput>
Notice that we are taking three steps to get to the Account Overview page:
- Go to homepage (login form)
- Submit login credentials
- Go to account overview page
Using this page flow, we mimic a real user and build a proper sessoin. And, running the code above, we get the following output:
As you can see, using ColdFusion and CFHttpSession.cfc, I was able to successfully log into my PayPal.com account and then further access my Account Overview page.
Here is the code for the updated CFHttpSessoin.cfc with referral spoofing (and a few other updates here and there):
Launch code in new window » Download code as text file »
- <cfcomponent
- output="false"
- hint="Handles a CFHTTP session by sending an receving cookies behind the scenes.">
-
- <cfset VARIABLES.Instance = {} />
-
- <cfset VARIABLES.Instance.Cookies = {} />
-
- <cfset VARIABLES.Instance.RequestData = {} />
- <cfset VARIABLES.Instance.RequestData.Url = "" />
- <cfset VARIABLES.Instance.RequestData.Referer = "" />
- <cfset VARIABLES.Instance.RequestData.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6" />
- <cfset VARIABLES.Instance.RequestData.Params = [] />
-
-
- <cffunction
- name="Init"
- access="public"
- returntype="any"
- output="false"
- hint="Returns an initialized component.">
-
- <cfargument
- name="UserAgent"
- type="string"
- required="false"
- hint="The user agent that will be used on the subseqent page requests."
- />
-
- <cfif StructKeyExists( ARGUMENTS, "UserAgent" )>
- <cfset THIS.SetUserAgent( ARGUMENTS.UserAgent ) />
- </cfif>
-
- <cfreturn THIS />
- </cffunction>
-
-
- <cffunction
- name="AddCGI"
- access="public"
- returntype="any"
- output="false"
- hint="Adds a CGI value. Returns THIS scope for method chaining.">
-
- <cfargument
- name="Name"
- type="string"
- required="true"
- hint="The name of the CGI value."
- />
-
- <cfargument
- name="Value"
- type="string"
- required="true"
- hint="The CGI value."
- />
-
- <cfargument
- name="Encoded"
- type="string"
- required="false"
- default="yes"
- hint="Determins whether or not to encode the CGI value."
- />
-
- <cfreturn THIS.AddParam(
- Type = "CGI",
- Name = ARGUMENTS.Name,
- Value = ARGUMENTS.Value,
- Encoded = ARGUMENTS.Encoded
- ) />
- </cffunction>
-
-
- <cffunction
- name="AddCookie"
- access="public"
- returntype="any"
- output="false"
- hint="Adds a cookie value. Returns THIS scope for method chaining.">
-
- <cfargument
- name="Name"
- type="string"
- required="true"
- hint="The name of the CGI value."
- />
-
- <cfargument
- name="Value"
- type="string"
- required="true"
- hint="The CGI value."
- />
-
- <cfreturn THIS.AddParam(
- Type = "Cookie",
- Name = ARGUMENTS.Name,
- Value = ARGUMENTS.Value
- ) />
- </cffunction>
-
-
- <cffunction
- name="AddFile"
- access="public"
- returntype="any"
- output="false"
- hint="Adds a file value. Returns THIS scope for method chaining.">
-
- <cfargument
- name="Name"
- type="string"
- required="true"
- hint="The name of the form field for the posted file."
- />
-
- <cfargument
- name="Path"
- type="string"
- required="true"
- hint="The expanded path to the file."
- />
-
- <cfargument
- name="MimeType"
- type="string"
- required="false"
- default="application/octet-stream"
- hint="The mime type of the posted file. Defaults to *unknown* mime type."
- />
-
- <cfreturn THIS.AddParam(
- Type = "Cookie",
- Name = ARGUMENTS.Name,
- Value = ARGUMENTS.Value
- ) />
- </cffunction>
-
-
- <cffunction
- name="AddFormField"
- access="public"
- returntype="any"
- output="false"
- hint="Adds a form value. Returns THIS scope for method chaining.">
-
- <cfargument
- name="Name"
- type="string"
- required="true"
- hint="The name of the form field."
- />
-
- <cfargument
- name="Value"
- type="string"
- required="true"
- hint="The form field value."
- />
-
- <cfargument
- name="Encoded"
- type="string"
- required="false"
- default="yes"
- hint="Determins whether or not to encode the form value."
- />
-
- <cfreturn THIS.AddParam(
- Type = "FormField",
- Name = ARGUMENTS.Name,
- Value = ARGUMENTS.Value,
- Encoded = ARGUMENTS.Encoded
- ) />
- </cffunction>
-
-
- <cffunction
- name="AddHeader"
- access="public"
- returntype="any"
- output="false"
- hint="Adds a header value. Returns THIS scope for method chaining.">
-
- <cfargument
- name="Name"
- type="string"
- required="true"
- hint="The name of the header value."
- />
-
- <cfargument
- name="Value"
- type="string"
- required="true"
- hint="The header value."
- />
-
- <cfreturn THIS.AddParam(
- Type = "Header",
- Name = ARGUMENTS.Name,
- Value = ARGUMENTS.Value
- ) />
- </cffunction>
-
-
- <cffunction
- name="AddParam"
- access="public"
- returntype="any"
- output="false"
- hint="Adds a CFHttpParam data point. Returns THIS scope for method chaining.">
-
- <cfargument
- name="Type"
- type="string"
- required="true"
- hint="The type of data point."
- />
-
- <cfargument
- name="Name"
- type="string"
- required="true"
- hint="The name of the data point."
- />
-
- <cfargument
- name="Value"
- type="any"
- required="true"
- hint="The value of the data point."
- />
-
- <cfargument
- name="File"
- type="string"
- required="false"
- default=""
- hint="The expanded path to be used if the data piont is a file."
- />
-
- <cfargument
- name="MimeType"
- type="string"
- required="false"
- default=""
- hint="The mime type of the file being passed (if file is being passed)."
- />
-
- <cfargument
- name="Encoded"
- type="string"
- required="false"
- default="yes"
- hint="The determines whether or not to encode Form Field and CGI values."
- />
-
- <cfset var LOCAL = {} />
-
- <cfswitch expression="#ARGUMENTS.Type#">
-
- <cfcase value="Body">
-
- <cfset LOCAL.Param = {
- Type = ARGUMENTS.Type,
- Value = ARGUMENTS.Value
- } />
-
- </cfcase>
-
- <cfcase value="CGI">
-
- <cfset LOCAL.Param = {
- Type = ARGUMENTS.Type,
- Name = ARGUMENTS.Name,
- Value = ARGUMENTS.Value,
- Encoded = ARGUMENTS.Encoded
- } />
-
- </cfcase>
-
- <cfcase value="Cookie">
-
- <cfset LOCAL.Param = {
- Type = ARGUMENTS.Type,
- Name = ARGUMENTS.Name,
- Value = ARGUMENTS.Value
- } />
-
- </cfcase>
-
- <cfcase value="File">
-
- <cfset LOCAL.Param = {
- Type = ARGUMENTS.Type,
- Name = ARGUMENTS.Name,
- File = ARGUMENTS.File,
- MimeType = ARGUMENTS.MimeType
- } />
-
- </cfcase>
-
- <cfcase value="FormField">
-
- <cfset LOCAL.Param = {
- Type = ARGUMENTS.Type,
- Name = ARGUMENTS.Name,
- Value = ARGUMENTS.Value,
- Encoded = ARGUMENTS.Encoded
- } />
-
- </cfcase>
-
- <cfcase value="Header">
-
- <cfset LOCAL.Param = {
- Type = ARGUMENTS.Type,
- Name = ARGUMENTS.Name,
- Value = ARGUMENTS.Value
- } />
-
- </cfcase>
-
- <cfcase value="Url">
-
- <cfset LOCAL.Param = {
- Type = ARGUMENTS.Type,
- Name = ARGUMENTS.Name,
- Value = ARGUMENTS.Value
- } />
-
- </cfcase>
-
- <cfcase value="Xml">
-
- <cfset LOCAL.Param = {
- Type = ARGUMENTS.Type,
- Value = ARGUMENTS.Value
- } />
-
- </cfcase>
-
- </cfswitch>
-
-
- <cfset ArrayAppend(
- VARIABLES.Instance.RequestData.Params,
- LOCAL.Param
- ) />
-
- <cfreturn THIS />
- </cffunction>
-
-
- <cffunction
- name="AddUrl"
- access="public"
- returntype="any"
- output="false"
- hint="Adds a url value. Returns THIS scope for method chaining.">
-
- <cfargument
- name="Name"
- type="string"
- required="true"
- hint="The name of the header value."
- />
-
- <cfargument
- name="Value"
- type="string"
- required="true"
- hint="The header value."
- />
-
- <cfreturn THIS.AddParam(
- Type = "Url",
- Name = ARGUMENTS.Name,
- Value = ARGUMENTS.Value
- ) />
- </cffunction>
-
-
- <cffunction
- name="Get"
- access="public"
- returntype="struct"
- output="false"
- hint="Uses the GET method to place the next request. Returns the CFHttp response.">
-
- <cfargument
- name="GetAsBinary"
- type="string"
- required="false"
- default="auto"
- hint="Determines how to return the file content - return as binary value."
- />
-
- <cfargument
- name="Debug"
- type="boolean"
- required="false"
- default="false"
- hint="If this is true, then the response object will be dumped out and the page will be aborted."
- />
-
- <cfreturn THIS.Request(
- Method = "get",
- GetAsBinary = ARGUMENTS.GetAsBinary,
- Debug = ARGUMENTS.Debug
- ) />
- </cffunction>
-
-
- <cffunction
- name="GetCookies"
- access="public"
- returntype="struct"
- output="false"
- hint="Returns the internal session cookies.">
-
- <cfreturn VARIABLES.Instance.Cookies />
- </cffunction>
-
-
- <cffunction
- name="NewRequest"
- access="public"
- returntype="any"
- output="false"
- hint="Sets up the object for a new request. Returns THIS scope for method chaining.">
-
- <cfargument
- name="Url"
- type="string"
- required="true"
- hint="The URL for the new request."
- />
-
- <cfargument
- name="Referer"
- type="string"
- required="false"
- default=""
- hint="The referring URL for the request. By default, it will be the same directory as the target URL."
- />
-
-
- <cfif Len( VARIABLES.Instance.RequestData.Url )>
-
- <cfset VARIABLES.Instance.RequestData.Referer = VARIABLES.Instance.RequestData.Url />
-
- </cfif>
-
-
- <cfset VARIABLES.Instance.RequestData.Url = ARGUMENTS.Url />
-
- <cfif Len( ARGUMENTS.Referer )>
-
- <cfset VARIABLES.Instance.RequestData.Referer = ARGUMENTS.Referer />
-
- </cfif>
-
-
- <cfset VARIABLES.Instance.RequestData.Params = [] />
-
- <cfreturn THIS />
- </cffunction>
-
-
- <cffunction
- name="Post"
- access="public"
- returntype="struct"
- output="false"
- hint="Uses the POST method to place the next request. Returns the CFHttp response.">
-
- <cfargument
- name="GetAsBinary"
- type="string"
- required="false"
- default="auto"
- hint="Determines how to return the file content - return as binary value."
- />
-
- <cfargument
- name="Debug"
- type="boolean"
- required="false"
- default="false"
- hint="If this is true, then the response object will be dumped out and the page will be aborted."
- />
-
- <cfreturn THIS.Request(
- Method = "post",
- GetAsBinary = ARGUMENTS.GetAsBinary,
- Debug = ARGUMENTS.Debug
- ) />
- </cffunction>
-
-
- <cffunction
- name="Request"
- access="public"
- returntype="struct"
- output="false"
- hint="Performs the CFHttp request and returns the response.">
-
- <cfargument
- name="Method"
- type="string"
- required="false"
- default="get"
- hint="The type of request to make."
- />
-
- <cfargument
- name="GetAsBinary"
- type="string"
- required="false"
- default="auto"
- hint="Determines how to return body."
- />
-
- <cfargument
- name="Debug"
- type="boolean"
- required="false"
- default="false"
- hint="If this is true, then the response object will be dumped out and the page will be aborted."
- />
-
- <cfset var LOCAL = {} />
-
- <cfhttp
- url="#VARIABLES.Instance.RequestData.Url#"
- method="#ARGUMENTS.Method#"
- useragent="#VARIABLES.Instance.RequestData.UserAgent#"
- getasbinary="#ARGUMENTS.GetAsBinary#"
- redirect="no"
- result="LOCAL.Get">
-
- <cfloop
- item="LOCAL.Key"
- collection="#VARIABLES.Instance.Cookies#">
-
- <cfhttpparam
- type="cookie"
- name="#LOCAL.Key#"
- value="#VARIABLES.Instance.Cookies[ LOCAL.Key ].Value#"
- />
-
- </cfloop>
-
-
-
-
- <cfhttpparam
- type="header"
- name="referer"
- value="#VARIABLES.Instance.RequestData.Referer#"
- />
-
-
- <cfloop
- index="LOCAL.Param"
- array="#VARIABLES.Instance.RequestData.Params#">
-
- <cfhttpparam
- attributecollection="#LOCAL.Param#"
- />
-
- </cfloop>
-
- </cfhttp>
-
-
- <cfif ARGUMENTS.Debug>
-
- <cfdump var="#VARIABLES.Instance.RequestData#" />
- <cfset WriteOutput( LOCAL.Get.FileContent ) />
- <cfdump var="#LOCAL.Get#" />
- <cfabort />
-
- </cfif>
-
-
- <cfset StoreResponseCookies( LOCAL.Get ) />
-
-
- <cfif StructKeyExists( LOCAL.Get.ResponseHeader, "Location" )>
-
- <cfif REFindNoCase(
- "^http",
- LOCAL.Get.ResponseHeader.Location
- )>
-
- <cfreturn THIS
- .NewRequest( LOCAL.Get.ResponseHeader.Location )
- .Get()
- />
-
- <cfelse>
-
- <cfreturn THIS
- .NewRequest(
- GetDirectoryFromPath( VARIABLES.Instance.RequestData.Url ) &
- LOCAL.Get.ResponseHeader.Location
- )
- .Get()
- />
-
- </cfif>
-
- <cfelse>
-
- <cfreturn LOCAL.Get />
-
- </cfif>
- </cffunction>
-
-
- <cffunction
- name="SetBody"
- access="public"
- returntype="any"
- output="false"
- hint="Sets the body data of next request. Returns THIS scope for method chaining.">
-
- <cfargument
- name="Value"
- type="any"
- required="false"
- hint="The data body."
- />
-
- <cfreturn THIS.AddParam(
- Type = "Body",
- Name = "",
- Value = ARGUMENTS.Value
- ) />
- </cffunction>
-
-
- <cffunction
- name="SetUserAgent"
- access="public"
- returntype="any"
- output="false"
- hint="Sets the user agent for next request. Returns THIS scope for method chaining.">
-
- <cfargument
- name="Value"
- type="string"
- required="false"
- hint="The user agent that will be used on the subseqent page requests."
- />
-
- <cfset VARIABLES.Instance.RequestData.UserAgent = ARGUMENTS.UserAgent />
-
- <cfreturn THIS />
- </cffunction>
-
-
- <cffunction
- name="SetXml"
- access="public"
- returntype="any"
- output="false"
- hint="Sets the XML body data of next request. Returns THIS scope for method chaining.">
-
- <cfargument
- name="Value"
- type="any"
- required="false"
- hint="The data body."
- />
-
- <cfreturn THIS.AddParam(
- Type = "Xml",
- Name = "",
- Value = ARGUMENTS.Value
- ) />
- </cffunction>
-
-
- <cffunction
- name="StoreResponseCookies"
- access="public"
- returntype="void"
- output="false"
- hint="This parses the response of a CFHttp call and puts the cookies into a struct.">
-
- <cfargument
- name="Response"
- type="struct"
- required="true"
- hint="The response of a CFHttp call."
- />
-
- <cfset var LOCAL = StructNew() />
-
- <cfset LOCAL.Cookies = StructNew() />
-
- <cfif NOT StructKeyExists(
- ARGUMENTS.Response.ResponseHeader,
- "Set-Cookie"
- )>
-
- <cfreturn />
-
- </cfif>
-
-
-
-
- <cfif IsSimpleValue( ARGUMENTS.Response.ResponseHeader[ "Set-Cookie" ] )>
-
- <cfset LOCAL.ReturnedCookies = {} />
- <cfset LOCAL.ReturnedCookies[ 1 ] = ARGUMENTS.Response.ResponseHeader[ "Set-Cookie" ] />
-
- <cfelse>
-
- <cfset LOCAL.ReturnedCookies = ARGUMENTS.Response.ResponseHeader[ "Set-Cookie" ] />
-
- </cfif>
-
-
- <cfloop
- item="LOCAL.CookieIndex"
- collection="#LOCAL.ReturnedCookies#">
-
- <cfset LOCAL.CookieString = LOCAL.ReturnedCookies[ LOCAL.CookieIndex ] />
-
-
- <cfloop
- index="LOCAL.Index"
- from="1"
- to="#ListLen( LOCAL.CookieString, ';' )#"
- step="1">
-
- <cfset LOCAL.Pair = ListGetAt(
- LOCAL.CookieString,
- LOCAL.Index,
- ";"
- ) />
-
- <cfset LOCAL.Name = ListFirst( LOCAL.Pair, "=" ) />
-
- <cfif (ListLen( LOCAL.Pair, "=" ) GT 1)>
-
- <cfset LOCAL.Value = ListRest( LOCAL.Pair, "=" ) />
-
- <cfelse>
-
- <cfset LOCAL.Value = "" />
-
- </cfif>
-
-
- <cfif (LOCAL.Index EQ 1)>
-
- <cfset LOCAL.Cookies[ LOCAL.Name ] = StructNew() />
-
- <cfset LOCAL.Cookie = LOCAL.Cookies[ LOCAL.Name ] />
-
-
- <cfset LOCAL.Cookie.Value = LOCAL.Value />
-
-
- <cfset LOCAL.Cookie.Attributes = StructNew() />
-
- <cfelse>
-
- <cfset LOCAL.Cookie.Attributes[ LOCAL.Name ] = LOCAL.Value />
-
- </cfif>
-
- </cfloop>
-
-
- </cfloop>
-
-
- <cfset StructAppend(
- VARIABLES.Instance.Cookies,
- LOCAL.Cookies
- ) />
-
- <cfreturn />
- </cffunction>
-
- </cfcomponent>
Download Code Snippet ZIP File
Comments (10) |
Post Comment |
Ask Ben |
Permalink |
Other Searches |
Print Page
What Other People Are Searching For
[ local search ]
log into paypal with coldfusion
[ local search ]
coldfusion paypal login
[ local search ]
referer spoof coldfusion
And I was scrolling, and scrolling... Jeez. Nice work, and lots of it!
Posted by Sami Hoda
on May 1, 2008
at 12:22 PM
@Sami,
Thanks man.
Posted by Ben Nadel
on May 1, 2008
at 12:28 PM
Ben,
As always, this is incredible. This is hopefully going to completely solve a problem we've had with automating deployment of our sites, which have to go through a 3rd party CMS system.
Anyway, I've run into an issue, where the redirect due to a Location caused a problem when the redirection wasn't a full redirect with http:// but instead an absolute redirect. It appends that new location onto the full request URL, which means that you get a 404.
Maybe an example is easiest.
http://apple.com/foo
302 Moved Temporarily
Location: /bar
With the way it's currently programmed, the new request goes to http://apple.com/foo/bar, and you get a 404.
I made a change to the component that seems to work on my end, but I'm sure I'm missing something. Let me know if you want a copy of the changed code.
Thanks again for this great tool!
Posted by Toby Reiter
on May 1, 2008
at 1:55 PM
@Toby,
The CFHttpSession.cfc component, when it finds a locational redirect is checking to see if the URL passed back starts with "http:". If it does, then is uses the given URL. If it does NOT, then it takes the