13 Mart 2022 Pazar

JSF CSRF protection

CSRF(Cross Site Request Forgery); kötü niyetli bir web sitesi, e-posta, blog, anlık mesaj veya program aracılığı ile kullanıcının kullandığı web tarayıcısı üzerinden, kimliği doğrulanmış olan web uygulamasından bir eylem gerçekleştirmesine neden olduğunda meydana gelen bir saldırı türüdür. JSF 2.2 in ci sürümden itibaren CSRF koruması gelmiş bulunmaktadır. CSRF saldırısı genelde phishing(e-dolandırıcılık) saldırısı ile yapılmaktadır. Senaryo alttaki şekilde olduğu gibi gerçekleşmektedir.

Bir JSF web uygulamasını kullanan kurban sisteme login olur. Yetkili session bilgileri kurbanın localinde yetkisine göre artık vardır. Saldırgan kurban a bir oltalama maili gönderir. Mail deki link saldırganın yazdığı form post işleminin yapıldığı siteye yönlendirilmiştir. Link açılır açılmaz, kurbanın aktif yetkili session bilgileri kullanılarak, kurbanın şifresi değiştirilmekte, kullanıcı adı ve şifre saldırgana mail olarak gitmektedir. Saldırgan çapraz bir sitedeki form post request i ile kurbanın tüm giriş bilgilerini ele geçirmiş bulunmaktadır. Başarılı bir CSRF saldırısının etkisi, savunmasız uygulamanın maruz kaldığı yeteneklerle ve kurbanların ayrıcalıklarıyla sınırlıdır.

Bu tarz bir istek sahteciliğini önleyebilmek için; Client – Server arası yapılacak her requestin güvenli hale getirilmesi gereklidir. Tavsiye edilen yöntemler;

1) Kullandığınız frameworklerde durum değişikliğine neden olan requestler için CSRF koruması olup olmadığını kontrol edin.

2) Eğer kullandığınız frameworkte CSRF koruması yok ise, Stateful(durum bilgisinin tutulduğu) synchronizer token pattern kullanın. Bu pattern da GET dışındaki her request için bir CSRF token üretip, doğrulamanız gereklidir.

3) Double Submit Cookie ile güçlü bir rastgele üretilmiş olan değeri, hem tanımlama bilgisi hem de istek bilgisi olarak cookie de client a gönderip, server site da doğrulayabilirsiniz.

4) Same Site Cookie Attribute: Yeni browser larda farklı sitelerden gelen isteklerde cookie lerin güvenlik seviyesi olarak yeni bir güvenlik attribute u olan “SameSite” eklenmiştir.

Set-Cookie: JSESSIONID=xxxxx; SameSite=Strict

Set-Cookie: JSESSIONID=xxxxx; SameSite=Lax

Set-Cookie: JSESSIONID=xxxxx; SameSite=None

SameSite ın desteklenen browser lardaki default değeri browserlara göre farklılık gösterir. Bu yüzden bu attribute i kendinizin ayarlamanız tavsiye edilir. (Browser desteği ve default değerleri için; https://caniuse.com/?search=samesite linkini inceleyebilirsiniz). “None” da hiçbir koruma yoktur. “Lax” ta farklı domainlerden gelen sadece GET request lerindeki cookielerin geçişine izin verilir. Güvenlik seviyesini en üst seviyede tutmak isterseniz bu değeri “Strict” yapmanız gerekir. Bu sayede farklı dns ler için cookie değerlerinin geçişine izin verilmez. Tabi bu da CSRF saldırılarını önlemede bir miktar daha katkı sağlamış olur. Yukarıda cookie de tutulan session id değerinin farklı domainlerden yapılan requestlerde izin verilip verilmemesi ile ilgili yetkilendirme yapılmaktadır.

5) Kullanıcı etkileşimli korumalar(Yetkilendirerek yeniden kimlik doğrulama, tek kullanımlık token lar, yeni CAPTCHA versiyonlarının kullanımı).

6) Header bilgileri özelleştirilmiş request(istek) kullanabilirsiniz.

7) Standart header da origin kontrolünü yapabilirsiniz(CORS filter).

8) XSS ile ilgili alınan tedbirler de CSRF saldırılarını önlemede yardımcıdır.

9) State(durum) değişikliğine neden olan tüm request lerde GET kullanmayın.

Benim CSRF güvenlik seviyesi ile ilgili olmazsa olmaz önerim 1. veya 2. öneri ile beraber 9. öneriyi web uygulamanız için implemente etmeniz olacaktır. Sonrasında 1 veya 2 yi uyguladıysanız ve güvenlik seviyenizi artırmak istiyorsanız ise; 4, 7 ve 8 inci önerileri sırayla uygulamanızı öneririm.

1. Öneride bahsedilen JSF frameworkte CSRF koruması nasıl çalışır ona bakalım. Yukarıda , JSF 2.2 den itibaren CSRF koruması geldi demiştik. Bununla ilgili implementation örneğini Oracle ‘ın kendi white paper larından alıp deneyebilirsiniz(https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/JSF-CSRF-Demo/JSF2.2CsrfDemo.html). Buradaki örnekte GET dışındaki tüm request lerde CSRF koruması default olarak aktif tir. GET requestlerindeki koruma için; “faces-config.xml” dosyasının içinde “protected-views” taglar i arasında GET request koruması yapılacak jsf sayfalarının pathleri tanımlanmalıdır. Örn.:

<protected-views>

            <url-pattern>/csrf_protected_page.xhtml</url-pattern>

</protected-views>

            Her form verisinin en altında “javax.faces.ViewState” isminde hidden olan bir input değeri otomatik olarak eklenir. Bu view state değerinin default değeri server side da tutulmaktadır. Ancak bazen gerek performans için gerekse de clustered web application larda sticky session kullanıldığından tüm sunucularda bu değerin ortak kullanılmasına ihtiyaç duyulmaktadır. Bunun için “web.xml” içerisinde aşağıdaki config değerlerinin girilmesi gerekir;

