onsdag 30 september 2009

DDS: Programmera med personnummer

I en tidigare bloggpost så introducerade jag tillsammans med Dan Bergh Johnsson begreppet Domändriven säkerhet (DDS). Den här bloggposten utgår från den.


Så kommer då verkligheten. Ett riktigt system ska utvecklas och domändriven säkerhet ska praktiseras. Mitt senaste projekt var en nationell webbapplikation och web service inom svensk sjukvård.


Ett centralt begrepp i vår domänmodell var svenska personnummer. Systemen som anropar tjänsten anger patientens personnummer och personnumret är primärnyckel i databasen.


Därför tog vi en rad beslut för att vår kod ska hantera personnummer korrekt, allt enligt domändriven säkerhet.


  • Personnummer ska heta just 'personnummer' i koden. Detta trots att kodkommentarer, metod-, variabel- och klassnamn i övrigt är på engelska. 'Social security number' betyder något helt annat och 'Personal number' har ingen specifik betydelse. 'Personnummer' är en central del av domänmodellen och måste benämnas rätt i programkoden.
  • Det finns bara en klass som representerar ett personnummer i hela systemet. Den är väl dokumenterad och beskriver på engelska vad ett personnummer är.
  • Inkommande personnummer är förstås strängar men förutom en inledande koll i parsern att storleken är OK så valideras inte inkommande personnummer förrän ett riktigt personnummerobjekt skapas. Därför ska ett sånt objekt skapas så fort som möjligt. Skälet att vi kontrollerar storleken direkt i parsern är att vi vill motverka DoS-attacker som skickar in 1 Mb data i personnummerfältet.
  • Personnummer valideras i fallande komplexitetsordning enligt: längd (12 eller 13 tecken), tillåtna tecken (siffror och '-'), syntax (12 siffror eller 8 siffror '-' 4 siffror) samt semantik (kontrollsiffra enligt Luhnalgoritmen).


Allt väl så. Det var till och med roligt att implementera! Här kommer så min kollega Kalle Gustafssons och min implementation av svenska personnummer i Java. Notera camel case med särskrivning "PersonNummer" :).


package model;

import java.io.Serializable;
import java.util.Calendar;
import java.util.TimeZone;

import model.support.ChecksumSupport;
import support.log.Logger;
import support.log.SystemLogger;

