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>
<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.
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:
- https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html
- https://github.com/righettod/poc-csrf/blob/master/src/main/java/eu/righettod/poccsrf/filter/CSRFValidationFilter.java
- https://owasp.org/www-community/SameSite
- https://caniuse.com/?search=samesite
- https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/JSF-CSRF-Demo/JSF2.2CsrfDemo.html
- https://owasp.org/www-project-csrfguard/
- https://github.com/OWASP/www-project-csrfguard
- https://github.com/ariferol/jsf-csrf-simple.git
- https://www.imperva.com/learn/wp-content/uploads/sites/13/2019/01/csrf-cross-site-request-forgery.png