Search code examples
phpruby-on-railshttprequestcsrfsugarcrm

How do I send CSRF token authenticity from a php app that I own to a rails app that I own?


I am an admin for a sugarCRM instance and I have a rails app on heroku. I want to be able to automatically add contacts to the rails app if they are added in sugarCRM. I have written a before_save logic_hook in my sugarCRM:

    function pushConts($bean, $event, $arguments)
    {
        $r = new HttpRequest('http://localhost:3000/contacts/', HttpRequest::METH_POST);
        $r->addPostFields(array('first_name' => $bean->first_name, 'last_name' => $bean->last_name, 'phone' => $bean->phone_mobile,'email' => $bean->email1, ));
        //$r->addHeaders(array('X-CSRF-Token'=> 'testing-csrf-token');
        try
        {
            echo $r->send()->getBody();
        } catch(HttpException $ex){
            SugarApplication::appendErrorMessage("<span style='color: red; font-size: 1.8em;'>Could Not Save Contact: Rails app is down.</span>");
            $queryParams = array('module' => 'Contacts', 'action' => 'ListView');
            SugarApplication::redirect('index.php?' . http_build_query($queryParams));          
        }

When I try to send this as it is, the console output from the rails app is:

Started POST "/contacts/" for 127.0.0.1 at 2015-06-18 15:30:14 -0500
Processing by ContactsController#create as */*
Parameters: {"first_name"=>"contact", "last_name"=>"one", "phone"=>"(555) 555-5555", "email"=>"[email protected]"}
Can't verify CSRF token authenticity
Completed 422 Unprocessable Entity in 1ms

ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
actionpack (4.2.0) lib/action_controller/metal/request_forgery_protection.rb:181:in `handle_unverified_request'
actionpack (4.2.0) lib/action_controller/metal/request_forgery_protection.rb:209:in `handle_unverified_request'
devise (3.4.1) lib/devise/controllers/helpers.rb:251:in `handle_unverified_request'

<stack trace continues>

I know the logic hook itself works because I tried adding skip_before_filter :verify_authenticity_token to my contacts controller and then it worked as expected but for security reasons, this is not a feasible solution.

As you can see commented out, I tried sending an X-CSRF-Token with the headers but that didn't work either.

What can I add either to this logic hook or to my rails app itself (or both) so that I can send Http requests from my sugarCRM to my rails app without compromising (too much) security?


Solution

  • The Rails CSRF system is not really intended to work across domains or servers. It leverages synchronizer tokens (cryptographically random tokens) which are bound to the user's session.

    Since Rails and SugarCRM do not share a user session it's impossible for Rails to validate a CSRF token from SugarCRM.

    Your best bet would be to turn it off for the action with skip_before_filter, only: [:create].

    If you need something truly secure to verify that the request is from your SugarCRM server you would need to use something like token based authentication.

    Many Rails based API's use a special controller and route for actions that can be performed by API clients. In your case it would look something like this:

    POST /api/v1/contacts

    # routes.rb
    namespace :api do
      namespace :v1 do
        resources :contacts
      end
    end
    
    # controllers/api/v1/api_controller
    class ApiController < ActionController::Base
      skip_before_filter, only: [:create] 
    
      def authenticate
        # @todo implement token based auth
      end
    end
    
    # controllers/api/v1/contacts_controller
    class Api::V1::ContactsController
    
      before_action :authenticate
    
      def create
        @contact = Contact.new(contact_params)
    
        # ...
      end
    
      # ...
    end