1 puan yazan GN⁺ 2024-05-22 | 2 yorum | WhatsApp'ta paylaş
  • Belge şablonlarıyla e-posta, SMS ve PDF üreten bir sistemde, yalnızca belirli bir müşteriye gönderilen e-postanın gövdesinde noktaların kaybolduğu bir sorun ortaya çıktı
  • Şablonun kaynağında, önizlemede ve PDF’de noktalar vardı; ancak müşteri bazlı placeholder değerleri gerçek verilerle değiştirildikten sonra yalnızca belirli satır uzunluklarında sorun yeniden üretilebildi
  • Özel bir SMTP istemcisi satır uzunluğunu sınırlandırırken noktayı yeni satırın ilk karakteri hâline getirdi ve SMTP’nin dot transparency kuralı nedeniyle sunucu bu noktayı kaldırdı
  • Düzeltme, noktayla başlayıp ardından başka karakterlerin geldiği satırların başına istemcinin bir nokta daha eklemesi şeklinde yapıldı
  • Aynı SMTP istemci kodunu paylaşan başka bir ekip yama uygulamayınca, bazı müşterilere aylık sigorta priminin $27.00 yerine $2700 gibi göründüğü e-postalar gönderildi ve hata hemen düzeltildi

Merkezi şablon sisteminde başlayan sorun

  • Yaklaşık 7 yıl önce bir müşteri, farklı Microsoft Word şablonlarına dağılmış belgeleri tek bir sistemde birleştirmek istiyordu
  • Eski yöntemde çalışanlar belgelerdeki placeholder alanlarını ad, soyad gibi bilgilerle elle değiştiriyordu; eski şartlar, önceki şirket logosu ve yanlış fontlar içeren şablonlar da dolaşımda kaldığı için yönetim zorlaşıyordu
  • Yeni sistem, belge şablonlarını merkezi olarak yönetiyor ve bunlardan PDF belgeleri, SMS mesajları ve e-posta gövdeleri üretiyordu
  • Yeni müşteri karşılama mesajları da iletim yöntemine göre farklı şablonlara sahip olabiliyordu
    • E-posta sürümünde HTML tabloları ve temel stiller kullanılabiliyordu
    • Postayla gönderilecek sürümde infografikler bulunabiliyordu
    • SMS sürümünde yalnızca kısa bir karşılama mesajı yer alabiliyordu

Yalnızca belirli müşterilerde kaybolan nokta

  • Sistem aylarca, hatta bir yıldan uzun süre çalıştıktan sonra bir yönetici, belirli bir müşteriye gönderilen e-postanın gövdesinde bir noktanın eksik olduğunu bildirdi
  • Aynı e-posta başka müşterilere gönderildiğinde nokta kaybolmadığı için bunun basit bir şablon hatası olduğu düşünülmedi
  • Şablonun kaynak kodunda ilgili nokta gerçekten vardı
  • Prodüksiyon şablonu yerel ortama kopyalanıp e-posta gövdesi önizleme olarak oluşturulduğunda da nokta görünüyordu; aynı şablonla üretilen çıktı sürümünde de doğru şekilde gösteriliyordu
  • Yerel ortamda e-postalar belirli bir porttaki localhost’a gönderiliyor, ardından SMTP4dev gibi sahte bir SMTP sunucusu ve Outlook ile kontrol ediliyordu
  • İlk başta yerelde gönderilen e-posta Outlook’ta incelendiğinde de nokta düzgün görünüyordu

Müşteri verisinin oluşturduğu yeniden üretim koşulu

  • Şablonlar, e-posta, PDF ve SMS üretirken müşterinin ad ve soyadı gibi placeholder değerlerini gerçek verilerle değiştiriyordu
  • Aynı şablon kullanılsa bile, her müşteriye gönderilen e-posta gövdesinin uzunluğu farklı olabiliyordu
  • Sorunlu müşteri için gerçekten kullanılan placeholder değerleri bulunup yerel ortamda aynı değerlerle e-posta tekrar gönderildiğinde, Outlook’ta noktanın kaybolduğu görüldü
  • Sorun, ilgili müşteri e-postasının belirli gövde uzunluğu ve içeriğine bağlıydı
  • İlk aşamada şablondaki nokta karakterinin encode edilip edilmediği ya da aslında başka bir karakter olup olmadığı kontrol edildi, ancak neden bu değildi
  • Şablon içindeki noktanın yeri bir karakter kaydırılınca, Outlook’ta nokta yeniden görünmeye başladı ve satır konumu yeniden üretim için ipucu oldu