<env-entry>

    <env-entry-name>jsf/ClientSideSecretKey</env-entry-name>

    <env-entry-type>java.lang.String</env-entry-type>

    <env-entry-value>[AES key in Base64 format]</env-entry-value>

</env-entry>

<context-param>

    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>

    <param-value>client</param-value>

</context-param>

Burada gireceğiniz AES key değerini en az 128 bit key olarak generate ettikten sonra mutlaka UTF8 Base64 string olarak encode etmeyi unutmayın. Bu config değerinden sonra artık client ta form içindeki viewstate hidden değeri browser tarafında gösterilmeye başlanacaktır. Her clustered sunucuda ki application aynı aes key değerini kullanarak viewstate değerini generate ettiği için sticky session ile ilgili bir sorun yaşanmayacaktır.. Bu projenin github örnek kaynak kodunu “https://github.com/ariferol/jsf-csrf-simple.git” linkinden inceleyebilirsiniz.

Peki, JSF 2.2 default CSRF protection dışında, JSF için başka bir koruma seçeneği var mıdır? Evet var. Özellikle JSF 2.2 den önceki sürümler için kendi özel çözümünüzü üretmeniz gerekmektedir. Özel çözüm üretecekseniz benim önerim; OWASP(Open Web Application Security Project) ta JEE application lar için önerilen CSRF Guard API sini kullanmanızdır. CSRF Guard API nin open source github repository sine “https://github.com/OWASP/www-project-csrfguard” linkinden ulaşabilirsiniz.

API nin JSF teki implementation u için;

1) Maven POM file in içine dependency lerini ekleyin;

        <dependency>

            <groupId>org.owasp</groupId>

            <artifactId>csrfguard</artifactId>

            <version>4.1.2</version>

        </dependency>

 

        <!-- Stateful web application support -->

        <dependency>

            <groupId>org.owasp</groupId>

            <artifactId>csrfguard-extension-session</artifactId>

            <version>4.1.2</version>

        </dependency>

 

        <!-- JSP TAG support -->

        <dependency>

            <groupId>org.owasp</groupId>

            <artifactId>csrfguard-jsp-tags</artifactId>

            <version>4.1.2</version>

        </dependency>

2) “src/main/resources” folder inin içine “csrfguard.properties” configrasyon dosyasını kopyalayın.

3) “web.xml” dosyası içerisine listenerları, context parametrelerini, filter ve koruma altına alınacak url pattern pathini, javascript servlet ve bu servletin url pattern ını aşağıdaki gibi ekleyin.

<servlet-mapping>

            <servlet-name>Faces Servlet</servlet-name>

            <url-pattern>/other-context-url/*</url-pattern>

</servlet-mapping>

    <listener>

        <listener-class>org.owasp.csrfguard.CsrfGuardServletContextListener</listener-class>

    </listener>

    <listener>

        <listener-class>org.owasp.csrfguard.CsrfGuardHttpSessionListener</listener-class>

    </listener>

 

    <context-param>

        <param-name>Owasp.CsrfGuard.Config</param-name>

        <param-value>/csrfguard.properties</param-value>

    </context-param>   

    <context-param>

        <param-name>Owasp.CsrfGuard.Config.Print</param-name>

        <param-value>true</param-value>

    </context-param>

   

    <filter>

        <filter-name>CSRFGuard</filter-name>

        <filter-class>org.owasp.csrfguard.CsrfGuardFilter</filter-class>

    </filter>

    <filter-mapping>

        <filter-name>CSRFGuard</filter-name>

        <url-pattern>/other-context-url/content/*</url-pattern>

    </filter-mapping>

 

    <servlet>

        <servlet-name>JavaScriptServlet</servlet-name>

        <servlet-class>org.owasp.csrfguard.servlet.JavaScriptServlet</servlet-class>

    </servlet>

    <servlet-mapping>

        <servlet-name>JavaScriptServlet</servlet-name>

        <url-pattern>/JavaScriptServlet</url-pattern>

    </servlet-mapping>

4) “csrfguard.properties” config dosyasının içinde;

org.owasp.csrfguard.UnprotectedMethods = GET (GET dışındaki tüm methodlarda POST,PUT,DELETE gibi CSRF korumasını açık hale getirin)

org.owasp.csrfguard.unprotected.Error = %servletContext%/error.html (context-root un olduğu giriş sayfasının olduğu pathe error.html isimli bir kullanıcı bilgilendirme sayfası ekleyip, hata durumunda yönlendirilmesini sağlayın)

5) Koruma altına alınacak her form bulunan jsf sayfasının head tag inin içerisine;

<h:head>

            <title>Page Title </title>

            <script src="/context-root/other-context-url/JavaScriptServlet"></script>       

</h:head>

Şeklinde apinin içinden çağırılan javascript kütüphanesi import bildirimini ekleyin. Eğer single page app kullanıyorsanız, dashboard ın olduğu ana sayfaya bu bildirimi bir kez eklemeniz yeterli olacaktır.Dashboard dan dallanılan tüm sayfa url leri için de dashboard sayfası render olduğu için diğer sayfalara eklemenize gerek kalmayacaktır.

Web uygulaması çalıştırıldığında jsf sayfasının browserda ki kaynak koduna baktığınızda formun en altında hidden field olarak aşağıdaki gibi CSRF token ın generate edildiğini göreceksiniz;

<input type="hidden" name="OWASP-CSRFTOKEN" value="S5T3-UWOD-QGHK-S37C-U0OZ-T22Q-PTMA-5D1V">

Kaynaklar: