onsdag 21 oktober 2009
AppSec Research 2010 Challenge 5
måndag 19 oktober 2009
TLS/SSL Cheat Sheet
söndag 18 oktober 2009
Orizon 2.0 på AppSec 2010 i Sthlm
torsdag 15 oktober 2009
DDS: Programmera med personnummer, del 2
- Reservnummer
- Katastrofnummer
- Tvillingnummer
- Försöksperson-id
- Ca 12 000 reservnummer hade en eller flera bokstäver i de fyra sista fälten och korrekt födelsedatum
- Ca 10 000 reservnummer hade ett nummer som börjar med 50-99 och resten är ett löpnummer av olika sort
- Ca 3 000 reservnummer var bara ett löpnummer
onsdag 7 oktober 2009
SAML 2.0 -- nu interoperabelt
SAML (security assertion markup language) 2.0 har funnits sen 2004 och används typiskt för single sign-on (SSO) i webbappliktioner och webbtjänster. En så kallad Identity Provider kan med hjälp av SAML utfärda säkerhetspåståenden (security assertions) som ett antal Service Providers kan välja att lita på. Såna säkerhetspåståenden kan vara vem du är, när du autentiserade dig, vilket system du använder med mera.

- Cross-Domain SSO. SSO löses traditionellt med cookies vilket kräver att alla applikationer och tjänster publiceras under den domän kakan gäller för. För att lösa SSO till olika domäner (cross-domain SSO, CDSSO) så behövs SAML.
- Interoperabilitet. Olika SSO-tekniker och -produkter fungerar dåligt ihop eftersom de har olika sätt att hantera autentisering och sessioner. SAML är ett standardiserat sätt att lösa det på.
- Auktorisering och spårbarhet i web services. De säkerhetstekniker man ser i WS-security och WS-policy handlar om skydd av information med hjälp av kryptering och elektroniska signaturer. SAML löser problemet med behörighetskontroll och spårbarhet i web services.
- Federering. Att hantera identiteter över organisationsgränser är svårt. SAML klarar att samla lokala identiteter till en (eller några) federerade identiteter.
- Service Provider Authentication Request MUST be communicated using HTTP Redirect binding
- Identity Provider Authentication Response MUST be communicated using HTTP POST binding or SOAP Artifact binding
- Assertion MUST be signed
- Entrust IdentityGuard Federation Module 9.2 och Entrust GetAccess 8.0
- IBM Tivoli Federated Identity Manager (TFIM) 6.2
- Microsoft Active Directory Federation Services (AD FS) 2.0
- Novell Access Manager 3.1
- PingFederate v6.1
- SAP NetWeaver Identity Management 7.2
- Siemens DirX Access V8.1
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 ...
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