Selamlar! Ben Bunjo. Bildiğiniz gibi Ruby dilinde Sinatra adında, basit ve hızlı web uygulamaları geliştirmemizi sağlayan bir framework var.
Bu framework için temel seviyede bir uygulama güvenliği konusu hazırladım.
En baştan belirteyim: Bu konuyu internette Türkçe kaynak olmadığı için yazıyorum ve ben bir güvenlik şirketi değilim
dolayısıyla bu konu temel bir anlatım içermektedir.
Konuda bulunan kodlarda eksik bir şey olduğunu düşünüyorsanız, istediğiniz gibi alıp geliştirebilirsiniz.

HTTP Security Headers
Bu framework için temel seviyede bir uygulama güvenliği konusu hazırladım.
En baştan belirteyim: Bu konuyu internette Türkçe kaynak olmadığı için yazıyorum ve ben bir güvenlik şirketi değilim
dolayısıyla bu konu temel bir anlatım içermektedir.
Konuda bulunan kodlarda eksik bir şey olduğunu düşünüyorsanız, istediğiniz gibi alıp geliştirebilirsiniz.

HTTP Security Headers
Ruby:
require 'sinatra'
require 'securerandom'
class SecurityHeaders
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
# Nonce oluştur
nonce = SecureRandom.base64(16)
env['csp_nonce'] = nonce
# Başlıklar
headers['Content-Security-Policy'] = "default-src 'self'; script-src 'self' 'nonce-#{nonce}'; style-src 'self' 'unsafe-inline'; object-src 'none'; frame-ancestors 'none'"
headers['X-Content-Type-Options'] = 'nosniff' # MIME sniffing engelle
headers['X-Frame-Options'] = 'DENY' # clickjacking
headers['X-XSS-Protection'] = '1; mode=block' # eski tarayıcılar için XSS koruması
headers['Referrer-Policy'] = 'strict-origin-when-cross-origin'
headers['Permissions-Policy'] = 'geolocation=(), microphone=()'
headers['Strict-Transport-Security'] = 'max-age=63072000; includeSubDomains; preload'
[status, headers, body]
end
end
use SecurityHeaders
CSP testi isteyenler için:
Ruby:
require 'sinatra'
require 'securerandom'
before do
nonce = SecureRandom.base64(16)
headers['Content-Security-Policy'] = "default-src 'self'; script-src 'self' 'nonce-#{nonce}'"
env['csp_nonce'] = nonce
end
get '/' do
"<script nonce='#{env['csp_nonce']}'>console.log('Inline script güvenli');</script>"
end
Bu başlıkların ne kadar çok yararlı olup olmadığı size kalmış bir durumdur.

Güvenli Dosya Yükleme
Bu konuda gelişmiş yöntemler Sinatra'da barınmıyor. MIME tür kontrolü ile yetinmelisiniz. Bunun içinde 'minimagic' kütüphanesini öneriyorum.

Güvenli Dosya Yükleme
Bu konuda gelişmiş yöntemler Sinatra'da barınmıyor. MIME tür kontrolü ile yetinmelisiniz. Bunun içinde 'minimagic' kütüphanesini öneriyorum.
Ruby:
require 'mimemagic'
file = params[:file][:tempfile]
mime = MimeMagic.by_magic(file)
allowed = %w[image/png image/jpeg application/pdf]
halt 415, "Unsupported file type" unless allowed.include?(mime.type)
İsteyenler için regex kontrolünü de ekleyeyim:
Ruby:
require 'securerandom'
filename = params[:file][:filename]
halt 400, "Geçersiz dosya adı" unless filename.match?(/\A[\w\-.]+\z/)
safe_name = "#{SecureRandom.hex(8)}-#{filename}"

CSRF Token
eksantrik yöntemler yapmaya gerek yok Rack ile kısa yoldan basit güvenlik sağlanabiliyor.
Ruby:
require 'sinatra'
require 'rack/protection'
require 'securerandom'
use Rack::Protection::AuthenticityToken
enable :sessions
set :session_secret, SecureRandom.hex(64)
before do
response.set_cookie('rack.session',
value: session,
httponly: true,
secure: request.secure?,
same_site: :strict)
end
get '/form' do
<<-HTML
<form action="/submit" method="post">
<input type="hidden" name="authenticity_token" value="#{session[:csrf]}">
<input type="text" name="data">
<button>Gönder</button>
</form>
HTML
end
post '/submit' do
"İstek başarılı! Gönderilen veri: #{params[:data]}"
end

