Response Headers

Jede Nachricht, die an einen Webserver geschickt wird bzw. von einem Webserver empfangen wird, besteht aus 2 Teilen:

  • dem Nachrichtenkopf (Header)
  • dem Nachrichtenrumpf (Body)

Der Nachrichtenkopf beschreibt einerseits den Nachrichtenrumpf genauer. Dabei geht es um Dinge wie die gewünschte Sprache oder den Zeichensatz mit welchem die Nachricht codiert wurde.
Andererseits werden aber gerade bei Antworts Nachrichten (responses), Direktiven an den jeweiligen Browser übergeben, wie zum Beispiel Cookies, welche zu setzen sind oder die Gültigkeitsdauer dieser Nachricht.

Wir kennen bereits ein Response Header-Attribut, das Set-Cookie Attribute. Wir haben es nicht direkt im Code gesetzt, denn das Session Cookie aus dem vorherigen Kapitel wird vom Applikations Server automatisch verwaltet.

1
Set-Cookie: JSESSIONID=6eba983f1b938302c8612269667f; Path=/security-web; HttpOnly

Wir können allerdings die Response Header-Attribute auch im Code manuell setzten. Hier ein kleines Beispiel:

1
2
3
4
HttpServletResponse resp = (HttpServletResponse) response;
Cookie username = new Cookie("username", "John Silver");
//username.setMaxAge(60*60*10);
resp.addCookie(username);

Header:

1
Set-Cookie: username="John Silver"

Dieser Code könnte in einem WebFilter verwendet werden.

In weiterer Folge sehen wir uns weitere Response Header Attribute an, welche Security relevant sind. Leider werden jedoch noch nicht alle Directiven auch von allen Browsern unterstützt, dies sollte man in sein Security Konzept mit einbeziehen. Wenn möglich wird auf die Browser Unterstützung hingewiesen.

X-Frame-Options

Browser Unterstützung

Die X-Frame-Options werden benutzt um anzugeben, ob die angeforderte Seite in einem Frame angezeigt werden darf oder eben nicht, dadurch schützt diese Option unter anderem vor Clickjacking.

Clickjacking ist eine Technik, bei der die Darstellung einer Internetseite überlagert wird. Dadurch werden Nutzer veranlasst, scheinbar harmlose Mausklicks und/oder Tastatureingaben durchzuführen.

Es gibt drei mögliche Werte für X-Frame-Optionen:

  • DENY: Die Seite kann nicht in einem Frame angezeigt werden
  • SAMEORIGIN: Die Seite kann nur in einem Frame angezeigt werden die von der selben Herkunft ist wie die Seite selbst.
  • ALLOW-FROM uri: Die Seite kann nur in einem Frame einer Seite spezifizierten Ursprungs angezeigt werden.

Umsetzung als Security Aspect:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class XFrameOptionsAspect implements SecurityAspect {
private String deny = "DENY";
private String sameorigin = "SAMEORIGIN";
private String allowfrom = "ALLOW-FROM http://www.freelenzer.at";
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
response.setHeader("X-Frame-Options", deny);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}

Header:

1
X-Frame-Options: DENY

Versucht man nun diese Seite trotzdem in einem Frame zu öffnen, bekommt man eine Fehlermeldung:

1
2
Refused to display 'http://localhost:8080/security-web/index.xhtml'
in a frame because it set 'X-Frame-Options' to 'DENY'.

HTTP Strict Transport Security

Browser Untestützung:

  • Internet Explorer: IE11
  • Firefox: 4
  • Opera: 12
  • Safari: Mavericks (Mac OS X 10.9)
  • Chrome: 4.0.211.0

HSTS wie HTTP Strict Transport Security abgekürzt wird, wurde erst 2012 unter RFC 6797 veröffentlicht.
Die Idee ist ganz einfach. Wenn ein User eine Website über den Browser aufruft, und dabei per HTTP zugreift, könnte sich in diesem Fall ein Hacker schon als “Man-in-the-Middle” dazwischen hängen. Wurde allerdings schon vorher einmal diese Seite aufgerufen und dabei kam der Response-Header “Strict-Transport-Security” zurück, so wandelt der Browser alle Anfragen an diese Adresse direkt in eine HTTPS Anfrage um.

Beim Header wird der Parameter “max-age” gesetzt. Dieser gibt die Gültigkeitsdauer dieser Directive in Sekunden an. Zusätzlich kann noch über “includeSubdomains” bestimmt werden, dass diese Directive auch für alle Subdomains gelten soll.

Achtung: Chrome verwaltet die sogenannte HSTS preload list, welche unter anderm von Firefox und Safari benutzt wird. Auch IE11 und Edge benutzen eine preload list welche die von Chrome inkludiert.

