WordPress’te Otomatik Güncelleme Kontrolü: Sadece Güvenlik Güncellemeleri
WordPress sitenizi yönetirken en can sıkıcı anlardan biri şu ikilemi yaşamaktır: Eklentileri güncellemek mi, yoksa güncel tutmamak mı? Güncellemezseniz güvenlik açıkları birikmaya başlar. Güncellerseniz plugin uyumsuzluklarından kaynaklanan beyaz ekranlarla karşılaşabilirsiniz. İşte tam da bu noktada akıllıca bir yaklaşım devreye giriyor: sadece güvenlik güncellemelerini otomatik uygula, özellik güncellemelerini ise manuel denetle.
Bu yazıda WordPress’in functions.php dosyasına ekleyebileceğiniz, güvenlik güncelleme kontrolünü tamamen otomatikleştiren, özellik güncellemelerini ise sizi bilgilendiren kapsamlı bir sistem kuracağız.
WordPress Güncelleme Sistemini Anlamak
WordPress’in kendi güncelleme mekanizması oldukça esnek bir API üzerine kurulmuştur. Arka planda wp_version_check, wp_update_plugins ve wp_update_themes gibi cron görevleri düzenli aralıklarla WordPress.org API’sine bağlanır ve mevcut güncellemeleri kontrol eder.
Ancak WordPress’in standart otomatik güncelleme sistemi ya tamamen açık ya da tamamen kapalıdır. Belirli türdeki güncellemeleri filtrelemek istediğinizde işler karmaşıklaşmaya başlar. Neyse ki WordPress bu konuda bize hook’lar ve filter’lar aracılığıyla müdahale imkanı tanıyor.
Güncelleme Türleri Nelerdir?
WordPress güncellemelerini üç ana kategoride değerlendirmek gerekir:
- Major güncellemeler: WordPress 5.x’ten 6.x’e geçiş gibi büyük sürüm atlamaları
- Minor güncellemeler: 6.4.0’dan 6.4.1’e geçiş gibi küçük sürüm değişiklikleri, genellikle güvenlik yamaları içerir
- Plugin ve tema güncellemeleri: Her birinin kendi sürüm geçmişi vardır ve güvenlik odaklı olup olmadığı her zaman belli değildir
İşte asıl sorun da burada yatıyor: WordPress.org, bir güncellemenin güvenlik yamalarını içerip içermediğini resmi API aracılığıyla her zaman açıkça belirtmiyor. Bu yüzden birkaç farklı strateji bir arada kullanmak gerekiyor.
Temel Yapıyı Kurmak
Önce functions.php dosyamıza ekleyeceğimiz iskelet yapıyı oluşturalım. Bu yapı, tüm güncelleme mantığını organize tutacak ve yönetimini kolaylaştıracak.
<?php
/**
* Güvenlik Güncelleme Kontrol Sistemi
*
* Sadece güvenlik güncellemelerini otomatik uygular,
* diğer güncellemeleri e-posta ile bildirir.
*
* @author Sysadmin Blog
* @version 2.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class WP_Security_Update_Controller {
private $security_keywords = [
'security',
'güvenlik',
'vulnerability',
'patch',
'critical',
'hotfix',
'XSS',
'SQL injection',
'CSRF',
'CVE-'
];
private $notification_email;
private $log_file;
public function __construct() {
$this->notification_email = get_option( 'admin_email' );
$this->log_file = WP_CONTENT_DIR . '/security-updates.log';
$this->init_hooks();
}
private function init_hooks() {
// WordPress core güncelleme filtreleri
add_filter( 'auto_update_core', [ $this, 'filter_core_updates' ], 10, 2 );
// Plugin güncelleme filtreleri
add_filter( 'auto_update_plugin', [ $this, 'filter_plugin_updates' ], 10, 2 );
// Tema güncelleme filtreleri
add_filter( 'auto_update_theme', [ $this, 'filter_theme_updates' ], 10, 2 );
// Güncelleme kontrolü sonrası bildirim
add_action( 'wp_update_plugins', [ $this, 'notify_pending_updates' ] );
// Admin paneli bildirimi
add_action( 'admin_notices', [ $this, 'show_admin_notice' ] );
}
}
$wp_security_controller = new WP_Security_Update_Controller();
WordPress Core Güncellemelerini Yönetmek
WordPress core güncellemeleri için minor sürümleri (güvenlik yamaları) otomatik onaylarken major sürümleri bekletmek en mantıklı yaklaşımdır.
public function filter_core_updates( $update, $item ) {
// Minor sürümler otomatik güncelle (6.4.1 -> 6.4.2 gibi)
if ( isset( $item->response ) && $item->response === 'autoupdate' ) {
$this->log_update( 'core', 'auto', $item->version ?? 'bilinmiyor' );
return true;
}
// Major sürüm kontrolü
if ( isset( $item->new_version ) && isset( $item->current ) ) {
$current_parts = explode( '.', $item->current );
$new_parts = explode( '.', $item->new_version );
// Major versiyon değişikliği var mı?
if ( $current_parts[0] !== $new_parts[0] ) {
// Major güncelleme - bildir ama otomatik yapma
$this->send_major_update_notification( 'WordPress Core', $item->current, $item->new_version );
$this->log_update( 'core', 'skipped_major', $item->new_version );
return false;
}
// Minor/patch güncelleme - otomatik yap
$this->log_update( 'core', 'auto_approved', $item->new_version );
return true;
}
return $update;
}
private function send_major_update_notification( $name, $current, $new_version ) {
$subject = "[{$_SERVER['HTTP_HOST']}] Major Güncelleme Bekleniyor: {$name}";
$message = "Merhaba,nn";
$message .= "Aşağıdaki major güncelleme otomatik uygulanmadı ve manuel inceleme gerekiyor:nn";
$message .= "Bileşen: {$name}n";
$message .= "Mevcut Sürüm: {$current}n";
$message .= "Yeni Sürüm: {$new_version}nn";
$message .= "Güncelleme paneli: " . admin_url( 'update-core.php' ) . "nn";
$message .= "Bu e-posta otomatik olarak gönderilmiştir.";
wp_mail( $this->notification_email, $subject, $message );
}
Plugin Güncellemelerinde Güvenlik Tespiti
Bu sistem için asıl zekice kısım burasıdır. WordPress.org plugin API’si, güncellemelerin değişiklik notlarını (changelog) içeriyor. Bu notları analiz ederek güvenlik yaması içeren güncellemeleri tespit edebiliriz.
public function filter_plugin_updates( $update, $item ) {
if ( ! isset( $item->slug ) ) {
return $update;
}
// Güvenlik güncellemesi mi kontrol et
$is_security = $this->check_plugin_security_update( $item->slug, $item->new_version ?? '' );
if ( $is_security ) {
$this->log_update( 'plugin', 'security_auto', $item->slug . ' v' . $item->new_version );
$this->send_security_update_notification( $item->name ?? $item->slug, $item->new_version ?? '' );
return true; // Otomatik güncelle
}
// Güvenlik güncellemesi değil, bildir ama yapma
$this->log_update( 'plugin', 'skipped_feature', $item->slug . ' v' . $item->new_version );
return false;
}
private function check_plugin_security_update( $slug, $new_version ) {
// Plugin bilgilerini WordPress.org API'sinden çek
$response = wp_remote_get(
"https://api.wordpress.org/plugins/info/1.0/{$slug}.json",
[ 'timeout' => 15 ]
);
if ( is_wp_error( $response ) ) {
// API'ye ulaşamazsa güvenli tarafta kal, güncelleme yapma
$this->log_update( 'plugin', 'api_error', $slug );
return false;
}
$plugin_data = json_decode( wp_remote_retrieve_body( $response ), true );
if ( empty( $plugin_data ) ) {
return false;
}
// Sections içinde changelog var mı?
$changelog = $plugin_data['sections']['changelog'] ?? '';
if ( empty( $changelog ) ) {
return false;
}
// Yeni sürümün changelog'unu bul
$version_pattern = '/=' . preg_quote( $new_version, '/' ) . '[^=]*=(.*?)(?==|z)/si';
preg_match( $version_pattern, strip_tags( $changelog ), $matches );
$version_changelog = $matches[1] ?? $changelog;
// Güvenlik kelimelerini ara
return $this->contains_security_keywords( $version_changelog );
}
private function contains_security_keywords( $text ) {
$text_lower = mb_strtolower( $text );
foreach ( $this->security_keywords as $keyword ) {
if ( strpos( $text_lower, mb_strtolower( $keyword ) ) !== false ) {
return true;
}
}
return false;
}
Tema Güncellemelerini Yönetmek
Temalar için de benzer bir yaklaşım kullanıyoruz ancak tema güncellemeleri genellikle visual değişiklikler içerdiğinden biraz daha temkinli olmak mantıklı.
public function filter_theme_updates( $update, $item ) {
if ( ! isset( $item->theme ) ) {
return $update;
}
// Aktif tema ise daha dikkatli ol
$active_theme = get_stylesheet();
$active_parent = get_template();
$is_active_theme = ( $item->theme === $active_theme || $item->theme === $active_parent );
// Tema güncelleme notlarını kontrol et
$is_security = $this->check_theme_security_update( $item->theme, $item->new_version ?? '' );
if ( $is_security && ! $is_active_theme ) {
// Aktif olmayan tema + güvenlik güncellemesi = otomatik güncelle
$this->log_update( 'theme', 'security_auto', $item->theme );
return true;
}
if ( $is_security && $is_active_theme ) {
// Aktif tema + güvenlik güncellemesi = bildir, uygulama
// Aktif tema güncellemesi site görünümünü bozabilir
$this->send_active_theme_security_alert( $item->theme, $item->new_version ?? '' );
$this->log_update( 'theme', 'active_security_alert', $item->theme );
return false;
}
// Güvenlik güncellemesi değil, atla
$this->log_update( 'theme', 'skipped', $item->theme );
return false;
}
private function check_theme_security_update( $theme_slug, $new_version ) {
$response = wp_remote_get(
"https://api.wordpress.org/themes/info/1.1/?action=theme_information&request[slug]={$theme_slug}",
[ 'timeout' => 15 ]
);
if ( is_wp_error( $response ) ) {
return false;
}
$theme_data = json_decode( wp_remote_retrieve_body( $response ), true );
$sections = $theme_data['sections'] ?? [];
$changelog = $sections['changelog'] ?? '';
if ( empty( $changelog ) ) {
return false;
}
return $this->contains_security_keywords( $changelog );
}
private function send_active_theme_security_alert( $theme, $version ) {
$subject = "[ACIL] [{$_SERVER['HTTP_HOST']}] Aktif Tema Güvenlik Güncellemesi: {$theme}";
$message = "DİKKAT: Aktif temanız için kritik bir güvenlik güncellemesi mevcut.nn";
$message .= "Tema: {$theme}n";
$message .= "Yeni Sürüm: {$version}nn";
$message .= "Bu güncelleme otomatik uygulanmadı çünkü aktif tema değişiklikleri sitenizi etkileyebilir.n";
$message .= "Lütfen bir staging ortamında test ederek uygulayınız.nn";
$message .= "Güncelleme sayfası: " . admin_url( 'themes.php' );
wp_mail( $this->notification_email, $subject, $message );
}
Güncelleme Loglaması
Her otomatik güncelleme işlemini dosyaya kaydetmek, ileride sorun çıkması durumunda ne zaman neyin değiştiğini anlamanızı sağlar.
private function log_update( $type, $action, $identifier ) {
$timestamp = current_time( 'Y-m-d H:i:s' );
$site = $_SERVER['HTTP_HOST'] ?? 'bilinmiyor';
$log_entry = "[{$timestamp}] [{$site}] [{$type}] [{$action}] {$identifier}n";
// Dosya boyut kontrolü (max 5MB)
if ( file_exists( $this->log_file ) && filesize( $this->log_file ) > 5 * 1024 * 1024 ) {
$this->rotate_log();
}
error_log( $log_entry, 3, $this->log_file );
}
private function rotate_log() {
$backup_file = $this->log_file . '.' . date( 'Y-m-d' ) . '.bak';
rename( $this->log_file, $backup_file );
// Eski backup dosyalarını temizle (30 günden eski)
$log_dir = dirname( $this->log_file );
$files = glob( $log_dir . '/security-updates.log.*.bak' );
foreach ( $files as $file ) {
if ( filemtime( $file ) < time() - ( 30 * DAY_IN_SECONDS ) ) {
unlink( $file );
}
}
}
Bekleyen Güncelleme Bildirimleri
Haftalık olarak bekleyen tüm güncellemelerin özetini admin’e göndermek, hiçbir şeyin gözden kaçmamasını sağlar.
public function notify_pending_updates() {
// Haftada bir çalışmasını sağla
$last_notification = get_transient( 'security_update_last_notification' );
if ( $last_notification ) {
return;
}
$update_data = get_site_transient( 'update_plugins' );
if ( empty( $update_data->response ) ) {
return;
}
$pending_updates = [];
foreach ( $update_data->response as $plugin_file => $data ) {
// Otomatik uygulanan güvenlik güncellemeleri dışındakileri topla
$is_security = $this->check_plugin_security_update( $data->slug, $data->new_version );
if ( ! $is_security ) {
$pending_updates[] = [
'name' => $data->name ?? $data->slug,
'current' => $data->Version ?? 'bilinmiyor',
'new_version' => $data->new_version,
'type' => 'feature'
];
}
}
if ( ! empty( $pending_updates ) ) {
$this->send_weekly_update_digest( $pending_updates );
}
// 7 gün boyunca tekrar gönderme
set_transient( 'security_update_last_notification', time(), 7 * DAY_IN_SECONDS );
}
private function send_weekly_update_digest( $updates ) {
$subject = "[{$_SERVER['HTTP_HOST']}] Haftalık Güncelleme Raporu";
$message = "Merhaba,nn";
$message .= "Bu hafta otomatik uygulanmayan güncellemeler:nn";
$message .= "NOT: Güvenlik güncellemeleri otomatik uygulandı. Aşağıdakiler özellik güncellemeleridir.nn";
foreach ( $updates as $update ) {
$message .= "- {$update['name']}: {$update['current']} -> {$update['new_version']}n";
}
$message .= "nGüncelleme paneli: " . admin_url( 'update-core.php' ) . "nn";
$message .= "Log dosyası: " . $this->log_file;
wp_mail( $this->notification_email, $subject, $message );
}
Admin Panel Bildirimi
WordPress admin panelinde görsel bir uyarı göstermek de son derece kullanışlıdır. Sisteme giriş yaptığınızda hemen durumu görürsünüz.
public function show_admin_notice() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
// Log dosyasından son güncellemeleri oku
$recent_updates = $this->get_recent_auto_updates( 5 );
if ( empty( $recent_updates ) ) {
return;
}
echo '<div class="notice notice-success is-dismissible">';
echo '<p><strong>Otomatik Güvenlik Güncellemeleri:</strong> Son 24 saatte uygulanan güvenlik yamaları:</p>';
echo '<ul>';
foreach ( $recent_updates as $update ) {
echo '<li>' . esc_html( $update ) . '</li>';
}
echo '</ul>';
echo '<p><a href="' . esc_url( WP_CONTENT_URL . '/security-updates.log' ) . '" target="_blank">Tüm log dosyasını görüntüle</a></p>';
echo '</div>';
}
private function get_recent_auto_updates( $limit = 5 ) {
if ( ! file_exists( $this->log_file ) ) {
return [];
}
$lines = file( $this->log_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
$recent = [];
$cutoff = time() - DAY_IN_SECONDS;
foreach ( array_reverse( $lines ) as $line ) {
if ( count( $recent ) >= $limit ) {
break;
}
// Sadece "auto" içerenleri al
if ( strpos( $line, '_auto' ) !== false ) {
preg_match( '/[(d{4}-d{2}-d{2} d{2}:d{2}:d{2})]/', $line, $time_match );
if ( ! empty( $time_match[1] ) ) {
$entry_time = strtotime( $time_match[1] );
if ( $entry_time >= $cutoff ) {
$recent[] = trim( $line );
}
}
}
}
return $recent;
}
Sistemi Test Etmek
Kurduğunuz sistemi gerçek güncellemeler gelmeden test etmek için aşağıdaki WP-CLI komutlarını kullanabilirsiniz:
# Mevcut güncelleme durumunu listele
wp plugin list --update=available --format=table
# Güncelleme kontrol mekanizmasını zorla çalıştır
wp cron event run wp_update_plugins
# Belirli bir plugin'i simüle et
wp eval "do_action('wp_update_plugins');"
# Log dosyasını canlı izle
tail -f /var/www/html/wp-content/security-updates.log
# Son 50 log kaydını göster
tail -50 /var/www/html/wp-content/security-updates.log | grep "security_auto"
Gerçek dünya senaryosunda şöyle bir durum yaşadım: WooCommerce’in bir güvenlik yaması geldi ve sistemimiz bunu otomatik uyguladı. Fakat aynı gün WooCommerce Stripe eklentisi de major bir özellik güncellemesi aldı ve bu güncelleme kasıtlı olarak tutuldu. E-posta ile bildirim geldi ve ödeme entegrasyonunu etkileyebilecek bu güncellemeyi staging’de test ettikten sonra manuel olarak uyguladık. Sistemin değeri tam da bu an ortaya çıktı.
Güvenlik Keyword Listesini Özelleştirmek
Farklı plugin ekipleri güvenlik güncellemelerini farklı şekillerde etiketler. Keyword listenizi genişletmek için şu eklemeleri yapabilirsiniz:
// WP_Security_Update_Controller sınıfının constructor'ında
// veya bir filter aracılığıyla listeyi genişlet
add_filter( 'security_update_keywords', function( $keywords ) {
// WooCommerce özelinde ekleme
$keywords[] = 'payment';
$keywords[] = 'authentication';
$keywords[] = 'authorization';
$keywords[] = 'injection';
$keywords[] = 'bypass';
$keywords[] = 'disclosure';
$keywords[] = 'traversal';
$keywords[] = 'güvenlik açığı';
return $keywords;
} );
// Sınıfın constructor'ında filtreyi uygula
$this->security_keywords = apply_filters( 'security_update_keywords', $this->security_keywords );
Cron Zamanlamasını Ayarlamak
WordPress’in varsayılan cron kontrolü her 12 saatte bir çalışır. Güvenlik güncellemeleri için bu süreyi kısaltmak isteyebilirsiniz:
// Güncelleme kontrolü sıklığını artır
add_filter( 'wp_update_plugins', function() {
return 6 * HOUR_IN_SECONDS; // 6 saatte bir kontrol
} );
// Veya sunucu cron'u ile daha güvenilir bir yöntem kullan
// /etc/cron.d/wordpress-updates dosyasına ekle:
// */30 * * * * www-data cd /var/www/html && wp cron event run wp_update_plugins --quiet
# WordPress cron'u sistem cron ile çalıştırmak için
# wp-config.php dosyasına şunu ekleyin:
# define('DISABLE_WP_CRON', true);
# Sonra sistem cron'una ekleyin:
echo "*/30 * * * * www-data /usr/bin/wp --path=/var/www/html cron event run --due-now --quiet" | sudo tee /etc/cron.d/wordpress-updates
# Cron dosyası izinlerini ayarla
sudo chmod 644 /etc/cron.d/wordpress-updates
sudo systemctl restart cron
Log Analizi için Basit Araçlar
Zamanla log dosyası büyüyecek ve analiz etmek zorlaşacak. Aşağıdaki bash scriptini /usr/local/bin/wp-update-report olarak kaydedin:
#!/bin/bash
# WordPress Güvenlik Güncelleme Raporu
# Kullanım: wp-update-report /var/www/html/wp-content/security-updates.log
LOG_FILE="${1:-/var/www/html/wp-content/security-updates.log}"
if [ ! -f "$LOG_FILE" ]; then
echo "Log dosyası bulunamadı: $LOG_FILE"
exit 1
fi
echo "=== WordPress Güvenlik Güncelleme Raporu ==="
echo "Log: $LOG_FILE"
echo ""
echo "--- Otomatik Uygulanan Güvenlik Güncellemeleri ---"
grep "security_auto" "$LOG_FILE" | wc -l | xargs echo "Toplam:"
grep "security_auto" "$LOG_FILE" | tail -10
echo ""
echo "--- Atlanan Özellik Güncellemeleri ---"
grep "skipped_feature" "$LOG_FILE" | wc -l | xargs echo "Toplam:"
grep "skipped_feature" "$LOG_FILE" | tail -5
echo ""
echo "--- Hata Durumları ---"
grep "api_error|skipped_major" "$LOG_FILE" | tail -5
echo ""
echo "--- Son 24 Saat Özeti ---"
YESTERDAY=$(date -d "yesterday" '+%Y-%m-%d' 2>/dev/null || date -v-1d '+%Y-%m-%d')
grep "$YESTERDAY|$(date '+%Y-%m-%d')" "$LOG_FILE" | grep "auto" | wc -l | xargs echo "Otomatik güncelleme:"
Sonuç
Bu sistemin güzelliği, tek bir functions.php dosyasına eklediğiniz kodla sitenizin güvenlik duruşunu dramatik biçimde iyileştirmenizdir. Güvenlik açıkları her gün keşfediliyor ve yamalar çıkıyor. Bu yamaları manuel takip etmek hem zaman alıcı hem de insan hatasına açık bir süreçtir.
Kurduğumuz sistemle özetle şunları elde ediyorsunuz: Core minor güncellemeleri anında uygulanıyor, plugin güvenlik yamaları changelog analizi ile tespit edilip otomatik uygulanıyor, tema güncellemelerinde aktif/pasif tema ayrımı yapılıyor, her şey loglanıyor ve haftalık özet rapor geliyor.
WooCommerce mağazaları için bu sistem özellikle kritik. Ödeme eklentilerindeki güvenlik açıkları dakikalar içinde istismar edilebilir. Otomatik güvenlik güncellemeleri bu riski minimize ederken, özellik güncellemelerini kontrollü bir şekilde test edip uygulamanıza olanak tanıyor.
Son bir not: Bu sistemi devreye almadan önce sitenizin tam bir yedeğini alın ve tercihen staging ortamında test edin. Her site farklıdır ve bazı eklentiler otomatik güncelleme mekanizmasıyla beklenmedik şekillerde etkileşime girebilir.
