Forum: Ruby on Rails HTTP Basic Authentication permitting wrong passwords through

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
3be29bee1806b097fc68621eb45f2e37?d=identicon&s=25 Ryan Waldron (rew)
on 2009-01-18 06:46
(Received via mailing list)
I've run into a strange problem with HTTP Basic authentication.  I've
observed this behavior on my dev box (connecting directly to mongrel)
and on an Apache+Passenger setup on my deployment machine.

I'm doing the standard thing according to the semi-holy trinity of
http_authentication.rb on github, Railscast #82, and
every-blog-tutorial-on-the-net: in my controller I have:

class CongsController < ApplicationController
   before_filter :authenticate, :only => [:edit, :delete, :update]

   ...

private

   def authenticate
     authenticate_or_request_with_http_basic do |username, password|
       username == 'fred' && password == 'sekr3t'
     end
   end
end

Sure enough, attempts to edit, update, or delete bring up the HTTP
basic dialog in the browser, and I have to enter a name and password.
If I enter them correctly, it passes me through properly.

The problem it also lets me through no matter WHAT I enter, right or
wrong.

This is what I see this in the Rails log file:

Processing CongsController#edit (for 127.0.0.1 at 2009-01-17 23:25:27)
[GET]
   Parameters: {"id"=>"1276"}
   SQL (0.1ms)   SET SQL_AUTO_IS_NULL=0
