Simovits

Python 2 är dött, leve Python 2!

Det är nu drygt en månad kvar av det nuvarande året, och med det närmare sig döden för Python 2. Efter första januari 2020 kommer väsentligen allt officiellt stöd för Python 2 försvinna, inklusive utfärdandet av nya säkerhetsuppdateringar. Mängden program ute i det vilda som är skrivna i det äldre Python 2 är dock så omfattande att det kommer dröja lång tid innan alla dessa har skrivits om för Python 3. Samlevnad av dessa två versioner kommer därför oundvikligen att vara ett faktum över de nästkommande åren. Samtidigt är det så att en ny utvecklare som idag (eller i framtiden) lär sig Python kommer att lära sig Python 3. Likaväl kan denna få i uppgift att underhålla och utöka funktionaliteten hos ett tidigare program skrivit i Python 2. Med bristande förståelse om skillnaden dem emellan kan säkerhetsbrister uppstå.

Med ena foten i Python 3 och andra i Python 2

Låt oss för stunden bortse ifrån allt vad uppdateringar beträffar och istället betrakta ett exempel på en risk som uppstår till följd av förändringar mellan hur en vanligt förekommande funktion fungerar i Python 2 och Python 3.

En junior utvecklare har blivit ombedd att lägga till funktionalitet hos ett Python 2 program som gör att en användare kan ange ett värde. Detta skulle exempelvis kunna vara ett värde som används i någon form av beräkning eller simulation. Utvecklaren använder sig av input funktionen för att ta emot data, och utför sedan en kontrollerad konvertering (med undantagshantering) till ett heltal eller flyttal enligt vanligt Python 3 förfarande. Antagandet är att all mottagen indata lagras i form av en sträng. Utvecklaren vet med sig att Python 3 är framtiden och skriver därför koden så att den ska kunna köras i båda versionerna, för att förenkla eventuell framtida uppdatering.

Förenklat så förekommer följande kod:

from __future__ import print_function

given_input = input ("Enter number : ")

try:
		
	given_number = int(given_input)
	## Do something
except ValueError:
	print ("Could not convert to number") 

Vad skiljer sig i Python 2 jämfört med Python 3 för denna kod? Mycket visar det sig. Låt oss testa vad som händer när vi kör följande kod med de olika Python versionerna.

Först testar vi med Python 3:

Inga konstigheter, vid inmatande av något som inte kan konverteras till en siffra så uppstår ett undantag som hanteras.

Låt oss se skillnaden när vi kör samma skript fast denna gång med Python 2:

När vi kollar felmeddelandet när vi skriver text istället för siffror ser vi något intressant, det ser ut som att Python klagar på att den stöter på en odefinierad variabel! Vi testar att försöka skriva Python kod direkt in, vilket visar sig fungera! Det går alltså för en användare att exekvera godtycklig kod på värdmaskinen som kör detta skript.

Skillnaden mellan dessa två fall beror på följande: Sättet att samla in inmatning från användare har förändrats mellan de olika versionerna. I Python 2 finns det främst två funktioner som båda kan användas, ”input” och ”raw_input”, medans enbart ”input” finns i Python 3 av dessa två. Vad är skillnaden mellan input och raw_input? Den första hanterar allt som Python-kod, som den sedan omedelbart exekverar; medans raw_input tolkar allt som en sträng. I andra ord, raw_input fungerar i princip som input gör i Python 3. En utvecklare som inte vet skillnaden på hur två identiskt namngivna funktioner fungerar i olika versioner av Python kan alltså gå i fällan att använda input istället för raw_input i Python 2. Detta illustrerar en risk som inte relaterar till frånvaron av säkerhetsuppdateringar, utan är en konsekvens av att grundläggande saker i språket ändras mellan versioner.

Versionskännedom i framtiden

I den här bloggen har ett kraftigt förenklat exempel används för att illustrera risker som uppstår till följd av bristande versionskännedom rörande Python 2 och 3. Fallet här är enkelt att lösa genom att lägga till, i början av skriptet, något i stil med:

try:
    input = raw_input
except:
    pass

Detta pekar om referensen i input variabeln till raw_input funktionen, vilket fungerar likartat med Python 3 input funktionen.

Slutsatsen är att även framtida utvecklare kommer behöva veta hur Python 2 fungerar, och vad för skillnader som finns mellan denna och version 3. Risken är annars att en ny kategori av sårbarheter dyker upp, vilket inte beror på någon omedveten brist, utan är en följd av namnbyte samt hur en gammal funktion var tänkt att användas.