Umsetzung als Security Aspect:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class HTTPStrictTransportSecurityAspect implements SecurityAspect {
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
boolean secure = request.isSecure() | "https".equalsIgnoreCase(request.getHeader("X-Forwarded-Proto"));
if (secure) {
response.setHeader("Strict-Transport-Security", "max-age=31536000;includeSubdomains");
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}

Header:

1
Strict-Transport-Security: max-age=216000;includeSubdomains

XSS-Protection

Urprünglich wurde die XSS-Protection Directive von Mircrosoft 2010 mit dem Internet Explorer 8 eingeführt. Mitlerweile unterstützen fast alle gängigen Browser dieses Feature.

Die Syntax für diesen Header schaut folgendermaßen aus:

1
X-XSS-Protection: (0|1{;mode=block}{;report=.*})

Man kann mit diesem Header die vom Browser zur Verfügung gestellte CrossSiteScripting Erkennung steuern. Mit 0 kann man dies für die eigene Seite deaktivieren. Mit 1 kann man sie aktivieren, falls diese der User generell deaktiviert hat.

  • 0 deaktiviert
  • 1 aktiviert, der Browser versucht den XSS Code mittels eingefügten # Zeichen unschädlich zu machen.
  • 1; mode=block aktiviert, allerdings wird nun nach einem erkannten Angriff die Seite nicht mehr gerendert.
  • report=(JSON POST URI) URI an welche ein XSS Alert geschickt werden soll.

Leider kann man in die Browser Engines nicht hineinschauen, und darum kann man auch nur schwer sagen, welche XSS Angriffe wirklich erkennt werden oder nicht.

Hier findet man dazu noch mehr Informationen und vor allem eine Testseite um den Response-Header zu testen.

X-Content-Type-Options

Dazu zeige ich am besten gleich ein Beispiel:

1
2
3
4
5
6
7
8
Content-Type: text/plain
X-Content-Type-Options: nosniff
<html>
<body bgcolor="red">
I am a HTML source Code!
</body>
</html>

Umsetzung als Security Aspect:

1
2
3
4
5
6
7
8
9
10
11
12
public class XContentTypeOptionsAsspect implements SecurityAspect {
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
response.setHeader("X-Content-Type-Options", "nosniff");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}

Würde man hier auf nosniff verzichten, dann würden einige Browser diesen Response als HTML Seite rendern. Die eigentliche Intention war allerdings den Response als plain Text auszuliefern.

X-Download-Options

Dieses Request-Header Attribut erlaubt es den “open” Button beim Download von Dateien zu steuern. Mit noopen wird der Öffnen Button nicht angezeigt.

1
2
3
Content-Type: text/html
Content-Disposition: attachment; filename=untrustedfile.html
X-Download-Options: noopen

Content-Security-Policy

Content Security Policy ist ein extrem mächtiges Werkzeug. Ich kann hier nur einen kurzen Abriss geben was man damit alles machen kann.

1
Content-Security-Policy: default-src 'self'

Wie der Name default-src schon vermuten lässt, bedeutet dies, dass für alle sourcen (child-src, connect-src, font-src, img-src, media-src, object-src, script-src und style-src) die nachfolgend angegebene Policy gilt. In unserem Fall ‘self’, also dürfen alle Sourcen nur von der selben Quelle kommen, wie die Seite selbst.

1
Content-Security-Policy: default-src 'self'; script-src example.com

In diesem Fall gilt das Gleiche wie oben, mit der Ausnahme, dass die Scripte auch von der Seite example.com geladen werden können.

1
Content-Security-Policy-Report-Only: default-src 'self'; report-uri http://loghost.example.com/reports.js

Man kann die Content-Security-Policy Einstellungen auch einfach mal testen. Wenn man nicht die Content-Security-Policy sondern eine Content-Security-Policy-Report-Only angibt, so verhindert der Browser das Laden einer nicht erlaubten Ressource nicht, allerdings reportet er einen Verstoß an die angegebene report-uri.

Umsetzung als Security Aspect:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ContentSecurityPolicyAspect implements SecurityAspect {
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
//response.setHeader("Content-Security-Policy", "default-src 'self'");
//response.setHeader("Content-Security-Policy", "default-src 'self'; report-uri /cspreporter");
response.setHeader("Content-Security-Policy-Report-Only", "default-src 'self'; report-uri "+request.getContextPath()+"/cspreporter");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}

Servlet um die Verstöße zu Loggen

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@WebServlet("/cspreporter")
public class ContentSecurityPolicyReporter extends HttpServlet {
private static final Logger LOG = Logger.getLogger(ContentSecurityPolicyReporter.class.getName());
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
resp.setContentType("text/plain");
StringBuilder buffer = new StringBuilder();
BufferedReader reader = req.getReader();
String line;
while ((line = reader.readLine()) != null) {
buffer.append(line);
}
String input = buffer.toString();
LOG.log(Level.WARNING, "CSP Report:\n" + input);
resp.getWriter().println("Reported.");
}
}

Mehr infos zu CSP gibt es hier und hier.

Cache-Control

Jeder Browser verfügt über einen Cache, welcher Ressourcen speichern kann. Darin können Ressourcen, welche sich selten oder nie ändern, abgelegt werden und dabei kann man dem Browser noch mitteilen, wie er mit diesen Inhalten umgehen soll.

1
Cache-Control: no-cache

Diese Ressource darf nicht gecached werden. Der Browser muss diese Ressource jedes Mal neu Anfragen.

1
Cache-Control: public, max-age=31536000

Diese Ressource ist öffentlich für jeden User zugänglich. Also nicht auf eine Session begrenzt. Die Lebensdauer (max-age) ist ein Jahr (Angabe in Sekunden). Der Browser kann diese Ressource für ein Jahr speichern und muss diese Ressource kein weiteres Mal vom Server abholen.

1
Cache-Control: private, max-age=31536000

Diese Ressource darf auch für ein Jahr gespeichert werden, allerdings ist sie privat gekennzeichnet, da sie eventuell Userdaten beinhaltet.

1
2
Cache-Control: max-age=120
ETag: "x222df"

Diese Ressource ist nur 120 Sekunden gültig. Danach kann der Browser den Server mittels des ETags fragen, ob sich die Ressource verändert hat (ETag ist unterschiedlich). Hat sich die Ressource nicht verändert, dann wird sie nicht neu herunter geladen.

Man kann mit der Cache-Control Direktive eine Web-Seite wesentlich effizienter machen. Es werden weniger Serveranfragen gestellt. Der Übertragungsaufwand vom Server zum Client wird minimiert. Immer öfter sieht man die Variante, die max-age auf ein Jahr zu erhöhen. Dabei wird in den URL’s der Ressourcen eine Versionsnummer integriert. Domit wird bei jedem Verändern der Files eine neue Versionsnummer in die URL integriert und der Cache überlistet.

Umsetzung als Filter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@WebFilter(
filterName = "CacheFilter",
urlPatterns = {"/*"},
initParams = {
@WebInitParam(name = "expiration", value = "6000")
}
)
public class CacheFilter implements Filter {
private long expiration;
private String cacheability = "public";
private boolean mustRevalidate;
private String vary;
@Override
public void doFilter(ServletRequest servletrequest, ServletResponse servletresponse, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletresponse;
StringBuilder cacheControl = new StringBuilder(cacheability).append(", max-age=").append(expiration);
if (mustRevalidate) {
cacheControl.append(", must-revalidate");
}
// Set cache directives
response.setHeader("Cache-Control", cacheControl.toString());
response.setDateHeader("Expires", System.currentTimeMillis() + expiration
* 1000L);
// Set Vary field
if (vary != null && !vary.isEmpty()) {
response.setHeader("Vary", vary);
}
chain.doFilter(servletrequest, servletresponse);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
try {
expiration = Long.valueOf(filterConfig.getInitParameter("expiration"));
} catch (NumberFormatException e) {
throw new ServletException(new StringBuilder("The initialization parameter ")
.append("expiration")
.append(" is invalid or is missing for the filter ").append(filterConfig.getFilterName())
.append(".").toString());
}
cacheability = Boolean.valueOf(filterConfig.getInitParameter("private")) ? "private" : "public";
mustRevalidate = Boolean.valueOf(filterConfig.getInitParameter("must-revalidate"));
vary = filterConfig.getInitParameter("vary");
}
@Override
public void destroy() {
}
}

Siehe auch hier.

Cookies und Sessionhandling

HTTP ist ein zustandsloses Protokoll, welches den Begriff einer Session nicht kennt. Es verwaltet keine Daten wie etwa eine eindeutige Identifikationsnummer eines Clients. Der Begriff Session wird erst in der Anwendungsschicht eingeführt. Kann ein Hacker eine Session eines Users übernehmen spricht man von Session Hijacking. Das folgende Kapitel befasst sich mit Methoden um die Session eines Users zu schützen.

Tracking Mode

In Java EE kann man konfigurieren wie eine Session identifiziert werden soll. Dabei gibt es 3 Möglichkeiten (SessionTrackingMode):

  • URL: Dabei wir eine Identifikationsid (JSESSIONID) als URL Parameter verwendet.
  • COOKIE: Die Sessionid wird in einem Cookie abgelegt, welches bei jedem Request wieder mitgeschickt wird.
  • SSL: Die SSL Sessionid wird als Identifikation für die Session verwendet.

Laut Spezifikation kann man Url und Cookie gemeinsam benutzen nur SSL darf nur für sich selbst stehen.

Konfigurieren kann man den TrackingMode unter anderem (andere Möglichkeite werden später im Kapitel erläutert) im web.xml.

1
2
3
<session-config>
<tracking-mode>COOKIE</tracking-mode>
</session-config>

Trackingmode URL

Url ist dabei wohl die unsicherste Variante wie man die Sessionid übertragen kann. Die Sessionid findet sich in der Browserhistorie wieder. Teilweise findet man eine Sessionid auch in Suchergebnissen von Suchmaschinen.
In JSF übernimmt das Form-Tag das Anhängen der Sessionid an die Action-URL.

1
2
3
<form id="j_idt5" name="j_idt5" method="post"
action="/security-web/util/information.xhtml;jsessionid=ff0cbc19baea9053ff9863f1a91b"
enctype="application/x-www-form-urlencoded">

