Massa-toewijzing, rails en u

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.


Wat is Mass Assignment?

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.


Het (potentiële) probleem met massatoewijzing

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.


Hoe om te gaan met massa-toewijzing

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 lijst

Gebruik 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 WhiteList

Het 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.

Massatoewijzingsrollen

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

Applicatie-brede configuratie

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.

striktheid

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.


Rails 4 Sterke parameters: een andere aanpak

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 edelsteen toe

Voeg de volgende regel toe aan Gemfile:

gem strong_parameters

Schakel modelgebaseerde bescherming tegen massa-toewijzing uit

Binnen config / application.rb:

config.active_record.whitelist_attributes = false

Vertel de modellen erover

klasse Gebruiker < ActiveRecord::Base include ActiveModel::ForbiddenAttributesProtection end

Werk de controllers bij

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.


Afsluiten

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!