Filter chain halted as [:authenticate] rendered_or_redirected.
Completed in 0ms (View: 0, DB: 0) | 401 Unauthorized
[http://localhost/congs/1276/edit]

Processing CongsController#edit (for 127.0.0.1 at 2009-01-17 23:25:30)
[GET]
   Parameters: {"id"=>"1276"}
   SQL (0.1ms)   SET SQL_AUTO_IS_NULL=0
   Cong Columns (4.6ms)   SHOW FIELDS FROM `congs`
   Cong Load (15.0ms)   SELECT * FROM `congs` WHERE (`congs`.`id` =
   1276)
Rendering congs/edit
Completed in 36ms (View: 7, DB: 20) | 200 OK
[http://localhost/congs/1276/edit]

I can make it simpler yet:  I can use this #authenticate method, and
it still lets me through:

   def authenticate
     return false;
   end

My project's script/about says this:

Mac:~/src/rails/coc(master)> script/about
About your application's environment
Ruby version              1.8.6 (i686-darwin8.8.2)
RubyGems version          1.3.1
Rails version             2.2.2
Active Record version     2.2.2
Action Pack version       2.2.2
Active Resource version   2.2.2
Action Mailer version     2.2.2
Active Support version    2.2.2
Application root          /Users/rew/src/rails/coc
Environment               development
Database adapter          mysql
Database schema version   20090114205156

This is a VERY simple app; no tricky stuff going on, just a basic CRUD
thing with a couple of models.  I have no idea what is going on here.

Anybody know what I'm doing wrong here? Ideas or suggestions?

--
Ryan Waldron    |||   http://www.erebor.com    |||    rew@erebor.com

"The web goes ever, ever on, down from the site where it began..."
Ed437e52d8d6720308720e7e678f3e6d?d=identicon&s=25 Patrick Doyle (Guest)
on 2009-01-19 02:26
(Received via mailing list)
On Sun, Jan 18, 2009 at 12:45 AM, Ryan Waldron <rew@erebor.com> wrote:

>   before_filter :authenticate, :only => [:edit, :delete, :update]
> end
> Processing CongsController#edit (for 127.0.0.1 at 2009-01-17 23:25:27)
>   SQL (0.1ms)   SET SQL_AUTO_IS_NULL=0
>   def authenticate
> Active Record version     2.2.2
> thing with a couple of models.  I have no idea what is going on here.
>
> Anybody know what I'm doing wrong here? Ideas or suggestions?
>
> According to the documentation (see e.g.
http://www.railsbrain.com/api/rails-2.2.2/doc/inde...)
if a #before_filter renders or redirects, the second half of an around
filter, and any after filters won't run.  I believe that you need to
redirect your unauthenticated user to some other page (such as your
login
page) if the authentication fails.

I thought I had read somewhere (perhaps in a book someplace) that if a
filter returns false, it simply skips the rest of the processing in the
filter chain -- but I don't see that in the documentation for filters.
Perhaps it is just well known lore.  (Or, perhaps, it's something I just
made up ;-).  Regardless, if you think about what's going in the
#before_filter chain, it's just a bunch of processing that get's called
prior to calling your action method.  If you want that processing to
redirect the user somewhere else, then you need to explicitly put that
in
your processing.  If you don't put a specific redirect in your
processing,
how would Rails know if and to where it should redirect?

In my (one and only) application, I have

before_filter :login_required

#login_required is provided by the authenticated system plugin and looks
like

    def login_required
      authorized? || access_denied
    end

My particular version of #access_denied looks like:

  def access_denied
    respond_to do |format|
      format.html do
        flash[:notice] = "You must log in first"
        store_location
        redirect_to root_path
      end
      format.any do
        request_http_basic_authentication 'Web Password'
      end
    end
  end

hope this helps a little...

--wpd
3be29bee1806b097fc68621eb45f2e37?d=identicon&s=25 Ryan Waldron (rew)
on 2009-01-19 07:50
(Received via mailing list)
Hey, Patrick!  Thanks for the reply.

On Sun, Jan 18, 2009 at 7:25 PM, Patrick Doyle <wpdster@gmail.com>
wrote:

> On Sun, Jan 18, 2009 at 12:45 AM, Ryan Waldron <rew@erebor.com> wrote:
>
>>
>> I've run into a strange problem with HTTP Basic authentication.  I've
>> observed this behavior on my dev box (connecting directly to mongrel)
>> and on an Apache+Passenger setup on my deployment machine.
>>
>

> According to the documentation (see e.g.
> http://www.railsbrain.com/api/rails-2.2.2/doc/inde...)
> if a #before_filter renders or redirects, the second half of an around
> filter, and any after filters won't run.  I believe that you need to
> redirect your unauthenticated user to some other page (such as your login
> page) if the authentication fails.
>

That may be true, but that's not how I understand it's supposed to work.
If
authorization fails, then authenticate_or_request_with_basic_http is
supposed to render a 401 (I believe) with this message:

        controller.__send__ :render, :text => "HTTP Basic: Access
denied.\n", :status => :unauthorized

So the controller knows where to redirect to by virtue of it being
hardcoded.

And the Rails documentation, as well as every other place I've seen
showing
how this works has it pretty much just like I have it.

I've either got a typo that I can't find, or have set something up
screwy in
my app configuration, or something.  I don't *think* that it's because
I'm
supposed to explicitly redirect unauthorized users elsewhere.  But I
could
be wrong.

Keep in mind that I'm not trying to build a full user-based auth system;
I
just want HTTP basic user/pass protection for a few actions in a single
controller, just to help discourage the curious.  So I'm not using any
of
the auth plugins or full-blown user login schemes available.

Am I missing something?
3be29bee1806b097fc68621eb45f2e37?d=identicon&s=25 Ryan Waldron (rew)
on 2009-01-22 17:52
(Received via mailing list)
OK, in the spirit of full disclosure (not that anyone cares), I'll admit
my
boneheadedness here.  I HAD a problem the first time I tried the
authentication in the standard way:

  def authenticate
    authenticate_or_request_with_http_basic do |username, password|
      username == 'fred' && password == 'sekr3t'
    end
  end

But it wasn't with the authenticate method.  By this point, I have no
idea
what was wrong initially.  But what I had actually done was something
like:

  def authenticate
    authenticate_or_request_with_http_basic do |username, password|
      if (username == 'fred' && password == 'sekr3t')
        logger.info("Passed with username: #{username} password:
#{password}")
        return true;
      else
        logger.info("Failed with username: #{username} password:
#{password}")
        return false;
      end
    end
  end

Oops.  Of course, you don't 'return' values for a block.  Duh.  I knew
that,
but just didn't see it for a day or so because I was looking for
something
deeper and less stupid.

Ah, well.  Maybe I won't forget this for a while.  On the plus side, I
got
to walk through how before_filters are called using ruby-debug.

New knowledge FTW!
This topic is locked and can not be replied to.