Jedoch spielen auch Cookies in der Sicherheitsbetrachtung einer Web-Applikation eine nicht unerhebliche Rolle. Denn es ist auch möglich eine über ein Cookie übertragene Sessionid abzufragen. Zum Beispiel über Javascript. Oder wenn das Cookie über einen HTTP Aufruf ausgeliefert wird und nicht über einen HTTPS. Cookies können jedoch so konfiguriert werden, dass sie vor Angreifern wesentlich besser geschützt sind. Im web.xml kann dazu die cookie-config angepasst werden.

1
2
3
4
5
6
<session-config>
<cookie-config>
<http-only>true</http-only>
<secure>true</secure>
</cookie-config>
</session-config>
  • Http-Only bedeutet in diesem Fall, dass der Browser ein Session Cookie zwar über HTTP und HTTPS verschicken darf, jedoch darf das Cookie nicht über Javascript ausgelesen werden.
  • Secure bedeutet, dass der Browser ein Cookie nur über HTTPS verschicken darf.

Diese zwei Einstellungen erhöhen die Sicherheit einer Web-Applikation schon wesentlich. Allerdings, wenn diese Einstellungen im web.xml gemacht werden, wie gehen wir dann mit Entwicklungsrechnern um, auf welchen eventuell gar kein SSL vorhanden ist?

Dazu gibt es einen Trick, man kann das SessionCookie auch im Code konfigurieren, allerdings nur solange der SessionContext nicht vollständig initialisiert ist. Man kann also die CookieConfig nur in einem WebListener setzten.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@WebListener
public class SecurityServletContextListener
implements ServletContextListener {
@Override
public void contextDestroyed(ServletContextEvent event) {
}
@Override
public void contextInitialized(ServletContextEvent event) {
configureSessionCookie(event.getServletContext());
}
private void configureSessionCookie(ServletContext servletContext) {
SessionCookieConfig sessionCookieConfig = servletContext.getSessionCookieConfig();
ProjectStage projectStage = FacesContext.getCurrentInstance().getApplication().getProjectStage();
if (projectStage == ProjectStage.Development) {
sessionCookieConfig.setSecure(false);
servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE));
}
}
}

Die Einstellungen für die Produktion sollten im web.xml stehen, die Ausnahmen für Development, UnitTest und SystemTest können im Code überschrieben werden.

Der WebListener wird entweder mit der Annotation @WebListener registriert, oder wiederum im web.xml eingetragen:

1
2
3
4
5
6
7
<web-app>
<listener>
<listener-class>
at.freelenzer.ui.jsf.util.SecurityServletContextListener
</listener-class>
</listener>
</web-app>

Das gleiche Dilemma wie mit der Cookie Config hat man allerdings auch mit der javax.faces.PROJECT_STAGE. Denn diese wird auch in der web.xml konfiguriert.

1
2
3
4
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Production</param-value>
</context-param>

Darüberhinaus sieht die Spezifikation auch vor, dass man die ProjectStage über JNDI (Java Naming Directory) definieren kann. Dazu sind folgende Schritte notwendig.

1. JNDI Referenz im web.xml eintragen:

1
2
3
4
5
<resource-ref>
<res-ref-name>jsf/ProjectStage</res-ref-name>
<res-type>java.lang.String</res-type>
<mapped-name>javax.faces.PROJECT_STAGE</mapped-name>
</resource-ref>

2. JNDI Resource am Server konfigurieren:

Hier am Beispiel von GlassFish:

jndiProjectStageConfig

  • Factory Class: com.sun.faces.application.ProjectStageJndiFactory
  • JNDI Name: javax.faces.PROJECT_STAGE
  • Type: java.lang.String
  • Property
  • stage: Development

Hier gilt dieselbe Empfehlung wie auch schon vorhin beim den Cookie Einstellungen. Die Einstellungen welche in der Produktion verwendet werden sollen, werden im web.xml eingetragen. Sollte auf einem Enwicklungsarbeitsplatz eine andere Konfiguration benötigt werden, so wird diese auf dem Server konfiguriert und nicht in der Applikation.

Trackingmode SSL

Der Secure Socket Layer, welcher im HTTPS Protokoll verwendet wird, ermöglicht es Requests welche von einem Client kommen, eindeutig zu identifizieren. Der Servlet Container kann diese Information nutzen um eine HTTP Session zu verwalten.
Sollte man mal programmatisch auf die SSL Session ID zugreifen, so kann man dies folgendermaßen machen.

1
String sslID = (String)request.getAttribute("javax.servlet.request.ssl_session_id");

Verfikation des Trackingmodes

Wenn man sich ganz sicher sein will, dass die Sessionid z.B: über ein Cookie kommt und zwar nur über ein Cookie, dann kann man das über einen Servlet Filter einfach lösen. Da wir später noch andere Aspekte über einen Servlet Filter lösen möchten implementieren wir einen Filter der es uns ermöglicht Seurity Aspekte einzuhängen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@WebFilter(filterName = "SecurityFilter", urlPatterns = {"/*"})
public class SecurityFilter implements Filter {
private static final List<SecurityAspect> securityAspects = new ArrayList<>();
static {
securityAspects.add(new SessionTrackingModeAspect());
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
for (SecurityAspect securityAspect : securityAspects) {
securityAspect.doFilter(request, response);
}
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
for (SecurityAspect securityAspect : securityAspects) {
securityAspect.init(filterConfig);
}
}
@Override
public void destroy() {
for (SecurityAspect securityAspect : securityAspects) {
securityAspect.destroy();
}
}
}