Güvenli Session Yönetimi
Temel düzeyde "HttpOnly, SameSite Cookie, Session Timeout, IP ve User-Agent, CSRF" korumaları bulunduran örnek bir kodu aşağıdaki şekilde yazabilirsiniz. Veritabanı bağlama kısmını daha anlatmadığım için şifre kontrolünü değişkenlerle yaptım. Şuan bu konuda o kısımları anlatmayacağım.
Ruby:
require 'sinatra'
require 'securerandom'
require 'rack/protection'
enable :sessions
set :session_secret, SecureRandom.hex(64) # baya güçlü secret key
use Rack::Protection::AuthenticityToken
before do
response.set_cookie('rack.session',
value: session,
httponly: true,
secure: request.secure?,
same_site: :strict)
if session[:last_seen] && Time.now - session[:last_seen] > 1800
session.clear
end
session[:last_seen] = Time.now
end
helpers do
def logged_in?
session[:user_id]
end
def require_login!
halt 401, "Lütfen giriş yapın" unless logged_in?
end
end
get '/login' do
<<-HTML
<form action="/login" method="post">
<input type="text" name="username" placeholder="Kullanıcı Adı">
<input type="password" name="password" placeholder="Parola">
<!-- CSRF Token -->
<input type="hidden" name="authenticity_token" value="#{session[:csrf]}">
<button>Giriş</button>
</form>
HTML
end
post '/login' do
if params[:username] == 'admin' && params[:password] == '1234'
session[:user_id] = params[:username]
session[:ip] = request.ip
session[:user_agent] = request.user_agent
redirect '/dashboard'
else
"Giriş başarısız!"
end
end
post '/logout' do
session.clear
redirect '/login'
end
get '/dashboard' do
require_login!
if session[:ip] != request.ip || session[:user_agent] != request.user_agent
session.clear
halt 403, "Oturum güvenliği ihlali!"
end
<<-HTML
Hoşgeldiniz #{session[:user_id]}! Bu sayfa sadece giriş yapmış kullanıcılar için.
<form action="/logout" method="post">
<input type="hidden" name="authenticity_token" value="#{session[:csrf]}">
<button>Çıkış Yap</button>
</form>
HTML
end
Ruby:
require 'erb'
include ERB::Util
# hatalı kullanım
<p>Merhaba <%= h(params[:name]) %></p>
# 40 yıllık HTML Escape fonksiyonu ile diğerine göre daha güvenli kullanım:
"Merhaba #{ERB::Util.html_escape(params[:name])}"
erb formatı inline js güvenli gömme:
Ruby:
<script nonce="<%= env['csp_nonce'] %>">
// yanlış: var name = "<%= params[:name] %>";
var name = <%= params[:name].to_json %>; // güvenli: JS string literal olarak
console.log(name);
</script>
kullanıcıya html izni verilecek ise "Sanitize" gem kullanılır:
Ruby:
require 'sinatra'
require 'erb'
require 'sanitize'
get '/show' do
name = params[:name].to_s
# 1) escape (varsayılan)
"<p>Merhaba #{ERB::Util.html_escape(name)}</p>"
end
get '/profile' do
# eğer user_html izinliyse: sanitize ile beyaz-listele yapılır
raw_html = params[:bio] || ""
clean = Sanitize.fragment(raw_html, elements: %w[p b i strong a], attributes: {'a' => ['href']})
"<div>#{clean}</div>"
end

Konuyu hazırlarken internette bulunan yabancı kaynaklardan yararlandım. İnternette arattığınız zaman zaten pek bir kaynak bulamazsınız çünkü yok.
İncelemek isteyenler olursa diye buraya örnek bırakıyorum.
Teorik ve içi boş anlatımlara pek girmedim zira bazı moderatör arkadaşlar bu işleri yapıyor zaten.
Injection zafiyetlerinde oldukça temel şeylerin kaynağı olduğu için pek sağlıklı bir içerik sunamayacaktım bu yüzden anlatmak istemedim.
Güvenli bir web sitesi istiyorsanız Sinatra'dan uzak durun.
Teşekkürler.