/**
* A class representing a 12 digit Swedish personnummer (Swedish personal
* identification number). To avoid ambiguity the proper Swedish word is chosen
* instead of trying to define an English equivalent.
* <p>
* A personnummer is formatted in the following way:
* YYYYMMDD-BBBX where BBB is a three digit birth number (odd for men, even for women)
* and X is a check digit calculated from the birth date and birth number.
* <p>
* This class can also handle a Swedish samordningsnummer (co-ordination number)
* where the figure for the birthday is increased by the number 60 and then the
* check digit is calculated. For further reading see SKV 707 utg 2.</p>
* <p>
* Instances of this class are immutable value objects.</p>
* <p>
*
* @author Kalle Gustafsson, Omegapoint AB
* @author John Wilander, Omegapoint AB
*/
public class PersonNummer implements Serializable {
private static final long serialVersionUID = -160928058318117179L;

private static final Logger LOG = SystemLogger.getLogger(PersonNummer.class);

private static final int EARLIEST_YEAR = 1840;
private static final TimeZone STOCKHOLM_TIME_ZONE = TimeZone.getTimeZone("Europe/Stockholm");

private final long m_personNummer; // 12 digit no dash

/**
* Create a personnummer from a twelve digit code that may or may not have
* a dash between the date and the extension.
*
* @param code The personnummer
* @throws IllegalArgumentException On invalid input.
*/
protected PersonNummer(String code) throws InvalidPersonNummerException {
validate(code);
if (code.length() == 13) {
code = code.substring(0, 8) + code.substring(9);
}
m_personNummer = Long.parseLong(code);
}

/**
* This is declared package-private for the unit tests.
*/
static long validate(String code) throws InvalidPersonNummerException {
if (code.length() != 12 && (!(code.length() == 13 && code.charAt(8) == '-'))) {
throw new InvalidPersonNummerException("Must be 12 digits", code);
}

if (code.charAt(8) == '-') { // Remove dash if present
code = code.substring(0, 8) + code.substring(9, 13);
}

Calendar c = Calendar.getInstance(STOCKHOLM_TIME_ZONE);
c.clear();
c.setLenient(false);
try {
StringBuilder dateCode = new StringBuilder(code.substring(0, 8));
int coNumCheckInt = Integer.parseInt(dateCode.substring(6, 7));
if (coNumCheckInt > 3) { // Samordningsnummer (co-ordination number) -> date field
// incremented by 60. Read more in Skatteverket SKV 707, utg 2
dateCode.replace(6, 7, Integer.toString(coNumCheckInt - 6));
}

c.set(Integer.parseInt(dateCode.substring(0, 4)), Integer.parseInt(dateCode.substring(4, 6)) - 1, Integer.parseInt(dateCode.substring(6, 8)));

} catch (ArrayIndexOutOfBoundsException e) {
// Thrown by set if any value is out of range
throw new InvalidPersonNummerException(e.getMessage(), code);
} catch (NumberFormatException e) {
throw new InvalidPersonNummerException(e.getMessage(), code);
}

int year;
try {
year = c.get(Calendar.YEAR);
} catch (IllegalArgumentException e) {
// This has happened
throw new InvalidPersonNummerException(e.getMessage(), code);
}

if (year < EARLIEST_YEAR) {
throw new InvalidPersonNummerException("Year cannot be before " + EARLIEST_YEAR, code);
} else if (c.after(Calendar.getInstance(STOCKHOLM_TIME_ZONE))) {
throw new InvalidPersonNummerException("The date is in the future.", code);
}

int extension = 0;
try {
extension = Integer.parseInt(code.substring(8, 12));
} catch (NumberFormatException e) {
throw new InvalidPersonNummerException("The extension is not a number", code);
}
if (extension < 0) {
throw new InvalidPersonNummerException("The extension is less than 0", code);
} else if (extension > 9999) {
throw new InvalidPersonNummerException("The extension is greater than 9999", code);
}

long pid = Long.parseLong(code);
// Validate checksum. Don't include the leading two century digits
if (!ChecksumSupport.validateChecksum(pid % 10000000000L)) {
LOG.warn("Invalid checksum in personnummer: " + code + ". It should be " + ChecksumSupport.calculateChecksum(pid % 10000000000L));
}
return pid;
}

public String get12DigitWithDash() {
return (m_personNummer / 10000L) + "-" + String.format("%04d", m_personNummer % 10000L);
}

public String get12DigitNoDash() {
return "" + m_personNummer;
}

protected String get10DigitNoDash() {
return (get12DigitNoDash().substring(2));
}

protected String get10DigitWithDash() {
return (get12DigitWithDash().substring(2));
}

/**
* Get the personnummer as a 12-digit long.
*/
public long getCode() {
return m_personNummer;
}

@Override
public String toString() {
// Don't change this. Code that parses logs etc assumes that
// personnummers are formatted like this
return get12DigitWithDash();
}

@Override
public boolean equals(Object o) {
return (o != null) && (o instanceof PersonNummer) && (((PersonNummer) o).m_personNummer == m_personNummer);
}

@Override
public int hashCode() {
return (int) m_personNummer;
}
}

Och så supportklassen för beräkning och validering av checksummor enligt Luhnalgoritmen. Man vill gärna ha den som en utilityklass för att underlätta enhetstestning.

package model.support;

/**
* Utility class for validating personnummer-style checksums.
*
* @author Kalle Gustafsson, Omegapoint AB
*/
public final class ChecksumSupport {
/**
* Validate a personnummer style checksum.
* @param code The code to validate. The last digit of the code is the
* checksum.
* @return {@code true} if the checksum is valid.
*/
public static boolean validateChecksum(long code) {
int myChecksum = (int) (code % 10L);
int checksum = calculateChecksum(code / 10L);
return myChecksum == checksum;
}

/**
* Calculate the personnummer style checksum for the code.
*/
public static int calculateChecksum(long code) {
int cs = 0;
int multiple = 2;
while (code > 0) {
int pos = multiple * (int) (code % 10L);
cs += pos % 10 + pos / 10;
multiple = (multiple == 1 ? 2 : 1);
code = code / 10L;
}

// Subtract the sum modulo 10 from 10.
// The remainder becomes the checksum. If the remainder is 10 the checksum i 0.
return (10 - (cs % 10)) % 10;
}
}

Som sagt, allt väl så. Men det kom mera. Plötsligt fick vi ett felmeddelande i produktion. Någon hade angivit en patient med ett personnummer som slutade på 'Y'. Var det en bugg i anropande system? Eller kan personnummer ha bokstäver? Fortsättning följer, i en kommande bloggpost ...

[Uppdatering] Del 2 har utkommit och finns här.

måndag 28 september 2009

770 DDoS-attacker per dygn

Snacket om förra månadens DDoS-attack mot den georgiske bloggaren gick vidare med en uppföljning om mängden DDoS-attacker och vart de riktar sig.


Arbor Networks driver det stora ATLAS-projektet där de samarbetar med 70 % av världens ISP:er för att samla in globala data om maskar, intrång, phishing, botnät och DDoS-attacker.