Den ersten Security Aspect (SessionTrackingModeAspect) haben wir schon eingetragen. Dieser könnte in etwa folgendermaßen aussehen:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class SessionTrackingModeAspect implements SecurityAspect {
@Override
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
if (((HttpServletRequest) request).isRequestedSessionIdFromURL()) {
//Könnte ein Angreifer sein.
System.err.println("Angriff");
((HttpServletResponse) response).sendError(HttpServletResponse.SC_BAD_REQUEST);
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//don't need
}
@Override
public void destroy() {
//don't need
}
}

Kommt die Sessionid also aus der URL, dann kann man von einem Angreifer oder von einer Fehlkonfiguration der eigenen Anwendung ausgehen.

Session Timeout

Nicht nur das Erzeugen und Tracking einer Session ist wichtig, sondern auch das Beenden der Session. Zum Einen kann man den User die Session invalidieren lassen. Das ist relativ einfach:

1
2
3
4
5
public void invalidateSession() {
FacesContext fCtx = FacesContext.getCurrentInstance();
HttpSession session = (HttpSession) fCtx.getExternalContext().getSession(false);
session.invalidate();
}

Zum Andern sollte man auch die Möglichkeit in betracht, ziehen die Session nach einer bestimmten Zeit zu beenden.
Dabei betrachten wir das inaktivitäts Timeout und ein absolutes Session Timeout.

Inaktivitäts Timeout

Sollte der User vergessen sich auszuloggen, muss die Session vom Server irgendwann aufgeräumt werden. Dazu kann man im web.xml den Session Timeout Parameter setzen.

1
2
3
<session-config>
<session-timeout>30</session-timeout>
</session-config>

Dies bedeutet, wenn der Benutzer 30 Minuten keinen Request gegen die Anwendung macht, wir die Session beendet.

Hat ein Angreifer jedoch schon geschafft die Session eines Users zu übernehmen, ist ein einfaches Session Timeout nicht mehr ausreichend. Der Angreifer könnte durch ein simples Polling die Session unendlich lange aufrecht erhalten.

Abhilfe schafft hier ein absolutes Session Timeout.

Absolutes Session Timeout

Ein absolutes Session Timeout begrenzt die Zeit, die eine Session aufrecht gehalten wird.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class AbsoluteSessionTimeoutAspect implements SecurityAspect {
public static final String ABSOLUTE_SESSION_TIMEOUT = "absolute-session-timeout"; //public für die Konfiguration!
private long absoluteSessionTimeout;
@Override
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpSession session = req.getSession( false );
if ( session != null) {
long activated = session.getCreationTime();
if ( System.currentTimeMillis() > ( activated + absoluteSessionTimeout * 60 * 1000 ) ) {
session.invalidate();
}
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
if ( filterConfig.getInitParameter(ABSOLUTE_SESSION_TIMEOUT) == null ) {
throw new IllegalStateException( "Absolutes Session Timout muss konfiguriert werden: " + ABSOLUTE_SESSION_TIMEOUT );
}
absoluteSessionTimeout = new Long( filterConfig.getInitParameter(ABSOLUTE_SESSION_TIMEOUT) );
}
@Override
public void destroy() {
//don't need
}
}

Die Konfiguration des Aspekts erfogt über einen Initparameter auf dem dazugehörigen Filter. Je nachdem wie der Filter konfiguriert wurde, kann der Initparameter entweder über eine Annotation am Filter oder über die web.xml gesetzt werden.

Annotation:

1
2
3
4
5
6
7
8
@WebFilter(
filterName = "SecurityFilter",
urlPatterns = {"/*"},
initParams = {
@WebInitParam(name = AbsoluteSessionTimeoutAspect.ABSOLUTE_SESSION_TIMEOUT, value = "60") //Eine Stunde
}
)
public class SecurityFilter implements Filter {

web.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<filter>
<description>Der Security Filter</description>
<filter-name>SecurityFilter</filter-name>
<filter-class>at.freelenzer.ui.jsf.util.SecurityFilter</filter-class>
<init-param>
<description>Eine Stunde</description>
<param-name>absolute-session-timeout</param-name>
<param-value>60</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SecurityFilter</filter-name>
<url-pattern>*</url-pattern> TODO
</filter-mapping>

WebListener

Mit der Servlet Spezifikation 3.0 gibt es ein sehr interessantes neues Feature die WebListener. Wir haben es schon weiter oben für den SecurityServletContextListener benutzt.
Die Annotation @WebListener wirkt immer im Zusammenhang mit dem Interface welches die Klasse implementiert (ServletContextListener, ServletContextAttributeListener, ServletRequestListener, ServletRequestAttributeListener, HttpSessionListener, HttpSessionAttributeListener oder HttpSessionIdListener). Wir können den WebListener nutzen um die Erzeugung und die Beendigung von Sessions zu beobachten.

1
2
3
4
5
6
7
8
9
10
11
@WebListener
public class SecurityHttpSeesionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent event) {
System.out.printf("Session ID %s erzeugt um %s%n", event.getSession().getId(), new Date());
}
@Override
public void sessionDestroyed(HttpSessionEvent event) {
System.out.printf("Session ID %s zerstört um %s%n", event.getSession().getId(), new Date());
}
}

