CSRF/XSRF ochrana při hlasování.
1.1 --- a/java/nekurak.net-lib/src/cz/frantovo/nekurak/preklady_cs.properties Sat Jan 15 14:27:02 2011 +0100
1.2 +++ b/java/nekurak.net-lib/src/cz/frantovo/nekurak/preklady_cs.properties Sat Jan 15 18:14:15 2011 +0100
1.3 @@ -43,7 +43,7 @@
1.4 prihlaseni.tlacitko=P\u0159ihl\u00e1sit se
1.5
1.6 pridatPodnik.nadpis=P\u0159id\u00e1n\u00ed nov\u00e9ho podniku
1.7 -pridatPodnik.bylPridan=Podnik byl \u00fasp\u011b\u0161n\u011b p\u0159id\u00e1n.
1.8 +pridatPodnik.bylPridan=Podnik byl \u00fasp\u011b\u0161n\u011b p\u0159id\u00e1n. Zobrazovat se bude po schv\u00e1len\u00ed spr\u00e1vcem.
1.9 pridatPodnik.nebylPridan=P\u0159i p\u0159id\u00e1v\u00e1n\u00ed podniku do\u0161lo k chyb\u011b. Zkontrolujte pros\u00edm zadan\u00e9 \u00fadaje.
1.10 pridatPodnik.tlacitko=P\u0159idat podnik
1.11
1.12 @@ -146,4 +146,4 @@
1.13
1.14 chat.nadpis=Chat
1.15 chat.anonym=Kolemjdouc\u00ed
1.16 -chat.tlacitkoOdeslat=Odeslat
1.17 +chat.tlacitkoOdeslat=Odeslat
1.18 \ No newline at end of file
2.1 --- a/java/nekurak.net-lib/src/cz/frantovo/nekurak/xml/HlasXML.java Sat Jan 15 14:27:02 2011 +0100
2.2 +++ b/java/nekurak.net-lib/src/cz/frantovo/nekurak/xml/HlasXML.java Sat Jan 15 18:14:15 2011 +0100
2.3 @@ -12,6 +12,7 @@
2.4
2.5 private int podnik;
2.6 private boolean kourit;
2.7 + private String csrfToken;
2.8
2.9 @XmlElement
2.10 public int getPodnik() {
2.11 @@ -30,4 +31,13 @@
2.12 public void setKourit(boolean hlas) {
2.13 this.kourit = hlas;
2.14 }
2.15 +
2.16 + @XmlElement
2.17 + public String getCsrfToken() {
2.18 + return csrfToken;
2.19 + }
2.20 +
2.21 + public void setCsrfToken(String csrfToken) {
2.22 + this.csrfToken = csrfToken;
2.23 + }
2.24 }
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/java/nekurak.net-web/src/java/cz/frantovo/nekurak/posluchac/OchranaProtiCSRF.java Sat Jan 15 18:14:15 2011 +0100
3.3 @@ -0,0 +1,23 @@
3.4 +package cz.frantovo.nekurak.posluchac;
3.5 +
3.6 +import javax.servlet.http.HttpSessionEvent;
3.7 +import javax.servlet.http.HttpSessionListener;
3.8 +
3.9 +/**
3.10 + *
3.11 + * @author fiki
3.12 + */
3.13 +public class OchranaProtiCSRF implements HttpSessionListener {
3.14 +
3.15 + public static final String NAZEV_ATRIBUTU = "CSRF_TOKEN";
3.16 +
3.17 + @Override
3.18 + public void sessionCreated(HttpSessionEvent se) {
3.19 + String csrfToken = String.valueOf(Math.random());
3.20 + se.getSession().setAttribute(NAZEV_ATRIBUTU, csrfToken);
3.21 + }
3.22 +
3.23 + @Override
3.24 + public void sessionDestroyed(HttpSessionEvent se) {
3.25 + }
3.26 +}
4.1 --- a/java/nekurak.net-web/src/java/cz/frantovo/nekurak/rest/HlasovaniREST.java Sat Jan 15 14:27:02 2011 +0100
4.2 +++ b/java/nekurak.net-web/src/java/cz/frantovo/nekurak/rest/HlasovaniREST.java Sat Jan 15 18:14:15 2011 +0100
4.3 @@ -1,5 +1,6 @@
4.4 package cz.frantovo.nekurak.rest;
4.5
4.6 +import cz.frantovo.nekurak.posluchac.OchranaProtiCSRF;
4.7 import cz.frantovo.nekurak.util.HttpPozadavek;
4.8 import cz.frantovo.nekurak.web.HledacSluzby;
4.9 import cz.frantovo.nekurak.xml.HlasXML;
4.10 @@ -23,7 +24,16 @@
4.11 @Consumes(MIME_XML)
4.12 @Produces(MIME_TEXT)
4.13 public String hlasuj(HlasXML xml) {
4.14 + zkontrolujCSRF(pozadavek, xml);
4.15 hledac.getPodnikEJB().hlasuj(xml.getPodnik(), xml.isKourit(), HttpPozadavek.getIPadresa(pozadavek));
4.16 return "ok";
4.17 }
4.18 +
4.19 + private static void zkontrolujCSRF(HttpServletRequest pozadavek, HlasXML xml) throws RuntimeException {
4.20 + String csrfTokenOcekavany = (String) pozadavek.getSession().getAttribute(OchranaProtiCSRF.NAZEV_ATRIBUTU);
4.21 + String csrfTokenObdrzeny = xml.getCsrfToken();
4.22 + if (csrfTokenOcekavany == null || !csrfTokenOcekavany.equals(csrfTokenObdrzeny)) {
4.23 + throw new RuntimeException("CSRF token zaslaný klientem neodpovídá očekávanému.");
4.24 + }
4.25 + }
4.26 }
5.1 --- a/java/nekurak.net-web/web/WEB-INF/tags/nekurak/stranka.tag Sat Jan 15 14:27:02 2011 +0100
5.2 +++ b/java/nekurak.net-web/web/WEB-INF/tags/nekurak/stranka.tag Sat Jan 15 18:14:15 2011 +0100
5.3 @@ -43,6 +43,7 @@
5.4 <meta name="robots" content="index, follow" />
5.5 </head>
5.6 <body>
5.7 + <p id="csrfToken"><c:out value="${sessionScope['CSRF_TOKEN']}"/></p>
5.8 <div class="body">
5.9
5.10 <div id="horniPruh">
6.1 --- a/java/nekurak.net-web/web/WEB-INF/web.xml Sat Jan 15 14:27:02 2011 +0100
6.2 +++ b/java/nekurak.net-web/web/WEB-INF/web.xml Sat Jan 15 18:14:15 2011 +0100
6.3 @@ -8,16 +8,19 @@
6.4 </welcome-file-list>
6.5 <!-- <chybovéStránky> -->
6.6 <error-page>
6.7 - <!-- Stránka nenalezena -->
6.8 + <!-- Stránka nenalezena -->
6.9 <error-code>404</error-code>
6.10 <location>/WEB-INF/chyby/404.jsp</location>
6.11 </error-page>
6.12 <error-page>
6.13 - <!-- Interní chyba serveru -->
6.14 + <!-- Interní chyba serveru -->
6.15 <error-code>500</error-code>
6.16 <location>/WEB-INF/chyby/500.jsp</location>
6.17 </error-page>
6.18 <!-- </chybovéStránky> -->
6.19 + <listener>
6.20 + <listener-class>cz.frantovo.nekurak.posluchac.OchranaProtiCSRF</listener-class>
6.21 + </listener>
6.22 <!-- <definiceServletů> -->
6.23 <servlet>
6.24 <servlet-name>atom</servlet-name>
6.25 @@ -70,7 +73,7 @@
6.26 <url-pattern>/kaptcha.jpg</url-pattern>
6.27 </servlet-mapping>
6.28 <servlet-mapping>
6.29 - <!-- Veřejné REST API -->
6.30 + <!-- Veřejné REST API -->
6.31 <servlet-name>REST</servlet-name>
6.32 <url-pattern>/zdroje/*</url-pattern>
6.33 </servlet-mapping>
6.34 @@ -80,7 +83,7 @@
6.35 </servlet-mapping>
6.36 <!-- </mapováníServletů> -->
6.37 <context-param>
6.38 - <!-- Pro případ, že chybí hlavička „Accept-language“ v HTTP požadavku -->
6.39 + <!-- Pro případ, že chybí hlavička „Accept-language“ v HTTP požadavku -->
6.40 <param-name>javax.servlet.jsp.jstl.fmt.fallbackLocale</param-name>
6.41 <param-value>cs</param-value>
6.42 </context-param>
7.1 --- a/java/nekurak.net-web/web/js/hlasovani.js Sat Jan 15 14:27:02 2011 +0100
7.2 +++ b/java/nekurak.net-web/web/js/hlasovani.js Sat Jan 15 18:14:15 2011 +0100
7.3 @@ -1,7 +1,8 @@
7.4 var hlasovani = {};
7.5
7.6 hlasovani.hlasuj = function (podnik, hlas) {
7.7 - var pozadavek = "<hlas><kourit>" + hlas + "</kourit><podnik>" + podnik + "</podnik></hlas>";
7.8 + var csrfToken = document.getElementById("csrfToken").innerHTML;
7.9 + var pozadavek = "<hlas><kourit>" + hlas + "</kourit><podnik>" + podnik + "</podnik><csrfToken>" + csrfToken + "</csrfToken></hlas>";
7.10
7.11 $.ajax({
7.12 type: "POST",
7.13 @@ -10,14 +11,12 @@
7.14 contentType: "text/xml",
7.15 dataType: "text",
7.16 success: function(odpoved) {
7.17 - if (odpoved == "ok") {
7.18 - /** TODO: předělat informaci – nepoužívat alert ale text v SVG */
7.19 - /** TODO: Lokalizace */
7.20 - alert("Váš hlas byl přijat.");
7.21 - } else {
7.22 - /** TODO: předělat ošetřování chyb */
7.23 - alert("Při hlasování došlo k chybě.");
7.24 - }
7.25 + /** TODO: Lokalizace */
7.26 + alert("Váš hlas byl přijat.");
7.27 + },
7.28 + error: function(odpoved) {
7.29 + /** TODO: Lokalizace */
7.30 + alert("Při hlasování došlo k chybě. Zkuste obnovit stránku (F5) a opakujte hlasování.");
7.31 }
7.32 });
7.33 };
8.1 --- a/java/nekurak.net-web/web/styl.css Sat Jan 15 14:27:02 2011 +0100
8.2 +++ b/java/nekurak.net-web/web/styl.css Sat Jan 15 18:14:15 2011 +0100
8.3 @@ -15,6 +15,10 @@
8.4 margin: 0 auto;
8.5 }
8.6
8.7 +#csrfToken {
8.8 + display: none;
8.9 +}
8.10 +
8.11 #horniPruh {
8.12 width: 1000px;
8.13 border: 1px solid silver;