Begin 2012 maakte een ontwikkelaar, Egor Homakov, gebruik van een beveiligingslek bij Github (een Rails-app) om toegang te krijgen tot het Rails-project..
Zijn bedoeling was vooral om een gemeenschappelijk beveiligingsprobleem aan te wijzen met veel Rails-apps die het resultaat zijn van een functie, bekend als massatoewijzing (en deed dat nogal luid). In dit artikel zullen we bekijken wat massa-toewijzing is, hoe het een probleem kan zijn en wat u eraan kunt doen in uw eigen toepassingen.
Om te beginnen, laten we eerst eens kijken naar wat massa-opdracht betekent, en waarom het bestaat. Stel je bij wijze van voorbeeld voor dat we het volgende hebben Gebruiker
klasse in onze applicatie:
# Neem de volgende velden aan: [: id,: first,: last,: email] class User < ActiveRecord::Base end
Massa-opdracht stelt ons in staat om een aantal attributen tegelijk in te stellen:
attrs = : first => "John",: last => "Doe",: email => "[email protected]" user = User.new (attrs) user.first # => "John" user.last # => "Doe" user.email # => "[email protected]"
Zonder het gemak van massa-toewijzing, zouden we een toewijzingsverklaring moeten schrijven voor elk attribuut om hetzelfde resultaat te bereiken. Hier is een voorbeeld:
attrs = : first => "John",: last => "Doe",: email => "[email protected]" user = User.new user.first = attrs [: first] user.last = attrs [: last] user.email = attrs [: email] user.first # => "John" user.last # => "Doe" user.email # => "[email protected]"
Het is duidelijk dat dit vervelend en pijnlijk kan worden; dus buigen we aan de voeten van luiheid en zeggen, ja ja, massa-opdracht is een goede zaak.
Een probleem met scherpe gereedschappen is dat je jezelf ermee kunt snijden.
Maar wacht! Een probleem met scherpe gereedschappen is dat je jezelf ermee kunt snijden. Massa-opdracht vormt geen uitzondering op deze regel.
Stel nu dat onze kleine imaginaire toepassing de vaardigheid heeft gekregen om raketten af te vuren. Omdat we niet willen dat de wereld zich tot as wendt, voegen we een Boolean-toestemmingsveld toe aan de Gebruiker
model om te beslissen wie raketten kan afvuren.
class AddCanFireMissilesFlagToUsers < ActiveRecord::Migration def change add_column :users, :can_fire_missiles, :boolean, :default => vals einde
Laten we ook aannemen dat we een manier hebben waarop gebruikers hun contactgegevens kunnen bewerken: dit kan een vorm zijn die ergens toegankelijk is voor de gebruiker met tekstvelden voor de voornaam, achternaam en e-mailadres van de gebruiker.
Onze vriend John Doe besluit zijn naam te veranderen en zijn e-mailaccount bij te werken. Wanneer hij het formulier verzendt, zal de browser een verzoek indienen dat lijkt op het volgende:
PUT http://missileapp.com/users/42?user[first]=NewJohn&user[email][email protected]
De bijwerken
actie binnen de UsersController
kan er ongeveer zo uitzien:
def update user = User.find (params [: id]) if user.update_attributes (params [: user]) # Mass assignment! redirect_to home_path else render: einde bewerking bewerken
Gezien ons voorbeeldverzoek, de params
hash ziet er ongeveer zo uit:
: id => 42,: user => : first => "NewJohn",: email => "[email protected]" #: id - parsed by the router #: user - parsed from the incoming querystring
Laten we nu zeggen dat NewJohn een beetje stiekem wordt. U hebt niet per se een browser nodig om een HTTP-verzoek uit te kunnen vaardigen, daarom schrijft hij een script dat de volgende aanvraag doet:
PUT http://missileapp.com/users/42?user[can_fire_missiles]=true
Velden, zoals
:beheerder
,:eigenaar
, en:publieke sleutel
, zijn vrij gemakkelijk te raden.
Wanneer dit verzoek op onze bijwerken
actie, de update_attributes
oproep zal zien : can_fire_missiles => true
, en geef NewJohn de mogelijkheid om raketten af te vuren! Wee is ons geworden.
Dit is precies hoe Egor Homakov zichzelf toegang gaf tot het Rails-project. Omdat Rails zo conventioneel zwaar is, velden zoals :beheerder
, :eigenaar
, en :publieke sleutel
zijn vrij gemakkelijk te raden. Verder, als er geen bescherming op zijn plaats is, kunt u toegang krijgen tot dingen die u niet zou mogen aanraken.
Dus hoe beschermen we ons tegen een moedwillige massa-opdracht? Hoe voorkomen we dat de New Yorks van de wereld onze raketten afvuren met roekeloze overgave?
Gelukkig biedt Rails een paar hulpmiddelen om het probleem te verhelpen: attr_protected
en attr_accessible
.
attr_protected
: De zwarte lijstGebruik makend van attr_protected
, u kunt opgeven welke velden mogelijk nooit zijn massa-ly toewijsbare:
klasse Gebruiker < ActiveRecord::Base attr_protected :can_fire_missiles end
Nu, elke poging om de massa massaal toe te wijzen can_fire_missiles
attribuut zal mislukken.
attr_accessible
: The WhiteListHet probleem met attr_protected
is dat het te gemakkelijk is om te vergeten een nieuw geïmplementeerd veld aan de lijst toe te voegen.
Dit is waar attr_accessible
komt binnen. Zoals je misschien al geraden hebt, is het het tegenovergestelde van attr_protected
: geef alleen de attributen op die u in massa kunt toewijzen.
Als zodanig kunnen we onze overstap maken Gebruiker
klasse voor deze aanpak:
klasse Gebruiker < ActiveRecord::Base attr_accessible :first, :last, :email end
Hier zetten we expliciet uiteen wat er in massa kan worden toegewezen. Al het andere zal worden afgewezen. Het voordeel hiervan is dat als we bijvoorbeeld een toevoegen beheerder
vlag naar de Gebruiker
model, het zal automatisch veilig zijn voor massatoewijzing.
Als algemene regel, zou u moeten verkiezen attr_accessible
naar attr_protected
, omdat het je helpt om voorzichtig te zijn.
Rails 3.1 introduceerde het concept van massatoewijzing "rollen". Het idee is dat je anders kunt specificeren attr_protected
en attr_accessible
lijsten voor verschillende situaties.
klasse Gebruiker < ActiveRecord::Base attr_accessible :first, :last, :email # :default role attr_accessible :can_fire_missiles, :as => : admin #: admin rol end user = User.new (: can_fire_missiles => true) # gebruikt de: standaardrol user.can_fire_missiles # => false user2 = User.new (: can_fire_missiles => true,: as =>: admin) user.can_fire_missiles # => waar
U kunt het massatoewijzingsgedrag in uw toepassing regelen door de config.active_record.whitelist_attributes
instellen binnen de config / application.rb
het dossier.
Indien ingesteld op vals
, bescherming tegen massa-toewijzing wordt alleen geactiveerd voor de modellen waar u een hebt opgegeven attr_protected
of attr_accessible
lijst.
Indien ingesteld op waar
, massa-toewijzing is onmogelijk voor alle modellen tenzij ze een attr_protected
of attr_accessible
lijst. Merk op dat deze optie standaard is ingeschakeld vanuit Rails 3.2.3 forward.
Beginnend met Rails 3.2, is er bovendien een configuratieoptie om de striktheid van bescherming tegen massa-toewijzing te regelen: config.active_record.mass_assignment_sanitizer
.
Indien ingesteld op : strikte
, het zal een verhogen ActiveModel :: MassAssignmentSecurity :: Error
elke keer dat uw toepassing probeert om massa toe te wijzen, iets wat het niet zou moeten doen. U moet deze fouten expliciet behandelen. Vanaf v3.2 is deze optie voor u ingesteld in de ontwikkel- en testomgevingen (maar niet productie), vermoedelijk om u te helpen bij te houden waar problemen met massatoewijzing kunnen optreden.
Als dit niet wordt ingesteld, wordt de bescherming van de massatoewijzing in stilte behandeld, wat inhoudt dat alleen de attributen worden ingesteld die het zou moeten toebedelen, maar dat er geen foutmelding wordt weergegeven.
Massa toewijzing beveiliging gaat echt over het omgaan met niet-vertrouwde invoer.
Het Homakov-incident leidde tot een gesprek over bescherming van massale toewijzingen in de Rails-gemeenschap (en verder naar andere talen); een interessante vraag werd gesteld: behoort beveiliging van de massatoewijzing tot de modellaag?
Sommige applicaties hebben complexe autorisatievereisten. Proberen om alle speciale gevallen in de modellaag aan te pakken, kunnen zich onhandig en te gecompliceerd gaan voelen, vooral als je merkt dat je bepleisterd bent rollen
overal.
Een belangrijk inzicht hierbij is dat beveiliging van de massatoewijzing in feite gaat om het omgaan met niet-vertrouwde input. Omdat een Rails-applicatie gebruikersinvoer ontvangt in de controllerlaag, begonnen ontwikkelaars zich af te vragen of het misschien beter is om het probleem daar aan te pakken in plaats van ActiveRecord-modellen.
Het resultaat van deze discussie is de edelsteenparameters, beschikbaar voor gebruik met Rails 3, en een standaard in de komende Rails 4-release.
Ervan uitgaande dat onze rakettoepassing van toepassing is op Rails 3, kunnen we deze op de volgende manier bijwerken voor gebruik met de parameter Stong parameters:
Voeg de volgende regel toe aan Gemfile:
gem strong_parameters
Binnen config / application.rb
:
config.active_record.whitelist_attributes = false
klasse Gebruiker < ActiveRecord::Base include ActiveModel::ForbiddenAttributesProtection end
class UsersController < ApplicationController def update user = User.find(params[:id]) if user.update_attributes(user_params) # see below redirect_to home_path else render :edit end end private # Require that :user be a key in the params Hash, # and only accept :first, :last, and :email attributes def user_params params.require(:user).permit(:first, :last, :email) end end
Nu, als u zoiets probeert user.update_attributes (params)
, je krijgt een foutmelding in je applicatie. Je moet eerst bellen toestaan
op de params
hash met de sleutels die zijn toegestaan voor een specifieke actie.
Het voordeel van deze aanpak is dat je expliciet moet zijn over welke invoer je accepteert op het moment dat je met de invoer te maken hebt.
Notitie: Als dit een Rails 4-app was, dan is de controllercode alles wat we nodig hebben; de sterke parameters functionaliteit zal standaard worden ingebakken. Als gevolg hiervan heeft u de include in het model of de afzonderlijke edelsteen in Gemfile niet nodig.
Massatoewijzing kan een ongelooflijk nuttige functie zijn bij het schrijven van Rails-code. In feite is het bijna onmogelijk om zonder de Rails-code te schrijven. Jammer genoeg is ook de hersenloze massa-opdracht vol gevaar.
Hopelijk ben je nu uitgerust met de nodige hulpmiddelen om veilig te navigeren in het water van de massa-toewijzing. Hier zijn minder raketten!