Mit Promodorotechnik produktiver arbeiten

Wer kennt das Problem nicht. Man sitzt im Büro, eigentlich hätte man ja genug Arbeit, aber irgendwie kommt man mit der zu erledigenden Arbeit nicht weiter.

Das hat natürlich unterschiedliche Gründe:

  • Arbeit muss immer wieder unterbrochen werden
  • Ständig kommen neue Anforderungen dazu
  • Wir können uns schwer auf eine Sache konzentrieren

Francesco Cirillo entwickelte die Pomodoro-Technik die uns helfen soll den Focus auf einer Sache zu halten. Der Name rührt daher, dass Cirillo für seine Technik eine Eieruhr zu Hilfe nahm und diese die Form einer Tomate hatte.

Die Arbeitszeit wird in Zeitslots unterteilt. Eine Arbeitseinheit besteht aus 25 Minuten. 25 Minuten ist eine Zeitspanne in der siche ein Erwachsener mühelos konzentrieren kann. In 25 Minuten schafft man auch einiges an Arbeit auf die Seite. Nach 25 Minuten kommt eine Pause von 5 Minuten. Diese Pausen werden genutzt um über das gearbeitete nachzudenken. Oft kommt man drauf, man muss die Arbeit doch anders angehen oder vielleicht sogar von neuem beginnen. Da sind dann 25 Minuten Arbeit nicht so viel die man vielleicht “vergeudet” hat. Nach 4 Arbeitseinheiten folgt dann eine längere Pause von 30 Minuten.

Zusammenfassung:

  • 1 Arbeitseinheit entspricht 25 Minuten (Pomodoro genannt)
  • nach jeder Arbeitseinheit gibt es eine Pause von 5 Minuten
  • nach 4 Arbeitseinheiten eine Pause von 30 Minuten

Wichtig bei der Umsetzung ist jedoch, dass in den Arbeitseinheiten keine Unterbrechungen vor kommen. Ansonsten ist der Fokus und auch der Pomodoro weg. Wird man Unterbrochen muss mit dem Pomodoro neu begonnen werden.

Wir wissen alle wie oft wir unterbrochen werden:

  • Das Telefon läutet
  • Eine SMS kommt rein
  • Ein Email muss gelesen werden
  • Eine WhatsApp Nachricht kommt
  • Ein Kollege braucht was

Es ist schon klar, dass man nicht alle externen Einflüsse abschalten kann, aber meist können die externen Einflüsse auch mal 15minuten warten.

Wenn man eine Arbeit in einem Pomodoro nicht schafft, dann fängt man einfach nach der Pause einen 2ten Pomodoro an.

Offizielle Homepage

Clean Code Vortrag bei der InfPro IT Solutions

Am vergangenen Freitag durfte ich einen 3 stündigen Vortrag zu Thema Clean Code halten.

Die InfPro IT Solutions hat mich zu diesem Vortrag eingeladen. Rund 10 Teilnehmer haben an dem Vortrag teilgenommen. Es war eine sehr gute Veranstaltung. Das Ambiente war sehr nett, die Mitarbeiter aufmerksam und die Diskussionen anregend.

Alles was man für ein gelungenes Event.

Wir konnten die Themen am Abend bei dem einen oder anderen Bierchen noch vertiefen.

Die fünf entscheidenden Fragen des Managements

Book review: Die fünf entscheidenden Fragen des Managements, von Peter F. Drucker.

Ich hatte das Buch von Peter F. Drucker schon fast ein Jahr in der Schublade liegen. Irgendwie war ich sehr skeptisch bei diesem Buch, denn ich hatte mich ja schon in das MBO (Management by Objectives / Führen durch Zielvereinbarung) Thema eingelesen und befunden, dass ein Thema das wesentlich älter ist als ist nix mehr für mich ist.

Weit gefehlt. Da könnten sich Manager von heute noch eine Scheibe runter schneiden.

Die einfachsten Fragen sind oft am schwersten zu beantworten.

  • Was ist unsere Mission?
  • Wer ist unser Kunde?
  • Worauf legt der Kunde Wert?
  • Was sind unsere Ergebnisse?
  • Was ist unser Plan?

Zwar geht es in dem Buch nur um “Non Profit”-Management, also um das Managen von “Non Profit” Organisationen, doch gerade diese Organisationen müssen umso straffer geführt werden, da unter anderem die Mittel für Fehltritte fehlen.

Die oben gestellten Fragen zielen auf die Selbsteinschätzung eines Unternehmens ab. Und das ist glaube ich auch der Punkt. Es geht um die tourliche retrospektive.

Allerdings geht dabei nichts ohne Vertrauen. Nur Vertrauen ermöglicht es, dass abweichende Meinungen öfter Vorgebracht werden. Ein nettes Zitat von Aristoteles:

In den wesentlichen Dingen Einheit, im Handeln Freiheit, und bei allen Dingen Vertrauen.

Mission steht in diesem Zusammenhang für das Was. Also was machen wir, nicht wie machen wir es. Was wollen wir erreichen, nicht wie wollen wir es erreichen. Am Ende sollte dann ein Plan entstehen, das wäre ein messbares Ergebnis, ein fokussierter Aktionsplan.

Sie soll:

  • soll jedem Mitarbeiter bekannt sein. (grundlegende Führungsverantwortung)
  • soll auf ein T-Shirt passen.

Wichtig ist: Gutes sollte man nur dann tun, wenn es zur Mission passt.

Wenn man von den Mitarbeitern Selbständigkeit einfordert bedeutet dies Freiheit und Verantwortung zu übergeben.

Jedes noch so gute Unternehmen kann ohne Kunden nicht überleben. Die Frage nach dem Kunden ist gleichzeitig die Frage nach dem Wer muss zufriedengestellt werden. Oft gibt es nicht nur Hauptkunden sondern auch Nebenkunden. Dabei gilt es die Prioritäten klar zu legen. Dabei kann man sich den Kunden auch selbst erschaffen. Noch besser als Kunden sind allerdings Fans.

Wert oder auf Englisch eben value ist das was wir unserem Kunden liefern müssen, damit er wirklich zufrieden ist. Dabei sind nicht nur die Hypothesen wichtig, man kann auch mal den Kunden zu fragen was ihm wirklich wichtig ist.

Retrospektiven, was haben wir für Ergebnisse erzielt. Die Mission mit dem tatsächlich erreichten vergleichen. Das ist oft sehr schwer, auch wird dieser Punkt gern vergessen. Wenn es gut läuft, denkt man nicht daran, wenn es nicht so gut läuft hat man vermeintlich keine Zeit. Aber nur die Rückschau erlaubt es den Plan zu verfeinern.

Der Plan verrät uns, ob wir noch auf schiene sind. Müssen wir die Mission ändern, oder passt unser Leitbild noch. Eine alternative zu einem Plan ist eine Vision. Allerdings sollte man beachten, wenn man mehr als fünf Ziele hat, dann hat man gar keine Ziele.


Ich hoffe ich habe noch nicht zu viel verraten. Das Buch beantwortet die Fragen in einem Detailgrad, der am Ende noch Spielraum für einen selbst lässt. Mich hat es angeregt zu träumen. Träumen von einer Organisation wie ich sie gerne führen würde. Peter F. Drucker ist für mich aufgestiegen zu einem der führenden Management Vordenker. Interessant wäre eine Synopse der Texte von Drucker und anderen Management Autoren wie z.B. Tom Demarco. Vielleicht könnte diese Idee jemand umsetzen.

Der eigenen Rolle bewusst werden

In einem Unternehmen kann leider nicht immer alles ganz rund laufen, das hat verschiedenste Gründe. Aber darauf möchte ich hier gar nicht weiter eingehen. Als Mitarbeiter ist man dann oft versucht die Probleme zu identifizieren und am liebsten fängt man da mal nicht bei den eigenen Problemen an.

He should mind his own business. (Er soll vor der eigenen Haustüre kehren) - Idiom

Ich möchte das hier mal mit einem Schneeballsystem vergleichen. Wenn sich jeder darum kümmert, dass er optimal seine Leistung bringen kann. Jeder aber auch die Dinge die Ihn daran hindern nach oben tragen würde zu seinem Vorgesetzten. Dann würde sich eine kleine Produktivitätslawine in Gang setzten, die sich schwer stoppen liese.

“Wenn sich jeder um sich selbst kümmert, ist für alle gesorgt.” - Aus der Schweiz

Ich bin zwar immer noch der Meinung, dass man durch geschickte Führung aus eine 5-8 Personen Team ein High-Perfomance-Team formen kann. Aber was machen, wenn sich das leider nicht ergibt. Wie kann man sich trotzdem professionell verhalten.

Mind your own business!

Man muss sich zu jeder Zeit seiner Rolle bewusst sein, oder eben die Rolle neu ausstecken. Dazu können die folgenden Fragen helfen:

  • Was spiele ich für eine Rolle in diesem Projekt, Meeting, Gruppe, Unternehmen?

  • Was sind meine Aufgaben, was habe ich für Pflichten?

  • Wann ist meine Aufgabe erledigt?

  • Wann muss ich meine Aufgaben erledigt haben?

  • Wen und wie muss ich informieren?

Ein Kollege von mir hat mal gesagt: “Verhalte dich so, als wärst du eine eigene Firma innerhalb des Unternehmens.”

Mit diesem Satz im Kopf möchte ich gerade noch einen Gedanken an diesen Text anknüpfen.

In einem letzten Gespräch wo auch ein Projektverantwortlicher teil nahm, wurde das Thema Tests angesprochen. Darauf hin antwortete der Projektverantwortliche mit den Worten, wenn es zeitlich eng wird, verzichten wir auf die Tests. Dazu meine Fragen die ich nur so in den Raum stellen möchte:

1. Haben sie schon mal an einem Projekt mitgearbeitet wo es am Ende nicht zeitlich eng geworden ist?

2. Wie würden sie sich verhalten, wenn sie als Firma den Auftrag bekommen würden, würden sie trotzdem Tests schreiben?

JSF Komponenten in JavaScript identifizieren, oder wie kommt man an die clientID einer Komponente

Generelle Patentrezepte gibt es auch hier nicht, aber ich möchte verschiedene Anwendungsszenarien aufzeigen, die Ihnen vielleicht helfen können einige knifflige JSF JavaScript Problemchen zu lösen.

Die einfachste Art an eine Komponenten Id zu kommen ist sicherlich folgende:

1
2
3
<h:inputtext binding="#{fullName}" value="#{fullName}">
</h:inputtext>
#{fullName.clientId}

Somit kann man einfach auf die Id zugreifen und diese z.B. in JavaScripts verwenden.

Für JavaScript bietet sich JQuery an, mit JQuery kann man leicht ein Dom Objekt identifizieren:

1
2
3
$('#{fullName.clientId}'.replace(/:/g,"\\:"))
oder
$('[@id=#{fullName.clientId}]')

Das Problem im ersten fall ist, dass alle ‘:’ escaped werden müssen, darum die replace Funktion, im zweiten Fall ist das nicht notwendig, da über das id Attribut gesucht wird.

Allerdings kommt man so nicht immer zum Ziel und ich gehe oft darauf über, über Klassen zu referenzieren, vorallem dann wenn man über eine Komponente innerhalb einer ui:repeat referenzieren will, ist das oftmals kein Fehler:

1
2
<h:inputText id="value"styleClass="#{field.name} #{page.id}"
value="#{field.value}"></h:inputText>

Zugreifen kann man dann über:

1
$('.#{field.name}.#{page.id}')

Das Ganze ist hier natürlich nur Theorie. In der Praxis wir man den JavaScript-Code z.B. über ein ClientBehavior rendern.

Public Id Generator

Ein PublicIdGenerator macht in JSF an mehreren Stellen Sinn. Hier möchte ich kurz wo es Sinn machen kann, und warum. Zum andern zeige ich euch dann den Code für den PublicIdGenerator. Die Verwendung ist glaube ich selbsterklärend.

Wo kann man den Generator verweden

  • Wenn man die interne (Datenbank) Id einer Klasse (Entität) nicht nach außen geben will.
  • Wenn man noch gar keine interne Id einer Klasse hat, weil eben diese z.B. noch nicht persistiert wurde.

Im Wesentlichen lassen sich alle Anwendungsszenarien darauf zurück führen. Wie in einem vorhergehenden Artikel beschrieben, kann man diese Id für die Generierung eine Style-Class verwenden, und dann nochmal in einem CientBehavior um JavaScript-Code zu generieren. Oder man benötigt sie für die Identifiezierung eines Objectes in Comboboxen, da bietet sich ein einfacher Object Konverter an.

Generator Code

Der Generator ist eigentlich straightforward implementiert.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@ViewAccessScoped
@Named
public class PublicIdGenerator implements Serializable {
private Map<String, Key> publicIds;
private Map<Key, String> objects;
protected PublicIdGenerator() {
publicIds = new HashMap<String, Key>();
objects = new HashMap<Key, String>();
}
public String generatePublicId(final Object object) {
return generatePublicId(object.getClass(), object);
}
public String generatePublicId(final Class<?> clazz, final Object object) {
final String publicId;
final Key key = new Key(clazz, object);
if (objects.containsKey(key)) {
publicId = objects.get(key);
} else {
publicId = generateId();
publicIds.put(publicId, key);
objects.put(key, publicId);
}
return publicId;
}
private String generateId() {
return UUID.randomUUID().toString();
}
public Object lookup(final Class<?> clazz, final String publicId) {
Key key = publicIds.get(publicId);
if (key == null) {
throw new IllegalStateException("No entry for public id " + publicId + ". Possible manipulation detected.");
}
if (!clazz.equals(key.getClazz())) {
throw new IllegalStateException("Invalid clazz " + clazz.getName() + " for public id " + publicId);
}
return key.getObject();
}
private static class Key implements Serializable {
private final Class<?> clazz;
private final Object object;
Key(final Class<?> clazz, final Object object) {
super();
this.clazz = notNull(clazz, "Parameter 'clazz' must not be 'NULL'");
this.object = notNull(object, "Parameter 'object' must not be 'NULL'");
}
Class<?> getClazz() {
return clazz;
}
Object getObject() {
return object;
}
//TODO Generate Equals and HashCode
}
}

Java Art - Puzzle 1

Java also can be art.

But what is the result of the execution.

1
2
3
4
5
6
7
8
9
10
11
public class Zero {
public static void main(String[] args) {
int s = 10;
System.out.println(s++ - --s);
System.out.println(++s - s--);
System.out.println(s-- - ++s);
System.out.println(--s - s++);
System.out.println(s++ + --s + s++ + --s);
}
}