Arbors analys visar att inte mindre än 770 DDoS-attacker pågick samma dag som attacken mot georgiern. Och i jämförelse var Georgien-attacken ganska blygsam. En asiatisk 3G-operatör utsattes för 30 Gbps trafik, dvs 3 Gb data i sekunden.


DDoS-attacker är en kamp mellan resurser. Attackeraren slåss med sina tusentals fjärrstyrda datorer (bots) och offret försvarar sig med sin nätverks- och serverkapacitet. Tragiskt nog är det billigare att köpa ett kraftfullt bot-nät än ett kraftfullt försvar.


De största botnäten har hundratusentals fjärrstyrda datorer men som tur är används de nästan aldrig för DDoS-attacker utan för att distribuera spam och malware. Frågan är om något företag i världen skulle kunna stå emot en samlad DDoS-attack från 300.000 datorer?


Dark Readings artikel om dagens DDoS-attacker:

http://www.darkreading.com/security/perimeter/showArticle.jhtml?articleID=219100668&cid=RSSfeed

onsdag 23 september 2009

Kvällens möte med OWASP Sweden Board

Hemma efter ett trevligt och givande möte med ledningsgruppen för OWASP Sweden. Vi har varit lite sena i starten nu i höst, mest pga arbetet med konferensen nästa år.

Vi diskuterade vilka seminarier vi vill ordna i höst. Det har inkommit förslag på tema "Säkerhet i molnet" från ett par av er och vi pratade en lång stund om det. Vem kan hålla föredrag? Hur ser vi till att det inte bara blir flum? Hur ser vi till att det inte blir en fajt om vad molnet är?

Vår slutsats blev att det kommer ett par intressanta moln-konferenser i Sverige nu i höst och att vi ska hålla koll på dem. Ser vi att det finns grund för en matnyttig seminariekväll om molnsäkerhet så kör vi i vår. Ett alternativ är att samlas för en webbsession med någon guru från t ex Google (kontakter finns).

De två teman som vi siktar på för hösten är ...
  • De stora protokollen. Säkerheten i våra webbappar förutsätter att underliggande infrastruktur funkar. Men gör den det? Vilka frågor är aktuella inom BGP, SSL och DNSSec? Nyttigt för alla oss som håller oss högt upp i OSI-stacken.
  • Penetrations- och blackbox-testning. Många medlemmar i chaptret är pentestare och många seminariekvällar har också berört pentestfrågor. Men vi skulle gärna bjuda in t ex IBM att dema AppScan + OunceLabs O2 och följa upp det med en presentation om en oberoende pentestarens vardag.
Inför våren tittar vi på "Säkerhet och RIA (plugins+HTML5+CSS3)" och "Identiteter på nätet (SAML, OpenID, e-leg)". Och så molnet då.

måndag 21 september 2009

AppSec Research 2010 Challenge 4

Utmaning fyra är publicerad på konferenswikin. Tävla och vinn en biljett till nästa års stora konferens i Stockholm.

onsdag 16 september 2009

Domändriven säkerhet / Domain-Driven Security

Jag och en kollega på Omegapoint, Dan Bergh Johnsson, har myntat designmetoden Domain-Driven Security (domändriven säkerhet) -- ett arbetssätt i skärningspunkten mellan domändriven design (DDD) och applikationssäkerhet.

Vårt intresse väcktes när Dan höll föredrag om DDD och jag kommenterade dess bärighet på indatavalidering. Sen dess har vi stött och blött olika exempel från verkligheten och inte minst DDD och OWASP topp tio.

Idag lanserar vi begreppet i varsitt blogginlägg med synkroniserade klockor. Läs Dans inlägg här.

Domändriven design
Domändriven design utgår från modellering av den aktuella domänen, dvs värdebärande informationsobjekt och kärnan av domänlogik. Inom sjukvård så är patient, läkemedel och samtycke del av den domänspecifika modellen. Inom e-handel hittar vi modellobjekt som vara, pris och valuta. DDD innebär att mycket tid och kraft läggs på att få domänmodellen rätt och att låta den driva systemutvecklingen.

Indatavalidering
Indatavalidering brukar (i alla fall av mig) kallas applikationssäkerhetens heliga graal. Varhelst applikationen tar emot data -- nätverk, filsystem, omgivningsvariabler, formulär -- så måste dessa data valideras. Attacker tar nämligen samma väg in.

Valideringen ska så långt det är möjligt utgå från positiva modeller, dvs modeller av hur korrekt indata ser ut. Inom säkerhet kallas det vitlistning. Genom att bara acceptera indata som stämmer med den positiva modellen så kommer applikationen automatiskt avvisa felaktig indata.

