I was wondering if anyone had thoughts on the most efficient way of making sure users never see internal table IDs? Clearly, scaffold views show a lot of IDs by default and those can be hidden. The problem seems to be all of the IDs that Rails passes around in URLs (such as http://mysite.com/user/show/12345). My primary concerns are security and confidentiality--one can imagine that there are exploits that could involve knowledge about ID numbers or simply guessing random table row IDs and putting them into URLs to see what happens. Combined with a few coding mistakes this could lead to a catastrophic security/privacy breakdown. I''m also concerned about the ability of clever people to discern how how much activity/signups a commercial web site is getting by looking at auto-incremented ID numbers that are assigned to various signups/posts/etc (various schemes for implementing non-sequential IDs all seem kludgy and inefficient to me, but willing to be corrected as always). Surely I can''t be the first person to ponder this issue. Any thoughts at all are appreciated. Mike J.
On 5/3/06, Michael Johnson <none@plzdontspam.us> wrote:> I was wondering if anyone had thoughts on the most efficient way of > making sure users never see internal table IDs? Clearly, scaffold views > show a lot of IDs by default and those can be hidden. The problem seems > to be all of the IDs that Rails passes around in URLs (such as > http://mysite.com/user/show/12345). > > My primary concerns are security and confidentiality--one can imagine > that there are exploits that could involve knowledge about ID numbers or > simply guessing random table row IDs and putting them into URLs to see > what happens. Combined with a few coding mistakes this could lead to a > catastrophic security/privacy breakdown. I''m also concerned about the > ability of clever people to discern how how much activity/signups a > commercial web site is getting by looking at auto-incremented ID numbers > that are assigned to various signups/posts/etc (various schemes for > implementing non-sequential IDs all seem kludgy and inefficient to me, > but willing to be corrected as always).If the page isn''t public information, include an authorization routine in a before filter. Usually you would do this by having the user login, storing information in the session, and checking the session information in the before filter. That way users can only see the information that they should have access to.
Jeremy Evans wrote:> On 5/3/06, Michael Johnson <none@plzdontspam.us> wrote: >> I was wondering if anyone had thoughts on the most efficient way of >> making sure users never see internal table IDs? Clearly, scaffold views >> show a lot of IDs by default and those can be hidden. The problem seems >> to be all of the IDs that Rails passes around in URLs (such as >> http://mysite.com/user/show/12345). > If the page isn''t public information, include an authorization routine > in a before filter. Usually you would do this by having the user > login, storing information in the session, and checking the session > information in the before filter. That way users can only see the > information that they should have access to.Sorry, I did a weak job of explaining myself. I''m not trying to protect pages--I''m already using the SaltedHashLoginGenerator and filtering so users can only get to information that belongs to them. My issue is that I don''t ever want users to see internal table ID column values. E.g. I never want to see URLs like: http://mysite/user/new?referred_by_user=27 http://mysite/order/show/113 A sneaky user could determine approximately how many orders my client company is generating per day by making an order in the morning, making an order in the evening, then subtracting the order_id numbers (assuming the database is using standard auto-increment IDs). And if some programmer on my project accidentally fails to filter the orders so each user can only see their own, then someone could easily start typing in http://mysite/order/show/114, /115, /116, etc. and see whatever they like. So for this example one solution would be to generate a complex and unique order number that is hard to guess and have the URL go to that instead (e.g., http://mysite/order/show/ax53nmfjw2312343k2 -- let''s not have a discussion about how you could still employ a brute-force attack to guess valid numbers, etc). That one specific example probably has a really, really simple answer (I''m still a noob so please do tell--save me the ten minutes it will take to figure it out!) but I was hoping anyone had comments on preventing all such exposed table ID usage application-wide that doesn''t require a lot of extra work and/or making sure every programmer understands a bunch of conventions that aren''t reflected in the code in some way. I''m really hoping not to have to cobble together a random ID generating scheme of some sort because I find those to be problematic (how does a support staffer adding a data record manually generate a random ID? How does the guy writing a Perl script get an ID? More work, more cost, every time someone needs to touch a table.)
Hi, What your urls look like is defined in the file routes.rb. The default configuration is :controller/:action/:id . Feel free to change this to whatever suits your needs. It can be :controller/:action/:hashed_id, and the action will receive a hased_id it can decode. You also can remove the controller and action if you can figure out what they are from the string returned by the user. Hope this helps, Nicolas On 5/3/06, Michael Johnson <none@plzdontspam.us> wrote:> > Jeremy Evans wrote: > > On 5/3/06, Michael Johnson <none@plzdontspam.us> wrote: > >> I was wondering if anyone had thoughts on the most efficient way of > >> making sure users never see internal table IDs? Clearly, scaffold views > >> show a lot of IDs by default and those can be hidden. The problem seems > >> to be all of the IDs that Rails passes around in URLs (such as > >> http://mysite.com/user/show/12345). > > If the page isn''t public information, include an authorization routine > > in a before filter. Usually you would do this by having the user > > login, storing information in the session, and checking the session > > information in the before filter. That way users can only see the > > information that they should have access to. > Sorry, I did a weak job of explaining myself. I''m not trying to protect > pages--I''m already using the SaltedHashLoginGenerator and filtering so > users can only get to information that belongs to them. My issue is that > I don''t ever want users to see internal table ID column values. E.g. I > never want to see URLs like: > > http://mysite/user/new?referred_by_user=27 > http://mysite/order/show/113 > > A sneaky user could determine approximately how many orders my client > company is generating per day by making an order in the morning, making > an order in the evening, then subtracting the order_id numbers (assuming > the database is using standard auto-increment IDs). And if some > programmer on my project accidentally fails to filter the orders so each > user can only see their own, then someone could easily start typing in > http://mysite/order/show/114, /115, /116, etc. and see whatever they > like. So for this example one solution would be to generate a complex > and unique order number that is hard to guess and have the URL go to > that instead (e.g., http://mysite/order/show/ax53nmfjw2312343k2 -- let''s > not have a discussion about how you could still employ a brute-force > attack to guess valid numbers, etc). > > That one specific example probably has a really, really simple answer > (I''m still a noob so please do tell--save me the ten minutes it will > take to figure it out!) but I was hoping anyone had comments on > preventing all such exposed table ID usage application-wide that doesn''t > require a lot of extra work and/or making sure every programmer > understands a bunch of conventions that aren''t reflected in the code in > some way. I''m really hoping not to have to cobble together a random ID > generating scheme of some sort because I find those to be problematic > (how does a support staffer adding a data record manually generate a > random ID? How does the guy writing a Perl script get an ID? More work, > more cost, every time someone needs to touch a table.) > > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails >-------------- next part -------------- An HTML attachment was scrubbed... URL: http://wrath.rubyonrails.org/pipermail/rails/attachments/20060503/ad7fc11e/attachment.html
On 5/3/06, Michael Johnson <none@plzdontspam.us> wrote:> > I''m really hoping not to have to cobble together a random ID > generating scheme of some sort because I find those to be problematic > (how does a support staffer adding a data record manually generate a > random ID? How does the guy writing a Perl script get an ID? More work, > more cost, every time someone needs to touch a table.)It sounds like what you are asking for is to represent each record with a GUID. There is a plugin that will let you accomplish this which follows an industry standard way of creating unique identifiers (thus solving your perl script problem described above). See reference below: http://wiki.rubyonrails.com/rails/pages/Uses+Guid+Plugin -------------- next part -------------- An HTML attachment was scrubbed... URL: http://wrath.rubyonrails.org/pipermail/rails/attachments/20060503/a7a66ebc/attachment-0001.html
> My issue is that I don''t ever want users to see internal table ID > column values.I sometimes accomplish this by storing the IDs in a session hash. I don''t know if I''d advocate this approach if there are hundreds of choices, but it works well if a user has only a few choices. The idea is simply to store the actual IDs sequentially in the session hash and then reference them by position instead of by ID. For example (I''ll use ''Option'' as a generic model): session[:option_list] = @member.options.collect {|option| [option.id, option.name, option.foobar] } session[:selected_option] = 0 Then in the view, something like: <% for item in session[:option_list] -%> <%= link_to(item[1], :action => ''something'', :id => session[: option_list].index(item) + 1)) %> <% end -%> And then from a link call an action where you use the :id (which is not really the id but just a number 1 through options.count + 1) to look up the actual ID from the hash. session[: selected_option] = (params[:id].to_i - 1) if params [:id].to_i > 0 option_selected = session[: option_list][session[:selected_option]] @option = Option.find(option_selected[0]) Not only does it hide the IDs from the user view but it will only look up IDs contained in the hash, which keeps users from "peeking over the fence at their neighbors". You don''t have to use session[:selected_option] but it''s handy if you want to maintain that selection around as they continue to navigate pages in an area. HTH, Kevin Skoglund
1. You could also probably do some sneaky routing to get the ID numbers in the URL to refer to the order number of the current user. That way all they would see is how many order they have placed. something like... order belongs_to :customer order acts_as_list then do @order = Order.find_by_position(params[:position], :conditions=>["customer_id = ?", params[:customer_id]) 2. You can''t get away from having to be careful about checking to see if the user has authorized access to the particular record. On Wednesday, May 03, 2006, at 12:20 PM, Josh Knowles wrote:>On 5/3/06, Michael Johnson <none@plzdontspam.us> wrote: >> >> I''m really hoping not to have to cobble together a random ID >> generating scheme of some sort because I find those to be problematic >> (how does a support staffer adding a data record manually generate a >> random ID? How does the guy writing a Perl script get an ID? More work, >> more cost, every time someone needs to touch a table.) > > > >It sounds like what you are asking for is to represent each record with a >GUID. There is a plugin that will let you accomplish this which follows an >industry standard way of creating unique identifiers (thus solving >your perl >script problem described above). See reference below: > >http://wiki.rubyonrails.com/rails/pages/Uses+Guid+Plugin > > >_______________________________________________ >Rails mailing list >Rails@lists.rubyonrails.org >http://lists.rubyonrails.org/mailman/listinfo/rails >_Kevin -- Posted with http://DevLists.com. Sign up and save your mailbox.
Nice idea Nicolas, thanks -- any idea if there is some way of centralizing the hashed ID handling so it''s completely automatic instead of forcing each and every action to unravel the IDs? Something like forcing the request to go to a controller that can unhash the ID then redirect to where you wanted to go originally. Mike Nicolas Buet wrote:> Hi, > > What your urls look like is defined in the file routes.rb. The default > configuration is :controller/:action/:id . Feel free to change this to > whatever suits your needs. It can be :controller/:action/:hashed_id, > and the action will receive a hased_id it can decode. You also can > remove the controller and action if you can figure out what they are > from the string returned by the user. > > Hope this helps, > > Nicolas > > On 5/3/06, *Michael Johnson* <none@plzdontspam.us > <mailto:none@plzdontspam.us>> wrote: > > Jeremy Evans wrote: > > On 5/3/06, Michael Johnson <none@plzdontspam.us > <mailto:none@plzdontspam.us>> wrote: > >> I was wondering if anyone had thoughts on the most efficient way of > >> making sure users never see internal table IDs? Clearly, > scaffold views > >> show a lot of IDs by default and those can be hidden. The > problem seems > >> to be all of the IDs that Rails passes around in URLs (such as > >> http://mysite.com/user/show/12345). > > If the page isn''t public information, include an authorization > routine > > in a before filter. Usually you would do this by having the user > > login, storing information in the session, and checking the session > > information in the before filter. That way users can only see the > > information that they should have access to. > Sorry, I did a weak job of explaining myself. I''m not trying to > protect > pages--I''m already using the SaltedHashLoginGenerator and filtering so > users can only get to information that belongs to them. My issue > is that > I don''t ever want users to see internal table ID column values. E.g. I > never want to see URLs like: > > http://mysite/user/new?referred_by_user=27 > http://mysite/order/show/113 > > A sneaky user could determine approximately how many orders my client > company is generating per day by making an order in the morning, > making > an order in the evening, then subtracting the order_id numbers > (assuming > the database is using standard auto-increment IDs). And if some > programmer on my project accidentally fails to filter the orders > so each > user can only see their own, then someone could easily start typing in > http://mysite/order/show/114, /115, /116, etc. and see whatever they > like. So for this example one solution would be to generate a complex > and unique order number that is hard to guess and have the URL go to > that instead (e.g., http://mysite/order/show/ax53nmfjw2312343k2 -- > let''s > not have a discussion about how you could still employ a brute-force > attack to guess valid numbers, etc). > > That one specific example probably has a really, really simple answer > (I''m still a noob so please do tell--save me the ten minutes it will > take to figure it out!) but I was hoping anyone had comments on > preventing all such exposed table ID usage application-wide that > doesn''t > require a lot of extra work and/or making sure every programmer > understands a bunch of conventions that aren''t reflected in the > code in > some way. I''m really hoping not to have to cobble together a random ID > generating scheme of some sort because I find those to be problematic > (how does a support staffer adding a data record manually generate a > random ID? How does the guy writing a Perl script get an ID? More > work, > more cost, every time someone needs to touch a table.) >-------------- next part -------------- An HTML attachment was scrubbed... URL: http://wrath.rubyonrails.org/pipermail/rails/attachments/20060504/875274dd/attachment.html
Josh, the GUID plugin is a great find--thanks much for the pointer. The only reason I would balk at using GUIDs is because you can no longer just go into a database table and stick a record in manually--everything has to be done through a tool of some sort. That being said you could cobble together a script for that in minutes so not a major drawback. Ever wonder why MySQL doesn''t have an automatic GUID column or a auto-random field? Sheesh. I suppose I could just switch to a database that has something like that. Mike Josh Knowles wrote:> On 5/3/06, *Michael Johnson* <none@plzdontspam.us > <mailto:none@plzdontspam.us>> wrote: > > I''m really hoping not to have to cobble together a random ID > generating scheme of some sort because I find those to be problematic > (how does a support staffer adding a data record manually generate a > random ID? How does the guy writing a Perl script get an ID? More > work, > more cost, every time someone needs to touch a table.) > > > > It sounds like what you are asking for is to represent each record > with a GUID. There is a plugin that will let you accomplish this > which follows an industry standard way of creating unique identifiers > (thus solving your perl script problem described above). See > reference below: > > http://wiki.rubyonrails.com/rails/pages/Uses+Guid+Plugin-------------- next part -------------- An HTML attachment was scrubbed... URL: http://wrath.rubyonrails.org/pipermail/rails/attachments/20060504/adb9e633/attachment.html