SMTP satır uzunluğu ve dot transparency

  • Hata ayıklama sonucunda, e-postayı veritabanına kaydeden kodun placeholder’ları müşteri bilgileriyle değiştirmek dışında şablonu değiştirmediği görüldü
  • Ardından inceleme, düzenli olarak gönderim hedeflerini alıp e-postaları yollayan cron işi koduna daraltıldı
  • Cron işinin çağırdığı kodun bir bölümü önceki bir projeden alınmıştı ve içinde özel bir SMTP istemcisi implementasyonu bulunuyordu
  • Bu kodda, e-posta gövdesindeki her satırın belirli bir karakter sayısını aşmaması için satırı bölen bir fonksiyon vardı
  • Bu davranış SMTP spesifikasyonundaki satır uzunluğu sınırıyla ilgiliydi
    • Metin satırının toplam azami uzunluğu <CRLF> dahil 1000 octets idi
    • Şeffaflık için kopyalanan baştaki noktalar bu hesaba dahil edilmiyordu
    • SMTP Service Extensions ile bu değer artırılabiliyordu
  • Sorunlu e-posta gövdesinde, satır uzunluğu sınırı nedeniyle nokta bir sonraki satırın ilk karakterine taşındı
  • SMTP spesifikasyonunda mail data bitiş göstergesi, yalnızca bir noktadan oluşan bir satır olarak ele alınıyordu
  • SMTP’nin dot transparency kuralı, noktayla başlayan gövde satırlarını ayrıca işliyordu
    • SMTP istemcisi, posta metni satırını göndermeden önce ilk karakterin nokta olup olmadığını kontrol etmeli ve öyleyse satırın başına bir nokta daha eklemeliydi
    • SMTP sunucusu, satır yalnızca tek bir noktadan oluşuyorsa bunu mail verisinin sonu olarak kabul eder
    • İlk karakter nokta olup aynı satırda başka karakterler de varsa, sunucu ilk karakteri siler
  • Özel SMTP istemcisi, satır noktayla başladığında bir nokta daha ekleme işlemini yapmıyordu; alıcı SMTP sunucusu ilk noktayı silince, gövdedeki gerçek nokta kayboluyordu

Düzeltme ve sonradan ortaya çıkan etki

  • Düzeltme, satır noktayla başlayıp aynı satırda başka karakterler de olduğunda istemcinin bir nokta daha eklemesi şeklinde yapıldı
  • Böyle gönderildiğinde, alıcı SMTP sunucusu ilk noktayı silse bile gövdede asıl nokta kalıyordu
  • Sorunlu müşterinin aynı alıcı bilgileriyle özgün e-posta yerelde yeniden gönderildiğinde, nokta artık kaybolmuyordu
  • Düzeltme dağıtıldıktan sonra, aynı SMTP istemci kodu önceki projeden alındığı için diğer ekiplere de bu hata bildirildi
  • Birkaç ay sonra başka bir ekibin sistemi hâlâ yamalanmamış durumdaydı ve müşterilere yeni aylık sigorta primini bildiren önemli e-postalar gönderdi
  • Bazı e-postalarda aylık sigorta primindeki ondalık ayırıcı noktası tam olarak satırın ilk karakter konumuna denk gelip kayboldu
  • Sonuç olarak bazı müşteriler, yeni primin $27.00 yerine $2700 olduğu izlenimini veren e-postalar aldı
  • Bu hata, e-posta gövdesindeki her satırın uzunluğuna bağlıydı ve yalnızca ad ile soyad uzunluğu tam denk gelen bazı müşterilerde ortaya çıkıyordu
  • Neden zaten bilindiği için ilgili ekibin kodu hemen yamalandı

2 yorum

 
surfindia 2024-05-22

