7 Replies Latest reply: Apr 23, 2012 10:31 AM by CFBYSTANDER RSS

    WSDL ColdFusion version visible - need it hidden

    CFBYSTANDER

      I have a web service and the auto generated WSDL at http://mysite/webservice.cfc?wsdl includes the full ColdFusion version number in comments on the second line of the WSDL.
      E.G. <!--WSDL created by ColdFusion version 9,0,1,274733-->

       

      The example on http://help.adobe.com/en_US/ColdFusion/9.0/Developing/WSc3ff6d0ea77859461172e0811cbec22c24 -78a6.html shows the comment as:

      <!--WSDL created by ColdFusion -->

       

      Is there a production style setting that can be used to hide this version number when auto generating the WSDL?

      Alternatively, is the auto generated WSDL stored temporarily on the CF server that can be read and edited onRequestStart and returned instead of the originally auto generated WSDL?

       

      I know i can set the "wsdlFile" property to a specific WSDL file that I can edit to remove the version number in webservice.cfc but I wanted to avoid having to manually maintain the WSDL.

       

      Thanks in advance for any assistane with this.

       

      Tom

        • 1. Re: WSDL ColdFusion version visible - need it hidden
          BKBK MVP

          CFBYSTANDER wrote:

           

          I have a web service and the auto generated WSDL at http://mysite/webservice.cfc?wsdl includes the full ColdFusion version number in comments on the second line of the WSDL.
          E.G. <!--WSDL created by ColdFusion version 9,0,1,274733-->

           

          The example on http://help.adobe.com/en_US/ColdFusion/9.0/Developing/WSc3ff6d0ea77859 461172e0811cbec22c24-78a6.html shows the comment as:

          <!--WSDL created by ColdFusion -->

          This is to be expected. It is traditional for text or code generators to include their signature. ColdFusion does it subtly, using a comment. Some generators actually include a 'live' header, comprising many lines!

           

          Is there a production style setting that can be used to hide this version number when auto generating the WSDL?

          Alternatively, is the auto generated WSDL stored temporarily on the CF server that can be read and edited onRequestStart and returned instead of the originally auto generated WSDL?

          I haven't looked into the inner workings ColdFusion's WSDL generation, but experience tells me it is unlikely that ColdFusion maintains a temporary store for WSDLs. I expect a WSDL to be generated either on the fly or from cache.

           

          That is also what you would hope for. You would want the WSDL to reflect changes you make in your CFC. Also, if you made no changes, you would want ColdFusion to use the cached version, instead of going to all the trouble of generating the WSDL again. 

           

          I know i can set the "wsdlFile" property to a specific WSDL file that I can edit to remove the version number in webservice.cfc

          That seems like a good workaround to me.

           

          Added update:

           

          I wanted to avoid having to manually maintain the WSDL.

           

          Remember that the WSDL is available through, for example:

           

          <cfhttp method="get" url="http://mysite/webservice.cfc?wsdl">

          • 2. Re: WSDL ColdFusion version visible - need it hidden
            CFBYSTANDER Community Member

            Thanks BKBK,

             

            My original problem with using the result from cfhttp was that it would timeout as OnRequestStart would enter a continuous loop as each cfhttp call would call the cfhttp code again. I tried other URL variables but that causes CF to open the CFC explorer which ruled that out. After a bit more sleep i have now solved the problem.
            What i've done is set an application variable on the first request and then call <cfhttp method="get" url="http://mysite/webservice.cfc?wsdl"> but then i check if that application variable is set and if it is i know that the request is from the 2nd request and that result.filecontent can then be used and the <!--WSDL created by ColdFusion version x.x.--> can be replaced and the application variable is deleted and then using cfcontent i can display the auto generated WSDL minus the CF version. It's a small processing overhead but resolves the issue that would have been highlighted during penetration testing.

             

            I'll post the code once i've tidied (removed cflog etc) it up a bit in case it is useful for other CF developers. Also, other people looking at your code and providing feedback is always beneficial.

            Cheers

            Tom

            • 3. Re: WSDL ColdFusion version visible - need it hidden
              BKBK MVP

              That is one fact you can count on: your solution will certainly help others.

              • 4. Re: WSDL ColdFusion version visible - need it hidden
                CFBYSTANDER Community Member

                I've had to take out sensitive pieces of code from the application.cfc but this onRequestStart function should strip the ColdFusion version from a .cfc?wsdl request:

                 

                  <cffunction name="onRequestStart" returnType="boolean" output="no">

                    <cfargument name="targetPage" type="string" required="true">

                 

                    <cfif listLast(arguments.thePage,".") EQ "cfc" AND structKeyExists(url, "wsdl")>

                      <!--- need application.InterceptWSDL to avoid infinite loop and subsequent timeout. Otherwise the cfhttp request would keep requesting itself when it hits onRequestStart --->

                      <cfif NOT structKeyExists(application, "InterceptWSDL")>

                        <cflock scope="application" timeout="30">

                          <cfset application.InterceptWSDL = 1>

                        </cflock>

                        <cfhttp url="http://mysite/#getPathToCFC()##getFileFromPath(getbasetemplatepath())#?wsdl" method="get" charset="utf-8" timeout="20" result="GeneratedWSDL" />  

                      <cfelse>

                        <!--- the code below should only run when the cfhttp request is made --->

                        <cflock scope="application" timeout="30">

                          <cfset structdelete(application, "InterceptWSDL")>

                        </cflock>

                      </cfif>

                 

                      <cfif structKeyExists(variables, "GeneratedWSDL") and len(GeneratedWSDL.fileContent)>

                        <cfprocessingdirective suppresswhitespace="Yes">

                        <cfcontent type="text/xml; charset=utf-8">

                        <cfoutput>

                          #REReplaceNoCase(GeneratedWSDL.filecontent, "<!--WSDL created by ColdFusion [A-Z0-9 ,-->]*\r\n", "", "ALL")#

                        </cfoutput>

                        </cfprocessingdirective>

                        <!--- without aborting here the auto generated WSDL is displayed --->

                        <cfabort>

                      </cfif>

                 

                      <cfset structDelete(this, "onRequest")>

                      <cfset structDelete(variables, "onRequest")>

                    </cfif>

                    <cfreturn true>

                  </cffunction>

                 

                I'm not overly keen on having to use cfabort but it's the only way, so far, i've found that stops the CF auto generated WSDL from being displayed.

                 

                Cheers

                Tom

                • 5. Re: WSDL ColdFusion version visible - need it hidden
                  BKBK MVP

                  Sorry I don't quite understand the reasoning behind your code. I expected no locks, and that you would write to file somewhere. What are the 2 structdeletes at the end for? Were you aiming for something like this?

                   

                  <cffunction name="onRequestStart" returnType="boolean" output="no">

                  <cfargument name="targetPage" type="string" required="true">

                  <cfif listLast(arguments.targetPage,".") EQ "cfc" AND lcase(CGI.QUERY_STRING) EQ "wsdl">

                      <!--- need application.InterceptWSDL to avoid infinite loop and subsequent timeout. Otherwise the cfhttp request would keep requesting itself when it hits onRequestStart --->

                      <cfif NOT structKeyExists(application, "InterceptWSDL")>

                          <cfset application.InterceptWSDL = 1>

                          <cfhttp url="http://mysite/#targetPage#?wsdl" method="get" charset="utf-8" timeout="20" result="GeneratedWSDL" /> 

                          <cfset newWSDL = REReplaceNoCase(GeneratedWSDL.filecontent, "<!--WSDL created by ColdFusion [A-Z0-9 ,-->]*\r\n", "", "ALL")>     

                          <cffile action="write" file="absolute-path-to-wsdl-file" output="#newWSDL#" >

                          <cfelse>

                          <!--- the code below should only run when the cfhttp request is made --->

                          <cfset structdelete(application, "InterceptWSDL")>

                      </cfif>

                  </cfif>

                  <cfreturn true>

                  </cffunction>

                   

                  In any case, I wouldn't implement this at the start of every request. I wouldn't wish to do anything in Application.cfc when the client invokes the web service. A web service call is a contract between server and client. Putting obstacles in the way of the call may be considered to be a breach of that contract.

                   

                  Also, a service shouldn't be changing on the fly. In fact, it takes a while for the CFC of a web service to change. One would therefore expect that the WSDL will remain the same for a while.

                   

                  So, the way I would do it would be to clean up the WSDL at selected moments of my own choosing. I would use a CFM page dedicated to that purpose.

                   

                  The CFM file would grab the WSDL by means of cfhttp, and strip off the offending comment line, as you have done. The result will be written to file, using cffile. Then the <cfcomponent> tag for the web service would include the attributes style and wsdlfile.

                   

                  I would perhaps only use onRequestStart to check for permission to modify WSDLs. For example, let's say, I expect the request to the CFM page to be something like this, where the webservice is Customer.cfc:

                   

                  http://www.mydomain.com/services/conf/modifyWSDL.cfm?wsdl=customer&code=47AC6b0f928Ee15d

                   

                  Then I would implement something like this in onRequestStart:

                   

                  <cfif listLast(arguments.targetPage,"/") EQ "modifyWSDL.cfm">

                  <cfif NOT structKeyExists(URL,"wsdl") OR NOT structKeyExists(URL,"code") OR NOT (compareNoCase(url.code, "47ac6b0f928ee15d") EQ 0)>

                      <!--- redirect to permission-refused page --->

                  </cfif>   

                  </cfif>

                  • 6. Re: WSDL ColdFusion version visible - need it hidden
                    CFBYSTANDER Community Member

                    If the application.cfc contains onRequest() and it is not deleted during onRequestStart() for WSDL requests then no output is rendered to the screen. This is because onRequest() expects a file to be included and none is. For displaying the generated WSDL I do not wish to include a template so I delete the method for requests for WSDL. If the application.cfc does not contain onRequest then the delete is not needed.

                     

                    The locks are present because the application scope is not single threaded in onRequestStart().

                     

                    The code does not do anything when the web service is actually invoked. It is only when the WSDL is requested that the code is processed. Since the documentation and the implementation of the web service are separate I don't think changing the way the WSDL is returned on the fly is a breach of the web service call contract.

                     

                    The WSDL should change very rarely, I agree. Which is another reason for having the WSDL generated automatically as any manual processing would be needed so rarely that it could be forgotten about even though it could be well documented.

                     

                    The issue with writing the WSDL to the file is that <cfcomponent> will only auto generate the WSDL if the wsdlFile attribute isn't present.

                    In order to have the <cfcomponent> auto generate the WSDL i'd need to deploy the <cfcomponent> without the wsdlFile attribute and then request it via cfhttp and then i'd need to write the WSDL, with CF version removed, to the server using cffile and then i'd need to alter the <cfcomponent> containing the remote function so that it contained the wsdlFile attribute. Since the production code is overwritten regularly with the code from our version control system this would mean adding a scheduled job to generate the wsdlFile. Also after any scheduled release the job would need to be run too as the WSDL file would be deleted during the release. Either that or the manual process would be needed across multiple CF servers and instances.

                     

                    Having the WSDL modified when it is requested means the production code matches what's in version control and means I won't have to manually maintain any files. So, if the CFC changes and the auto generated WSDL changes those changes are available immediately and with no developer/admin effort.

                     

                    Thank you for taking the time to look at this problem and look at the code I wrote as it has made me think about the details a lot more closely even though our solutions to the problem are ultimately quite different.

                     

                    Cheers

                    Tom

                    • 7. Re: WSDL ColdFusion version visible - need it hidden
                      CFBYSTANDER Community Member

                      There was an issue with the original solution where if you hit the page with enough simultaneous requests you would have application.InterceptWSDL defined even though you weren't the request that was calling CFHTTP. Whilst an unlikely problem it kind of defeats the purpose of not wanting to show the ColdFusion version and patch number in the WSDL for penetration testing purposes. I've bypassed that problem by ditching the application.interceptWSDL and using the userAgent attribute on CFHTTP. I normally avoid the CGI scope since it can vary by web server but in this case it seems the best solution. Here's the new code:

                       

                      <cffunction name="onRequestStart" returnType="boolean" output="no">

                      <cfargument name="targetPage" type="string" required="true">

                        <cfif listLast(arguments.targetPage,".") EQ "cfc" AND structkeyexists(url, "wsdl")>>

                          <cfif cgi.User_Agent NEQ "UniqueWSDLIdentiferAgent">

                            <!--- this will stop the cfhttp request calling itself since cgi.User_Agent will be changed for this request only --->

                            <cfhttp useragent="UniqueWSDLIdentiferAgent" url="http://mysite#arguments.thePage#?wsdl" method="get" charset="utf-8" timeout="20" result="GeneratedWSDL" />

                          </cfif>

                       

                          <cfif structKeyExists(variables, "GeneratedWSDL") and len(GeneratedWSDL.fileContent)>

                            <cfprocessingdirective suppresswhitespace="Yes">

                            <cfcontent type="text/xml; charset=utf-8">

                            <cfoutput>

                            #REReplaceNoCase(GeneratedWSDL.filecontent, "<!--WSDL created by ColdFusion [A-Z0-9 ,-->]*\r\n", "", "ALL")#

                            </cfoutput>

                            </cfprocessingdirective>

                            <!--- without aborting here the auto generated WSDL is displayed --->

                            <Cfabort>

                          </cfif>

                          <!--- if we don't delete onRequest then it will fire and no output will be rendered to the screen as no file will be included by onRequest(). This is because OnRequest() is being fired for the cfhttp request and this then stops further processing. --->

                          <cfset structDelete(this, "onRequest")>

                          <cfset structDelete(variables, "onRequest")>

                        </cfif>

                        <cfreturn true>

                      </cffunction>

                       

                      Hope that helps anyone doing this in the future.

                       

                      Cheers

                      Tom