Domändriven säkerhet
Skärningspunkten mellan domändriven design och applikationssäkerhet handlar för mig mycket om just indatavalidering. De värdebärande objekt som driver applikationens logik måste vara korrekta, dvs ha tillstånd som den övriga applikationen kan lita på.

I konkreta objektorienterade termer innebär det att domänobjekt aldrig ska kunna konstrueras eller populeras med felaktiga data -- objektfabrikerna måste validera indata så att resten av logiken kan förutsätta att konstruerade objekt har kompletta och korrekta tillstånd.

Något nytt under solen?
OK, säger det här oss något nytt? Ja, det finns ett gammalt problem inom just indatavalidering -- var i arkitekturen ska den placeras och hur förhindrar vi att valideringsmodellen blir inkonsistent om den finns i flera lager?
  • Klassisk säkerhet säger att indatavalidering ska ske så tidigt som möjligt, gärna som ett skalskydd i form av brandväggar och applikationsbrandväggar men åtminstone direkt i den kod som tar emot indata, t ex i dina servlets. Problemet med det är att man får en komplex koppling mellan brandväggsregler/protokollparsning och applikationslogik.
  • Databasfolket anser att databasmodellen är sanningen och att det är där datats korrekthet ska avgöras. Problemet med det är databasers undermåliga programmeringsförutsättningar (jämför med en modern IDE) och svårigheten att skriva applikationslogik som felfritt kan hantera elaka indata tills databasen kan validera dem.
  • Säkerhet a la domändriven design anser alltså att indatavalidering ska göras i värdeobjekten, relativt djupt inne i applikationen. Problemen, eller utmaningarna, är att hålla indatalogiken enkel fram till domänobjektens validering och att hålla domänmodell och databasmodell i synk. En stor fördel är att valideringen då fungerar oavsett varifrån data kommer. Om någon lyckats få in skadlig kod i den bakomliggande databasen eller en bakomliggande web service så kommer applikationen klaga på samma sätt som om den skadliga koden kom från ett webbformulär.

måndag 14 september 2009

Svenska IT-säkerhetshandboken 1.0

Predrag Mitrovic samlade mig och tolv andra kollegor i säkerhetsbranschen för att skriva Svenska IT-säkerhetshandboken 1.0. Förra veckan hade vi release-party på LabCenter och alla författarna signerade böcker så pennorna glödde ;).

Jag tycker det är jättekul med modern facklitteratur på svenska. Dels har vi kunnat rikta innehållet mot de förhållanden vi tycker oss möta i Sverige, dels slipper läsarna den språkbarriär som ofta gör det segt att ta till sig fackböcker.

För egen del skrev jag kapitlet om säkra web services och tillsammans med Omegapointkollegor kapitlet om perimeterskydd.

Boken har en webbplats med en blogg där ni kan kommentera och diskutera. Planen är nämligen att ni i communityn ska hjälpa till med version 2.0!


torsdag 3 september 2009

Facebook-attack just nu?

Fick precis ett meddelande från en vän på Facebook. Men meddelandelänken gick inte till Facebook utan till "fastredbk.info/lagin.php". Ytterst få referenser på nätet men domänen verkar vara registrerad i måndags.

Om ni får meddelanden på Facebook, högerklicka, kopiera länken och klistra in den i en texteditor. Verkar den skum så bör du inte klicka. Min gissning är att det snart kommer en förklaring och om det är en attack så gör nog Facebook något åt den snart.

[Uppdatering 1]
Fick tag på min kompis som ovetandes skickat meddelandet med den konstiga länken. Så här beskriver han vad som hänt:

"Jag fick själv en "[kompisnamn] sent you a message"-notifiering, tänkte "Ett meddelande? Till mig?!" och klickade på den. Då slussades jag till någon "Your photos"-applikation som verkade strula, men sedan fick jag veta att jag minsann skickat en likadan notifiering till en hög med vänner. Ber om ursäkt.

Typiskt maskbeteende. Jag fick bara notifiering nere i högra hörnet av Facebook vilket antyder att det är notifieringstjänsten och inte själva meddelandetjänsten som man lyckats utnyttja. Kan vara en CSRF. Frågan är om det kommer slå MySpace-masken Samy.

[Uppdatering 2]
Det twittras en del om det hela. Sök på "fastredbk", "Facebook worm", "Facebook trojan" och "Facebook virus".

Jag har nu kontaktat Facebooks säkerhetsteam och beskrivit problemet. Jag återkommer med deras svar.

[Uppdatering 3]
Fem dagar senare svarar FBs säkerhetsteam. Deras svar ger inte intryck av att de läst vad jag skrev:

We apologize for our delayed response to your inquiry. If you have resolved this issue on your own, please ignore this email. If you are still experiencing security issues, let us know and we’ll investigate further. We appreciate your patience in this matter.