Başlıkta period kelimesini nokta değil, süre/dönem olarak yorumlamış gibi görünüyor haha

 
GN⁺ 2024-05-22
Hacker News yorumları
  • Bu kodun bir kısmı SMTP istemcisini doğrudan uyguluyordu ve temel neden de oradaymış gibi görünüyor
    Protokolü doğru uygulamak zordur; yazıda anlatılan hata türü de sık görülür
    Doğru uygulanmış bir SMTP istemci kitaplığı olsaydı, giriş metnindeki noktanın konumundan bağımsız olarak SMTP kurallarına uygun şekilde kodlardı; şablon katmanının SMTP’yi düşünmesine gerek kalmazdı

    • Asıl sorun protokolün kendisinden çok, o protokole yönelik kovboy usulü yaklaşımda
      Metni SMTP kurallarına uygun biçimde alıp kodlamak zor değil, ama en başta böyle bir işlemin gerektiğini bilmek gerekiyor
      SQL injection, XSS gibi sayısız hata ve güvenlik açığı da aynı hatadan, yani string birleştirmeden doğuyor
      SQL sorgularında değerleri sorgu şablonuna bağlama işi, tipsiz bir string alanında değil “SQL alanında” gerçekleşmeli; SELECT * FROM foo WHERE foo.bar = + $userData gibi serileştirilmiş SQL string’ini doğrudan oluşturmak yanlış bir yöntem
      HTML şablonları da benzer şekilde string gösterimi olarak değil belge ağacı düzeyinde ele alınırsa aptalca açıklar önlenebilir
      E-postada noktaların kaybolmasını önlemek için SMTP hattının ortasına yapısız metin enjekte etmemek, çalışılan soyutlama düzeyine saygı göstermek gerekir
      Bununla bağlantılı olarak langsec de bakmaya değer
    • Asıl sorun SMTP’nin in-band sinyaller içeren bir “düz metin” protokolü olması
      “Yalnızca tek bir noktadan oluşan satırı” literal bir satır değil, kontrol dizisi olarak tanımladığı için böyle şeyler oluyor
      SMTP gereksiz karmaşık tasarımın bir örneği; uygulama hataları da bu karmaşıklığı yansıtıyor
      Kimseye bunu baştan yazmasını önermiyorum ama SMTP, tek başına bile doğru uygulanması zor olacak seviyede olmamalı
    • Kulağa iyi bir fikir gibi geliyor ama bir SMTP kitaplığı ekliyorsunuz; o kitaplık 5 bağımlılık getiriyor, her biri de 3’er geçişli bağımlılık çekiyor; içlerinden biri gerçekte yalnızca %1’i kullanılan her işe yarayan araç kutusu projesi olabiliyor
      Geri kalanı ölü yük olduğu hâlde, hiç çağrılmayan fonksiyonlar yüzünden 20 bağımlılık daha geliyor; bir bakmışsınız kod tabanı birkaç MB şişmiş, kullandığınızı bile bilmediğiniz bir kitaplık için CVE uyarısı alıyorsunuz
    • https://en.wikipedia.org/wiki/Jamie_Zawinski#Zawinski's_Law
  • Teknik kısmı başkaları zaten benden daha iyi ele almış, ama cümle sonundaki tek bir nokta kadar önemsiz görünen bir şeyin ne kadar önemli olabileceğini hatırlatan bir anekdot var
    Çalıştığım Almanya’da işten ayrılırken yapılan işleri ve çalışan değerlendirmesini içeren bir referans mektubu olan “Zeugnis” istemek yaygındır; iş ararken de genelde istenen önemli bir belgedir
    Elbette bir çalışan “bu kişi tembeldir, işe almayın” gibi bir ifadeyi kabul etmeyeceği için, övgü gibi gizlenmiş “Zeugnissprache” denen bir tür şifre ortaya çıkmış
    Bu şifrelerden biri de son cümlede nokta yoksa “burada yazanların hepsini görmezden gelin, bu kişi berbattır” anlamına geldiği
    Önceki işimden sonra Zeugnis’imi bir avukata incelettim; tüm değerlendirmeler olumlu olmasına rağmen, muhtemelen dikkatsizlikten son cümlede nokta eksikti

    • Referans mektuplarında gizli şifre kullanıldığı bir şehir efsanesi
      İnsan kaynakları çalışanlarının potansiyel rakipleriyle ortak kullanacakları gizli kodlar üretmek için bir motivasyonu yok; bunu yeni İK çalışanlarına öğretip aynı zamanda gizli tutmak da mantıklı değil
      Bu tür efsaneler, İK’nın birinin baş belası olduğunu fazla açık yazamamasından doğuyor
      Olumlu yazacak bir şey yoksa dakiklik gibi sıradan bir artıyı övmek gibi; bu, gizli şifreden çok İK dilindeki “bless their heart”a yakın
      Dava açılırsa “Sayın hâkim, iyi bir şey söyledik! Her zaman zamanında gelirdi!” diyerek sıyrılacak alan bırakılmış oluyor
    • “Son cümlede nokta yoksa burada yazanların hepsini görmezden gelin, bu kişi berbattır” demek; ne ters gidebilir ki diye düşünüyor insan
    • Performans değerlendirme belgelerinde de noktanın eksik olup olmadığını kontrol edip etmediğini merak ediyorum
  • E-posta gönderen bir cron işinin neden kendi SMTP istemcisini uygulaması gerektiğini anlamıyorum
    Basitçe mailutils’in mail komutu gibi bir program kullanılabilir
    Teslim edilebilirlik açısından da soket üzerinde iskelet hâlinde bir SMTP etkileşimini baştan yazmak en baştan zorlama
    Günümüzde herhangi bir mail exchange ana makinesine doğrudan bağlanıp e-posta gönderemezsiniz; genellikle ISP’nin sağladığı belirli bir SMTP relay ana makinesine bağlanmanız gerekir
    Bunun için TLS bağlantısı, kimlik doğrulama vb. her şeyi uygulamak gerekir
    Biraz ironik olan, cron’un kendisinin zaten e-posta göndermeyi bilmesi
    Cron işinin çıktısı sahibine e-posta olarak gönderilir; bazı cron’lar crontab’daki MAILTO değişkeni gibi şeylerle alıcı adresini değiştirebilir

    • E-posta gönderirken ana makinenin MTA’sını kullanması gerektiği hâlde kendi SMTP istemcisini gömen şeyler gerçekten çok
      Nedeni şu: “o şeyi” kullanmak isteyen kullanıcı bir SMTP sunucu adresi girebilir, ama programın sendmail gibi gönderici MTA’nın doğru yapılandırıldığına güvenmesinin bir yolu yok
      Şirketlerde sistem e-postası gönderemeyen çok sayıda sunucu bulunuyor ve bu durum çoğu zaman program yazarının ya da kullanıcının kontrolü dışında oluyor
      E-posta yapılandırması DNS, şifreleme, politika gibi ön koşullar gerektiren uzmanlık alanı; bir cüzdan uygulamasına yalnızca e-posta göndertmek için fazla ağır
      “Sistem yöneticinize başvurun” büyük ya da küçük ölçekte çoğu zaman gerçekçi bir seçenek değil
      İyi bir neden değil ama gerçek neden bu
    • Sistemde e-posta gönderecek bir binary’nin zaten bulunduğuna güvenebileceğiniz bir dünya yok; ama yine de kendim yazmazdım
      Çoğu dilde, standart kitaplıkta olmasa bile kullanılabilir çok sayıda SMTP istemci kitaplığı var
    • Bu da Zawinski yasasının bir örneği değil mi diye düşünüyorum
  • Burada iki büyük kötü alışkanlık görünüyor
    İlki, birçok kişinin işaret ettiği gibi standartları baştan savma uygulamamak gerektiği
    Kendiniz uygulamanız gerekiyorsa gereken dikkat ve özeni göstermeli ya da hazır bir kütüphane kullanmalısınız
    İkincisi, bağımlılıkları vendor etmemeniz gerektiği
    Kullandığınız kütüphaneleri düzenli olarak, zamanında güncellemelisiniz; “yalnızca ihtiyaç olduğunda” değil
    Güncellemeleri geciktirmek ya da tamamen kaçınmak, upstream’de zaten düzeltilmiş hataların bile ancak sorun kendilerinin karşısına çıktığında güncellenmesi gerektiğini düşünenler için büyük problemlere dönüşmesine yol açabilir

    • Bağımlılıkları vendor etmemeye alternatif daha kötü görünüyor
      O zaman uygulamanın kararlılığı upstream değişikliklerine açık hâle gelir ve bu değişiklikler kodu bozabilir
      Düzeltmeleri hemen alamayabilirsiniz ama istenmeyen kararsızlık ve ek riskleri içeri almaktansa, ihtiyaç duyduğum bir düzeltme nedeniyle değişiklik yaptığımı bilmenin daha iyi olduğunu düşünüyorum
      “Bozuk değilse tamir etme” tarafındayım
  • Günümüzde birçok kişi terminalle doğrudan etkileşime girerek temel protokolleri öğrenmiyor gibi
    SMTP sanki başlangıçta bu tür elle etkileşim düşünülerek tasarlanmış; bir süre gerçekten böyle kullanmış biri olarak, mesajı bitiren “tek noktalı bir satır” zihnime kalıcı biçimde kazındı
    Bununla ilgili escape işlemleri de birçok programcıya yabancı bir kavram gibi görünüyor
    Yukarıdaki durumu görüp “Peki yalnızca nokta içeren bir satırı olan e-posta göndermek istersem ne olacak?” diye sormayan çok kişi var; ama başka büyük bir grup da bu soruyu son derece mantıklı ve anlaşılır bulacaktır

    • Son 30 yıl içinde yazılım geliştiricilerin anlamlı bir oranı böyle öğrendiyse buna şaşırırdım
  • Dot stuffing gerekiyor
    SMTP https://www.rfc-editor.org/rfc/rfc5321#section-4.5.2
    POP3’te de var https://www.rfc-editor.org/rfc/rfc1939#page-8

  • Bir ağ protokolü implementasyonunda hata ayıkladığım deneyimi hatırlattı. Daha spesifik olarak, eskiler için AppleTalk NBP idi
    Her şeyi kodlamıştım ama paketlerim reddediliyor, yani sessizce drop ediliyor; gerçek Apple implementasyonunun paketleri ise geçiyordu
    İyi paketlerle kötü paketleri bilgisayar ekranında yan yana açıp bayt bayt karşılaştırdım ama sorunu bulamadım; checksum dâhil baştan sona tamamen aynıydılar
    İşten çıkma vakti gelince, sonra bakmak için o aptal şeylerin çıktısını almaya karar verdim; çıktı alır almaz hatayı gördüm
    Benim sürümüm iki sayfaydı, düzgün implementasyon bir sayfaydı
    Veriyi göndermeden önce buffer’ı düzgün boşaltmamıştım. mbuf dediğin böyle bir şey
    Hâlâ aklıma geldikçe gülüyorum

  • “We are happy to welcome you to our family.” satırı satır uzunluğu sınırının yakınına bile gelmiyor
    Başka bir şey olmuş gibi; örneğin tamamı aslında bir HTML MIME eki olabilir
    Şöyle, ... lots of text ... ardından We are happy to welcome you to our family. gelmiş olabilir
    Ama HTML’i gelişigüzel satırlara bölerseniz etiketleri kırarsınız

    • Bu yalnızca bir örnekti; tam karakter sayısına sahip gerçek bir örnek veremediğim için kusura bakmayın
    • Bozuk HTML e-posta alanların çoğu e-posta biçiminin garip olduğunu fark eder, okunması kolay bir e-postayı bile doğru yazamayan bir şirket olarak görüp şirketin yetkinlik değerlendirmesini düşürür ve sonraki e-postaya geçer
      Şirketin o küçük sorunun hiç farkına varmadan geçip gitmesi muhtemeldir
    • quoted-printable kodlamanın soft line break’leri kullanılırsa HTML etiketlerini bozmadan da satırlar gelişigüzel bölünebilir
      quoted-printable, CRLF dâhil en fazla 78 karakter sınırına sahip olmalı; ancak e-posta istemcileri genelde toleranslıdır
  • Dot stuffing’i ilk kez duyan biri, e-posta dünyasında başka ne tür korkular olduğunu inanmakta zorlanacaktır
    Header folding, local part’ın tırnaklanması, IPv6 literal’ları gibi şeyler var

    • IPv6 literal’larında ne sorun olduğunu merak ediyorum
  • Önceki bir projeden ödünç alınmış SMTP istemci kodu olduğunu okur okumaz, sonucun diğer ekibin bu hatayı hâlâ patch etmediği şeklinde çıkacağını sandım